Fork me on GitHub
#clojurescript
<
2017-05-02
>
john00:05:22

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

souenzzo01:05:40

@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...

rarous17:05:15

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

souenzzo19:05:17

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

souenzzo01:05:02

I think that would be an easier way

john01:05:20

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

(defn my-clj->js
   [x]
   (when-not (nil? x)
     (if (satisfies? IEncodeJS x)
       (-clj->js x)
       (cond
         (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)))
                    m)
         (coll? x) (let [arr (array)]
                     (doseq [x (map clj->js x)]
                       (.push arr x))
                     arr)
         :else x))))
;modified cljs, Copyright © Rich Hickey ;)

john01:05:18

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

john02:05:04

@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
   [x]
   (when-not (nil? x)
     (if (satisfies? IEncodeJS x)
       (-clj->js x)
       (cond
         (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)))
                    m)
         (coll? x) (let [arr (array)]
                     (doseq [x (map my-clj->js x)]
                       (.push arr x))
                     arr)
         :else x))))
All slightly modified cljs

john02:05:35

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

qqq05:05:59

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.

thheller10:05:43

@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

thheller10:05:05

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.

rauh10:05:30

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

rauh10:05:42

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.

rauh10:05:26

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?

thheller10:05:26

yes, maybe 🙂

rauh11:05:23

@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))
                                                   e
                                                   be)]
                                         (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"))))
                                         be2)
                                       be))
                                   nil @method-table)
                           (get @method-table dispatch-val))]
          (when best-entry
            (if (= @cached-hierarchy @hierarchy)
              (do
                (swap! method-cache assoc dispatch-val (second best-entry))
                (second best-entry))
              (do
                (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.

hagmonk14:05:36

something of a noob question here: I'm using reagent and ant.design, 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

pesterhazy14:05:30

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

hagmonk14:05:06

it appears they wrap another component called rc-collapse

hagmonk14:05:26

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

pesterhazy14:05:17

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

pesterhazy14:05:32

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

pesterhazy14:05:42

I might be looking in the wrong place though

zacanger14:05:09

I'm just learning cljs, but know react pretty well. @pesterhazy is right there, there's no state at all in that component https://github.com/ant-design/ant-design/blob/master/components/collapse/index.tsx#L28

hagmonk14:05:09

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

hagmonk14:05:28

hmm so would the solution be to wrap it myself?

pesterhazy14:05:30

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

hagmonk14:05:08

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

pesterhazy14:05:35

yeah, not sure what to recommend

pesterhazy14:05:10

I'd look into reactstrap (bootstrap) perhaps

pesterhazy14:05:36

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

zacanger14:05:04

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

pesterhazy14:05:48

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

ksmithbaylor15:05:27

@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?

ksmithbaylor15:05:07

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

ksmithbaylor15:05:12

(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"]])

ksmithbaylor15:05:56

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

ksmithbaylor15:05:18

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.

samueldev15:05:45

I'm having some trouble with an async cljs test

samueldev15:05:33

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

samueldev15:05:45

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

samueldev15:05:53

But it intermittently fails and passes

samueldev15:05:48

The exact same test, the next run:

samueldev16:05:15

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

samueldev16:05:39

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

hagmonk16:05:21

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

hagmonk16:05:34

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

notkevinc16:05:50

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?

ksmithbaylor16:05:30

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

ksmithbaylor16:05:51

you could easily do [@which] instead though

hagmonk16:05:01

@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

hagmonk16:05:24

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

ksmithbaylor16:05:47

@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

hagmonk16:05:25

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

ksmithbaylor17:05:39

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

ksmithbaylor17:05:09

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

hagmonk17:05:26

@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

hagmonk17:05:49

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

hagmonk18:05:37

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

hagmonk18:05:52

“Look Ma, no macros!”

hagmonk18:05:44

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

hagmonk18:05:29

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.

ksmithbaylor21:05:37

@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.

hagmonk21:05:03

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 🙂

danielcompton21:05:42

@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.