binaryage / cljs-oops Goto Github PK
View Code? Open in Web Editor NEWClojureScript macros for convenient native Javascript object access.
License: Other
ClojureScript macros for convenient native Javascript object access.
License: Other
Hello,
I'm trying to use oops to interop with Automerge.
This snippet fails to make an update:
(oset+ doc "todos" (str idx) "done" true)
The browser complaints that: data property descriptor has writable=false
But if I try something a bit different:
(gobj/set (oget+ doc "todos" (str idx)) "done" done)
This works just fine, the google impl is a single obj[key] = value
. I wonder if there is a way to make oops
work directly without having to fall back into Closure helpers.
Compiling an app that uses cljs-oops
with the latest ClojureScript (1.9.946
) with :checked-arrays :warn
compiler option, the following warnings are printed:
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [nil string] instead (consider goog.object/get for object access) at line 10 resources/public/js/build/oops/helpers.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [nil string] instead (consider goog.object/get for object access) at line 20 resources/public/js/build/oops/helpers.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [nil string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [js string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [nil string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 28 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [nil string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [js string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [nil string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
WARNING: cljs.core/aget, arguments must be an array followed by numeric indices, got [any string] instead (consider goog.object/get for object access) at line 31 resources/public/js/build/oops/core.cljs
Would be nice not to see those warnings :)
In ClojureScript 1.9.655, :const
variables are inlined. See https://blog.fikesfarm.com/posts/2017-06-28-clojurescript-const-var-inlining.html and https://dev.clojure.org/jira/browse/CLJS-2093.
Since the variable value is inlined, it should be usable by cljs-oops:
(defonce ^:const ks [:oops :i :did :it :again])
(defonce ^:const strs (mapv #(str \! (name %)) ks))
;["!oops" "!i" "!did" "!it" "!again"]
(oset! #js {} strs true)
;Error: Oops, Unexpected dynamic selector usage (consider using oset!+)
Using [binaryage/oops "0.6.4"]
running in browser tests via [thheller/shadow-cljs "2.8.35"]
(https://shadow-cljs.github.io/docs/UsersGuide.html#target-browser-test)
What would be the cljs-oops equivalent of:
a.e.f(x).g(y,z);
Is there a better way than:
(ocall (ocall a "e.f" x) "g" y z)
It would be nice to be able to do something similar to Clojure's ..
macro, but that doesn't seem to be compatible with cljs-oops's flexible way of handling paths.
Just brainstorming here, maybe it would be possible to support the %
character as a placeholder for args, like so:
(ocall a "e.f(%).g" x y z)
Forgive me if I'm wrong, but using goog libraries in CLJS code is still a JS interop, and technically falls under cljs-oops intentions.
At this point, from the README it's not clear that one must not use cljs-oops with goog libraries as they're included in Google Closure compiler set and are minimized along with your CLJS code.
I'm using SheetJS in some project and I need to retrieve data from obj["!ref"]
. Doing (oget obj "!ref")
obviously warns me about punching and still generates code about it. Right now I've reverted to using aget
, but still it would be nice to handle that case. :)
P.S. Hi! :)
I'm dealing with an api that faux namespace their keys with a dot as the namespace separator; example: {"home-page.main-image": {...}}
. cljs-oops thinks dots are nested objects. Is there a workaround for this, or should I just use goog.object with this api?
I was reading the docs and didn't get any good examples of using ocall!
and oapply!
How are they different from the normal ocall
and oapply
?
=> (oget #js {:constructor nil, :a 1} :a)
TypeError: Cannot read properties of null (reading 'prototype')
at Object.oops$helpers$is_prototype_QMARK_ [as is_prototype_QMARK_] (helpers.cljs:7)
at Object.oops$helpers$cljs_type_QMARK_ [as cljs_type_QMARK_] (helpers.cljs:19)
at Object.oops$core$validate_object_access_dynamically [as validate_object_access_dynamically] (core.cljs:39)
at eval (views.cljs:308)
The culprit seems to be this function:
(defn is-prototype? [o]
(identical? (.-prototype (.-constructor o)) o))
Perhaps it should check if (.-constructor o)
is not null
?
Hey Binaryage, just want to say thanks for Oops, because man this just saved my butt working on my Cljs-based lockdown project.
I was having a hard time with name-mangling even with :optimizations :simple
interfacing with Algolia, and ocall
worked straight away. I feel like this should be part of the standard Cljs interop.
Cheers
P.
Is it expected that ocall
should generate a warning when *warn-on-infer*
is enabled?
(ns scratch.core
(:require [oops.core :refer [ocall]]))
(set! *warn-on-infer* true)
(ocall js/document :getElementById "foo")
Produces compiler warning:
6 (ocall js/document :getElementById "foo")
^--- Cannot infer target type in expression (. fn-126930 call (oops.helpers/unchecked-aget call-info-126931 0) "foo")
Perhaps the oops codegen could typehint the fn-* symbol with ^js/Function
?
Resource: oops/core.cljs:42:3
--------------------------------------------------------------------------------
39 | (runtime/validate-object-access-dynamically obj mode key push? check-key-read? check-key-write?))
40 |
41 | (defn ^boolean validate-fn-call-dynamically [fn mode]
42 | (runtime/validate-fn-call-dynamically fn mode))
---------^----------------------------------------------------------------------
Use of undeclared Var cljs.core/js-fn?
--------------------------------------------------------------------------------
43 |
44 | (defn ^:dynamic punch-key-dynamically! [obj key]
45 | (runtime/punch-key-dynamically obj key))
46 |
--------------------------------------------------------------------------------
Only solution is to go back to version 0.7.0.
Hello Darwin!
I was trying to make cljs-oops
work in lumo
(self-host cljs) and I have run across the following:
#error {:message No such namespace: env-config.core, could not locate env_config/core.cljs, env_config/core.cljc, or Closure namespace "env-config.core" in file oops/config.clj, :data {:tag :cljs/analysis-error}}
I have two questions:
Thanks, as usual awesome work (I have used this successfully with cljs on jvm) !
Right now there doesn't seem to be any docs on how to use ocall...and I can't seem to figure it out by trial and error myself.
I'm trying to translate:
(.call (oget % "getDoc") %)
to use ocall
, with no success. I've tried things such as:
(ocall % "getDoc")
, (ocall % "getDoc" %)
, (ocall % ("getDoc"))
, (ocall "getDoc" %)
, etc.
I would imagine the first one is what I should be using. But since there's no documentation I can't tell if its breaking because I'm using it wrong, or because of some other error.
It would be nice to setup chromedriver to exercise tests against real chrome as well. Some javascript hackery we employ works only under chrome.
It can be done. I have done it once for Dirac. But it is quite non-trivial work to get right.
So this is a bit of a weird onc since there's hardly anyone (possibly just me?) is using prepl for development right now, if you don't care just yet then that's totally fine! Thought I'd let you know anyway ๐
The error is Can't change/establish root binding of: *cljs-warnings* with set
, which works fine in a normal REPL. This could be an issue with prepl itself that isn't setting up binding in the right way.
Even if I can't fix it, finding a work around would be great. If I find something I'll put it here, any help would be much appreciated though. This error occurs with any oops operation due to the fact that the setting of *cljs-warnings*
is attempted by every macro low down.
I'm not sure what the difference is between a prepl and regular REPL though. You can try it yourself by starting a prepl:
clj -J-Dclojure.server.node="{:port 5556 :accept cljs.server.node/prepl}"
Connecting with something like nc
:
nc 127.0.0.1 5556
And executing any oops operation such as:
(oops.core/ocall #js {"a" +} "a" 1 1)
When entering the code
(def d (js/Date.))
(oops/ocall d "toISOString")
I am getting the error
Oops, Unexpected object value (date-like) #object[devtools.toolbox.t_devtools$toolbox78167]
What gives?
Unexpectedly dealing with frozen objects could cause a lot of confusion. We should check for frozen objects in dev mode and give user a friendly, descriptive warning.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
Hey guys,
first of all thanks a lot for this awesome project. I have only heard good things about it so I started using it recently.
However I was faced with an error that I cannot get my head around. I am not sure if this is just not the intended use or if this is a bug in the library, so here it is.
If I have something like (oops/apply+ object f args)
oops will complain that args is not a sequence. My guess is that it is trying to check this at compile time, however I only know the args value at runtime, just as with the rest of the arguments ๐
Is this a bug or just not an intended use case?
This case of (.
doesn't work when converted to use ocall
:
(.contains "foobar" "foo")
This returns true
as expected. When I do this:
(ocall "foobar" "contains" "foo")
I get "Oops, Unexpected object value (string)"
I've had to stop using cljs-oops for a subset of my uses, specifically https://capacitorjs.com plugins[1], because their top-level plugin modules return a Proxy that behaves somewhat like this (not exactly, but this achieves a similar effect):
const p = new Proxy({}, {get: () => () => true})
That is to say, typeof p.anythingAtAll
is 'function'.
Unfortunately, goog.isDateLike
only looks for goog.isObject
and typeof val.getFullYear == 'function'
.
I fully appreciate that this style of duck-typed checking is common in JS, including for promises (as thenables) and similar -- and also that this is more of an upstream quirk than anything else.
That said, given that upstream libraries can't always be changed, I wanted to propose configuration to pick which of the safety checks are run, one at a time (maybe a set like #{:date-like :string-like ...}
?)
I find that I rarely trigger the date-like
check in ordinary use and disabling it for my own codebase would be helpful for this situation -- on the other hand, I find the other runtime checks valuable and do run into them, so I would love to leave them enabled.
I almost wrote the PR alongside the issue, but I wanted to get your thoughts before doing so, in case you have a preference for how such a thing would work.
Thanks so much for the library -- I like it enough to have wrapped it at https://github.com/tekacs/access in a different syntax, which is primarily how I use it. :)
[1]: such as @capacitor/filesystem
, which returns the output of registerPlugin
in @capacitor/core
Currently oops can generate key get/set code either via aget
/aset
or goog.object/get
/goog.object/set
[1].
My compilation output tests under advanced mode showed that DCE is more aggressive with goog.object implementation. Compare [2] and [3].
And I don't understand why. Any insights?
[1]
cljs-oops/src/lib/oops/core.clj
Lines 69 to 77 in 6141637
The following constructor call works great with compilation level "none":
(defonce game (js/Phaser.Game. 1152 648 js/Phaser.AUTO "app" #js{"create" create, "preload" preload}))
I'm trying to convert this, using cljs-oops, so it will work under advanced compilation, for example:
(defonce game (new (oget js/Phaser "Game") 1152 648 (oget js/Phaser "AUTO") "app"
#js{"create" create,
"preload" preload}))
But this does not properly compile into a constructor call. The output that is produced is below. Notice how the inputs are getting moved outside of the constructor call. How do I do this properly?
if ("undefined" === typeof EK) {
var EK = (new function() {
return Phaser.Game;
})(1152, 648, Phaser.AUTO, "app", {create:OK, preload:FK});
}
Hello,
I'm getting this issue when compiling my clojurescript code with shadow-cljs 2.20.17:
------ WARNING #1 - :undeclared-var --------------------------------------------
Resource: oops/core.cljs:52:3
Use of undeclared Var cljs.core/js-fn?
--------------------------------------------------------------------------------
I have tried to import the js-fn? macro using:
(ns my.project
(:require-macros [cljs.core]))
but I still get the error.
When used in NodeJS, the library throws window is not defined
unless advanced compilation is used.
Hello Darwin,
I have a project on clojure 1.8.0
and I receive the following.
clojure.lang.ExceptionInfo: failed compiling file:/home/arichiardi/git/rest-resources-viz/src/web/rest_resource_viz/core.cljs {:file #object[java.io.File 0xadc8c4b "/home/arichiardi/git/rest-resources-viz/src/web/rest_resource_viz/core.cljs"]}
...
Caused by: java.io.FileNotFoundException: Could not locate clojure/spec__init.class or clojure/spec.clj on classpath., compiling:(oops/config.clj:1:1)
...
Caused by: java.io.FileNotFoundException: Could not locate clojure/spec__init.class or clojure/spec.clj on classpath.
...
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:512)
-> at oops.config$eval30123$loading__5569__auto____30124.invoke(config.clj:1)**
-> at oops.config$eval30123.invokeStatic(config.clj:1)**
-> at oops.config$eval30123.invoke(config.clj:1)**
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6916)
at clojure.lang.Compiler.load(Compiler.java:7379)
... 117 more
Is there a way to avoid requiring clojure.spec
if not present at the moment?
Thanks for this lib, it looks fun!
Heyho, thanks for this project, it makes javascript interop a lot better than using native functions!
I realized, that you are already replacing old GCL functions, and shadow-cljs released a new version today, which breaks using oops + latest shadow-cljs together. And I hope that you can release a new version soon :-)
Thanks for your work in this project!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.