Fork me on GitHub

@souenzzo What about breaking it apart. (namespace :newman/john) ;=> "newman" and (name :newman/john) ;=> "john") and then just string them together?


@john right. I can do, but it's really needed to create a new function to generate keys and overwrite the default one from clj->js...


you should use some kind of serializer for your case, something like transit, where you can plug your own writer for keywords.


Although I can choose the names of keys, JSON is mandatory in project.


I think that would be an easier way


@souenzzo Haven't tested this, but you could try something like

(defn my-clj->js
   (when-not (nil? x)
     (if (satisfies? IEncodeJS x)
       (-clj->js x)
         (keyword? x) (str (namespace x) "/" (name x))
         (symbol? x) (str x)
         (map? x) (let [m (js-obj)]
                    (doseq [[k v] x]
                      (aset m (key->js k) (clj->js v)))
         (coll? x) (let [arr (array)]
                     (doseq [x (map clj->js x)]
                       (.push arr x))
         :else x))))
;modified cljs, Copyright © Rich Hickey ;)


or check to see if the keyword was passed in namespaced and do it then? Not sure if there's a namespaced? check


@souenzzo sorry, got another repl up to test it. You'll need:

(declare my-clj->js)

(defn my-key->js [k]
  (if (satisfies? IEncodeJS k)
    (-clj->js k)
    (if (or (string? k)
            (number? k)
            (keyword? k)
            (symbol? k))
      (my-clj->js k)
      (pr-str k))))

(defn my-clj->js
   (when-not (nil? x)
     (if (satisfies? IEncodeJS x)
       (-clj->js x)
         (keyword? x) (str (namespace x) "/" (name x))
         (symbol? x) (str x)
         (map? x) (let [m (js-obj)]
                    (doseq [[k v] x]
                      (aset m (my-key->js k) (my-clj->js v)))
         (coll? x) (let [arr (array)]
                     (doseq [x (map my-clj->js x)]
                       (.push arr x))
         :else x))))
All slightly modified cljs


Though, if you're round-tripping data with other tools, you may be breaking contracts by changing this behavior.


if I'm writing a mobile ios app in html/cljs, is the best debugging approach to have the ipad run safari, then run safari on the desktop and debug that way? or is there something better [generally, I prefer to debug inside chrome]

Oliver George10:05:32

I'm seeing a performance problem with clojurescripts defmulti. Specifically, dispatch is slow when there is no match for the dispatch value.

Oliver George10:05:49

It seems to be proportional to the number of defmethods hooked up.


@olivergeorge it usually is just a map lookup so it shouldn't make a difference?

Oliver George10:05:57

(defmulti exp1 :type)
(defmethod exp1 :default [x] x)
(dotimes [n 10] (defmethod exp1 n [x] x))
(time (dotimes [n 100] (exp1 {})))
"Elapsed time: 14.655000 msecs"
(dotimes [n 100] (defmethod exp1 n [x] x))
(time (dotimes [n 100] (exp1 {})))
"Elapsed time: 81.760000 msecs"
(time (dotimes [n 100] (exp1 {:type :default})))
"Elapsed time: 0.460000 msecs"

Oliver George10:05:01

@thheller looks like it has to scan through all possibilities in the method-table and isa? takes a lot of time

Oliver George10:05:32

Three examples there. The first two show relation to number of dispatch methods. The final one shows that it's specifically a "miss" that shows a performance penalty.

Oliver George10:05:00

Same between optimisations :none and :advanced

Oliver George10:05:03

Not repeatable on clj


ah right I forgot about the inheritance part

Oliver George10:05:45

I wondered if the caching was intentionally not done on "misses" for fear of a memory leak. Not trivial to scan the code and understand.

Oliver George10:05:26

In my app it's a x8 penalty happening a heap in a tight loop which sucks.


I could see a potential optimization that kicks in if the hierarchy is manually set to (atom nil)


I mean, "to be implemented" 🙂

Oliver George10:05:18

I'll scan JIRA for related tickets since CLJ can handle it better it might be worth fixing.


If you want the behaviour you can manually do -add-method in :default

Oliver George10:05:31

@rauh tell me if it changes those numbers... a quick win would be awesome.

Oliver George10:05:14

@thheller do you think this is worth adding a JIRA ticket for?


yes, maybe 🙂


@olivergeorge Not battle tested:

(set! cljs.core/find-and-cache-best-method
      (fn find-and-cache-best-method
        [name dispatch-val hierarchy method-table prefer-table method-cache cached-hierarchy]
        (let [hier @hierarchy
              use-isa? (not (empty? (:ancestors hier)))
              best-entry (if use-isa?
                           (reduce (fn [be [k _ :as e]]
                                     (if (isa? hier dispatch-val k)
                                       (let [be2 (if (or (nil? be) (dominates k (first be) prefer-table hier))
                                         (when-not (dominates (first be2) k prefer-table hier)
                                           (throw (js/Error.
                                                    (str "Multiple methods in multimethod '" name
                                                         "' match dispatch value: " dispatch-val " -> " k
                                                         " and " (first be2) ", and neither is preferred"))))
                                   nil @method-table)
                           (get @method-table dispatch-val))]
          (when best-entry
            (if (= @cached-hierarchy @hierarchy)
                (swap! method-cache assoc dispatch-val (second best-entry))
                (second best-entry))
                (reset-cache method-cache method-table cached-hierarchy hierarchy)
                (find-and-cache-best-method name dispatch-val hierarchy method-table prefer-table
                                            method-cache cached-hierarchy)))))))

Oliver George11:05:18

@rauh Thanks, I'll try it out.


something of a noob question here: I'm using reagent and, using their Collapse and Collapse.Panel react components … I'm not sure how to persist the panel's collapsed state into reagent. Right now each figwheel update causes all the panels I've expanded to collapsed again


@hagmonk can you link to the docs/source of the component?


it appears they wrap another component called rc-collapse


(I literally knew nothing about react until a few days ago, apologies if this is super obvious)


@hagmonk it looks like it doesn't support persisting the state


there would have to be a prop like collapsed that gets passed in


I might be looking in the wrong place though


I'm just learning cljs, but know react pretty well. @pesterhazy is right there, there's no state at all in that component


so they have activeKey on the Collapse and key on the Panel … but weird things start happening when I try to set those manually


hmm so would the solution be to wrap it myself?


honestly it doesn't speak to the quality of the whole framework if such a basic feature is not supported


I'll take recommendations for other UI frameworks 🙂 My goal is to avoid doing much design work on this prototype, so something like ant was appealing because of how much ground it covered


yeah, not sure what to recommend


I'd look into reactstrap (bootstrap) perhaps


Completely different question: is there a way to specify the initial namespace using figwheel?


another popular one is material-ui (which is material design). and palantir's blueprintjs, but i think that one has fewer components.


I like to have a repl ns around for simple utility functions - and that ns should only be required in figwheel sessions, not prod builds. Ideally figwheel should start in that ns too


@hagmonk I'm also building something with reagent and ant design. Is it similar to the way their Menu/Menu.Item works, where you give each one a key and the parent gets a list of what keys are open?


Here's an example that works for me through figwheel updates:


(defonce which (r/atom "one"))

(defn panel []
  [Collapse {:accordion true
             :activeKey @which
             :onChange #(reset! which %1)}
    [Collapse-Panel {:key "one" :header "First"}
      "First panel"]
    [Collapse-Panel {:key "two" :header "Second"}
      "Second panel"]
    [Collapse-Panel {:key "three" :header "Third"}
      "Third awesome panel"]])


I've had a great time with ant so far!


You'll need to persist the state of which key is active somewhere - either a ratom, or wherever you're storing your app's data.


I'm having some trouble with an async cljs test


I'm subscribing to the response of calling auth/login via a pub/sub system I have set up


And I am calling (done) when I am done my assertions in the callback for my subscription


But it intermittently fails and passes


The exact same test, the next run:


Am I misunderstanding the use of the async and done combo for cljs testing?


My understand is that I want to call done when all of my assertions have been run, to tell the test runner to move on to the next test


@ksmithbaylor oh that's interesting, thanks for that. I think one difference might be that activeKey is an array for this component


@ksmithbaylor but I'll hack on it some more now and see how it goes


I have a few string parameters that I want to pass from my server (Django templates) to the clojurescript app. In Elm there is a way to pass parameters to initialize an app. I don’t see this pattern in any of the tutorials or docs for cljs (that I’ve found). Is there a standard way to bootstrap a cljs app with parameters after the document has loaded?


@hagmonk I think you can pass either an array or a single string


you could easily do [@which] instead though


@ksmithbaylor I got it working with the code you provided. Very cool! It didn't occur to me that the onChange handler needed to be implemented … I guess I assumed there was some kind of internal state tracking in the component itself for that


feels like I basically had to "take over" state management for that component


@hagmonk There may be some internal state, but you have to use the onChange handler to make it "external" so your app can use it


got it … can I expect most components to follow this same general rule?


It's pretty common if a component has internal state to expose it via a callback like onChange, etc


@hagmonk on a related note, I made a macro to help me more easily use antd components from Reagent: You may find it useful. I'm fairly new to Clojure myself, so there may be a much better way to do this.


@ksmithbaylor I'll look in more detail later, but I think a better approach might be to create a namespace, then use tools.namespace simply enumerate all the components in js/antd, def'ing them up in your namespace wrapped with adapt-react-class


there's some sample code for that I have floating around that i know for sure works with Java interop stuff I've been working on


And also see their aws/* namespaces which are empty apart from a call to set-client that interns all the symbols


“Look Ma, no macros!”


Oh one more thing. Reagent has a special hiccup syntax such that you can do: [:< antd.Collapse.Panel {:stuff “this”}]


The downside being it’s a bit of an eyesore if used everywhere. The upside being it calls adapt-react-class and everything for you.


@hagmonk nice! I didn't know about tools.namespace. And thinking about it now, I definitely did too much work with that macro 😄 I think I was just excited about trying a macro. I'll take a look and see if there's a more idiomatic way to do it.


Oh yeah, it's totally natural to write a bunch of macros then discover they aren't strictly needed … that's part of the journey towards judicious use of macros 🙂


@olivergeorge I can see the same slowdown as you, roughly two orders of magnitude

Oliver George22:05:38

yeah, it's a killer if you use defmulti as an extension point but rely on default behaviour most of the time.

Oliver George22:05:16

That seems like a pretty common use case.