Code Monkey home page Code Monkey logo

cljs-bean's People

Contributors

mfikes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cljs-bean's Issues

Separate out copied code

Some code is from ClojureScript and test.check and can be separated out into a separate directory.

A license section in the readme can indicate this.

Simplify unwrapping code

There are several places where a value is unwrapped if it is a bean or ArrayVector. These could be factored out into simpler logic.

->clj with React events

I've tried using ->clj with mouse events in a Reagent app with both (fn [e] (->clj e) ...) where the fn is the handler for the event, but it doesn't convert (it remains JS object). Using ->clj on the event's .-nativeEvent doesn't work either - what should I do ?

Inline -lookup calls

Especially in self-hosted, inlining -lookup calls speeds things up anywhere from 1.25 to 4.0

prop->key and key->prop for values

In the app I'm working on we use custom converters to be able to preserve namespaced keywords through a cljs->js->cljs roundtrip. In cljs-bean this fits perfectly, we were able to reuse our code by plugging it in as prop->key and key->prop converters.

That's for the keys, which is obviously the most common and sensible use case. But we also occasionally need to do the same for property values. So for us it would be pretty great to be able to extend the cond statement in the ->val function.

Count incorrect after dissoc non-key

cljs.user=> (require '[cljs-bean.core :refer [bean]])
nil
cljs.user=> (def x (bean #js {:a 1}))
#'cljs.user/x
cljs.user=> (count x)
1
cljs.user=> (count (dissoc x :b))
0

Working With Arrays

Hi, thanks in advance for your help and this library!

I've got a question about the following code. Is this expected? If I were to change ->clj to (js->clj % :keywordize-keys true) the code would pass. My understanding was that ->clj should behave similarly to js->clj.

Cheers!

{:deps {cljs-bean/cljs-bean {:mvn/version "1.7.0"}}
(deftest cljs-bean-test
  (let [clj-obj (->clj #js {:coll #js [#js {:id "foo"}]})]
    (is (= (conj (:coll clj-obj) {:id "bar"})
           [{:id "foo"} {:id "bar"}]))))

FAIL in (cljs-bean-test)
expected: (= (conj (:coll clj-obj) {:id "bar"}) [{:id "foo"} {:id "bar"}])
  actual: (not (= [#js {:id "foo"} {:id "bar"}] [{:id "foo"} {:id "bar"}]))

Can assoc map or vector while remaining a bean

This shows how two maps that are {:a 1, :b {:x 2}} can turn into two different JavaScript representations. The problem is that assoc on a bean can assoc a non-primitive value without it snapshotting to a regular persistent data structure.

cljs.user=> (require '[cljs-bean.core :refer [->clj ->js]])
nil
cljs.user=> (->js {:a 1 :b {:x 2}})
#js {:a 1, :b #js {:x 2}}
cljs.user=> (assoc (->clj #js {:a 1}) :b {:x 2})
{:a 1, :b {:x 2}}
cljs.user=> (->js *1)
#js {:a 1, :b {:x 2}}

Avoid contains? in -contains-key?

The Bean type implements IAssociative -contains-key? by simply calling contains?

With CLJS-3283, contains? is being revised to first check if a type implements IAssociative and then directly calls -contains-key?

This causes infinite recursion with Bean implementation.

bean :recursive true and ->clj behaves differently

When I try to interact with the objects returned by the Google Cloud getProjects function I get different results when using ->clj and bean :recursive true

(p/let [[ps] (.getProjects resource #js {:filter "name:uc*"})]
    (->> ps
         (map #(bean % :recursive true)
         first
         cljs.pprint/pprint)))
;; =>
{:metadata
 {:projectNumber "XXXXXXXX",
  :projectId "XXXXX",
  :lifecycleState "ACTIVE",
  :name "XXXXX",
  :createTime "2019-11-28T09:56:03.185Z",
  :parent {:type "folder", :id "1004516592721"}},
 :baseUrl "/projects",
 :parent #object[Resource [object Object]],
 :id "XXXXX",
 :createMethod #object[bound wrapper],
 :methods
 {:create true,
  :delete true,
  :exists true,
  :get true,
  :getMetadata true,
  :setMetadata {:reqOpts {:method "PUT"}}},
 :interceptors [],
 :Promise #object[Promise],
 :restore #object[wrapper],
 :create #object[wrapper],
 :delete #object[wrapper],
 :exists #object[wrapper],
 :get #object[wrapper],
 :getMetadata #object[wrapper],
 :setMetadata #object[wrapper],
 :request_ #object[Function],
 :request #object[wrapper], 
 :requestStream #object[Function]}

vs.

(p/let [[ps] (.getProjects resource #js {:filter "name:uc*"})]
    (->> ps
         (map ->clj)
         first
         cljs.pprint/pprint)))
;; =>
#object[Project [object Object]]

Is this because the objects returned from the Google Cloud library is not "pure" javascript Objects?

I would love to add something to the README about this gotcha, but not sure how to formulate it as I don't really understand the underlying reason for this happening.

How to use bean and ->clj on objects like js/Error?

For now, for the following code:

(def e (js/Error. "test string"))
;; 1
(bean/e) 
;; 2
(->clj/e)
  1. gives
{:clojure$core$protocols$Datafiable$ #js {}, :clojure$core$protocols$Datafiable$datafy$arity$1 #object[Function]
  1. gives
#object[Error Error: test string]

Would be better if it can give things like

{"message":foo,  "line":foo,  "column":foo,  "stack":foo}

Project Name

Be careful with the project name, because Pinto in Brazil is the same of Dick. When someone search for Pinto project on the google, some unrequested pictures can be appear.

Cache js-keys

Internally cache the result of js-keys if they are produced. This can help performance because js-keys is an O(n) operation. Also, caching helps ensure that seqs, etc. are produced in a consistent order.

Recursive beans

Allow a bean to be in a mode where it is recursive. This means it behaves akin to js->clj where object values are represented as beans, and array values are converted to vectors, and each of these container's values are recursively treated in the same way.

Illustrative example:

cljs.user=> obj
#js {:a 1,
     :m #js {:b 2, :m #js {:c 3}},
     :v #js [1
             2
             #js {:x 3,
                  :m #js {:a 1,
                          :v #js [1 2]}}]}
cljs.user=> (bean obj)
{:a 1,
 :m #js {:b 2, :m #js {:c 3}},
 :v #js [1
         2
         #js {:x 3,
              :m #js {:a 1, :v #js [1 2]}}]}
cljs.user=> (js->clj obj :keywordize-keys true)
{:a 1,
 :m {:b 2, :m {:c 3}},
 :v [1 2 {:x 3, :m {:a 1, :v [1 2]}}]}
cljs.user=> (bean obj :recursive true)
{:a 1,
 :m {:b 2, :m {:c 3}},
 :v [1 2 {:x 3, :m {:a 1, :v [1 2]}}]}

Writing ->clj to transit doesn't work

(t/write (t/writer :json) (bean/->clj #js {:hello "world"}))

throws

writer.js:464 Uncaught Error: Cannot write 
    at Object.writer.maybeQuoted (writer.js:464)
    at Object.writer.marshalTop (writer.js:471)
    at Transit$Writer.writer.Writer.write (writer.js:501)
    at cognitect$transit$write (transit.cljs:278)
    at transit.cljs:8

Not sure what is happening there, but happy to dig in and put up a PR.

Unwrap the bean as a JS obj

For my use case, I want to provide a user of my library the ability to immutably update a JS object using the core library and then I unwrap the object so that I can pass it to React.

Something akin to this:

(defn Component [props]
  (let [cljs-props (bean props)
        cljs-props' (assoc cljs-props :foo "bar")]
    (react/createElement (unwrap cljs-props') "baz")))

AFAICT updates to the bean return a regular map so, for my use case, it just might not fit. What do you think, @mfikes ?

Document that values are immutable but not persistent

CLJS Bean values are immutable. This lets identical? work on them along with other guarantees you gain via immutability. (Reinforce that passed JavaScript maps and arrays should be effectively immutable to ensure this.)

But the object are not persistent. (Operations like assoc involve CoW.)

It is worth documenting (perhaps in the Object Extraction section?) some of these aspects.

Add fast paths for primitive values

If you compare perf for assoc on beans vs persistent maps, you can see that persistent maps have around a speedup of 6:

cljs.user=> (require '[cljs-bean.core :refer [->clj]])
nil
cljs.user=> (simple-benchmark [m (->clj #js {:a 1, :b 2})] (assoc m :c 3) 1e6)
[m (->clj #object[cljs.tagged-literals.JSValue])], (assoc m :c 3), 1000000 runs, 678 msecs
nil
cljs.user=> (simple-benchmark [m {:a 1, :b 2}] (assoc m :c 3) 1e6)
[m {:a 1, :b 2}], (assoc m :c 3), 1000000 runs, 112 msecs

If we add fast paths for primitives in the code that deals with snapshot checks and unwrapping, we can get bring the speedup down to a factor of 2.

Type hint return type of object

If we type hint, then, if externs inference is enabled in the compiler, then something like this would work under advanced:

(let [b (bean #js {:myInc (fn [x] (inc x))})]
  (.myInc (object b) 1))

Question marks in key cause issues with Transit

This works fine:

(t/write (t/writer :json) {"some-test?" true}))

This fails:

(t/write (t/writer :json) (cljs-bean/->clj #js {"some-test?" true}))
Error: Cannot write
    at Object.maybeQuoted (compiled/cljs-runtime/com/cognitect/transit/impl/writer.js:469:23)
    at Object.marshalTop (compiled/cljs-runtime/com/cognitect/transit/impl/writer.js:476:57)
    at Transit$Writer.w [as write] (compiled/cljs-runtime/com/cognitect/transit/impl/writer.js:506:26)
    at Object.cognitect$transit$write [as write] (compiled/cljs-runtime/cognitect/transit.cljs:285:11)
    at f (...)
    at compiled/cljs-runtime/promesa/impl.cljc:86:30
    at processTicksAndRejections (internal/process/task_queues.js:93:5)โŽ

Interestingly wrapping the bean in (take) also prevents the error:

(t/write (t/writer :json) (take 1 (cljs-bean/->clj #js {"some-test?" true})))

Is this expected?

Data prints as expected but cannot be access as expected?

I'm processing a data structure that is an object with a list of results containing maps of properties describing a row in a data table.

The names of the properties may contain spaces so using cljs-bean's bean function with a custom key fn to process it, lowercase the keys, and replace any spaces with dashes (-).

Test Data:
[{:id 1,
  :properties
  {:uid
   {:type "formula", :formula {:type "string", :string "abcdefg"}}}}
 {:id 2,
  :properties
  {:uid
   {:type "formula", :formula {:type "string", :string "hijklmn"}}}}
 {:id 3,
  :properties
  {:uid
   {:type "formula", :formula {:type "string", :string "opqrstu"}}}}]

Test:
(map #(get-in % [:properties :uid :formula :string]) beaned)

Expected:
 (abcdefg hijklmn opqrstu) 

Actual:
 (nil nil nil)

To make it easier to reproduce, here's a replit with this exact problem recreated with the above test data.

https://replit.com/@eccentric-j/nbb-cljs-bean-oddity-repro#src/cli/core.cljs

Just run it to show the data, the expected, and the actual data which should match the above. It will then enter a repl from which the data may be inspected, or you may fork it to directly edit the files.

I don't really understand why it prints the way I would expect, but processing it shows a different result.

If you run:

(map #(get-in % [:properties :UID :formula :string]) beaned)

That does work, returning the expected list value but it seems to be ignoring the call to bean with a specific prop->key fn to lower-case the keys. It does not match what gets printed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.