Fork me on GitHub
#boot
<
2016-09-20
>
alexyakushev10:09:53

I'm trying to prefetch some dependencies for my project from Maven/Clojars, and save them into the Docker image.

alexyakushev10:09:29

However, I don't want to keep them directly in /root/.m2/ because I then plan to mount host's .m2/ on top of the image.

alexyakushev10:09:37

I suppose that the solution would be to copy /root/.m2 to say /root/.m2cache, and then specify file:///root/.m2cache as one of the :repositories in /root/.boot/profile.boot.

alexyakushev11:09:34

Should this be enough? I tried doing all this, and then deleted /root/.m2/, but Boot still downloads the dependencies from Maven and Clojars instead of copying the from the .m2cached.

dominicm11:09:16

@alexyakushev I might guess that it's done in priority order.

dominicm11:09:47

I'm guessing in profile.boot that you conj to the repositories?

juhoteperi11:09:31

There is also: BOOT_LOCAL_REPO The local Maven repo path (~/.m2/repository).

dominicm11:09:10

@juhoteperi I don't think that would work because he wants both .m2 and .m2cache

alexyakushev11:09:31

@dominicm: I thought it was priorities, but then I tried completely removing other repositories (value instead of conj) and it is still the same

dominicm11:09:53

Odd, I wouldn't expect boot to be able to grab from maven when repositories is unset?

alexyakushev11:09:03

boot show -e shows:

:repositories
 [["m2-prefetched" {:url "file:///root/.m2_prefetched/repository/"}]]

alexyakushev11:09:30

There are two problems, actually. First is that many dependencies are still downloaded from and from Clojars. But eventually, it starts grabbing some from my local repo, and then at some point it fails with:

Exception in thread "FileRepositoryConnector-1" java.lang.IllegalArgumentException: number of transferred bytes cannot be negative

alexyakushev11:09:03

At the very least, I wanted to figure out if I'm moving in the right direction at all. Can a local repository be used like this, without any additional "wagons"? Also, does Boot at all depend on Maven's configuration?

alexyakushev11:09:27

@dominicm Thank you! I will try to investigate what can be done about this.

dominicm11:09:00

@alexyakushev report back if you remember, I'm always interested to know how these things turn out!

coyotespike14:09:19

Howdy y'all. The basic Buddy auth example for JWE tokens doesn't seem to be working for me. I'm using Boot and System with Sente to send the POST request to login, and it returns success. But I always get unauthorized when I check with buddy/authenticated?

coyotespike14:09:09

I'm guessing the problem is that Buddy only checks the :identity key in the Ring map, but the example puts the token elsewhere? Ahdunno, tried this for a few hours yesterday.

coyotespike14:09:20

I kind of want to use the Buddy middleware because I figure it will easily tell if the token has expired.

martinklepsch14:09:50

@coyotespike maybe better asked in #clojure or #funcool ?

coyotespike14:09:24

Yeah, I wasn't very sure where to ask this one - I'll try #funcool and then #clojure, thanks

coyotespike14:09:49

🙂 didn't know we had a funcool channel

martinklepsch15:09:06

Is there a way to disable namespace pretty printing?

coyotespike19:09:34

Boot is hot-reloading my DevCards (yay!!) but not my regular page .

coyotespike19:09:54

If my dev task is just this, then nothing really happens:

(deftask dev
  "Run a restartable system in the Repl"
  []
  (comp
   (environ :env config)
   (watch :verbose true)
   (system :sys #'dev-system :auto true :files ["handler.clj" "services.clj"])
   (reload)
   (cljs :source-map true
         :compiler-options {:devcards true})
   (repl :server true)
   (speak)))

coyotespike19:09:52

If I make it look like this, then it *does hot reload*, but I get target container is not a DOM element:

(deftask development []
  (task-options! cljs {:optimizations :none
                       :unified-mode true
                       :source-map true
                       :compiler-options {:devcards true}}
                 reload {:on-jsload 'holy-grail.core/app})
  identity)

(deftask dev
  "Run a restartable system in the Repl"
  []
  (comp
   (development)
   (environ :env config)
   (watch :verbose true)
   (system :sys #'dev-system :auto true :files ["handler.clj" "services.clj"])
   (reload)
   (cljs)
   (repl :server true)
   (speak)))

coyotespike19:09:06

Seems like I'm really close!

coyotespike20:09:21

Hmmm...I figured out the source of the error, nothing to do with build.boot

coyotespike20:09:50

Instead it's Secretary.

coyotespike20:09:20

For some reason this function with reagent messes with Boot's reload:

(defn hook-browser-navigation! []
  (doto (History.)
    (events/listen
      HistoryEventType/NAVIGATE
      (fn [event]
        (secretary/dispatch! (.-token event))))
    (.setEnabled true)))

dominicm20:09:30

@coyotespike You running that on every reload?

coyotespike20:09:58

@dominicm Yes, I put it in my init function

dominicm20:09:57

Probably unrelated. But that might cause you to have multiple listeners to the history.

coyotespike20:09:43

hmmm interesting

dominicm20:09:50

I'm not certain, but it could happen

coyotespike20:09:01

I think you might be right - at least, it looks like as a var it will hook in history, and since it's not in the init function it's not causing an error

coyotespike20:09:54

That did fix the reloading, thanks for your input @dominicm 😄.....now to get History working again...

coyotespike21:09:49

I think this bit is still Boot-related...after I update the page and the browser reloads, I'm able to navigate, just get the error: history.js:899 Uncaught TypeError: Cannot set property 'value' of null

coyotespike21:09:00

everything works so I can ignore it, but it's odd.

coyotespike21:09:17

Before a hot-reload, a navigation event does not trigger that error.

micha21:09:49

that hook-browser-navigation function will add more history event handlers every time it is called

micha21:09:17

i would expect weird things to happen if you have multiple handlers there

coyotespike21:09:59

Thanks @micha, that's really good to know

coyotespike21:09:14

I now have history like:

(def history
  (doto (History.)
    (events/listen EventType.NAVIGATE
                   (fn [event] (secretary/dispatch! (.-token event))))
    (.setEnabled true)))

coyotespike21:09:32

And init function like:

(defn app
  "Configure and bootstrap the application."
   []
  (dispatch-sync [:initialise-db])
  (when (identical? config/production false)
    ;; -- Debugging aids ----------------------------------------------------------
    (devtools/install!)       ;; we love 
    (enable-console-print!))   ;; so println writes to console.log
    
  (reagent/render [todo-app] (.getElementById js/document "container"))
  (sente/start-chsk-router! ch-chsk event-msg-handler*))

coyotespike21:09:47

Bad weirdness gone without history as a function, just errors in console after hot reload. Back button works, etc.

micha21:09:13

so in hoplon we have a construct like definterval

micha21:09:34

like if you have a function that does (js/setInterval ...)

micha21:09:45

that will keep adding more and more of them every time that code is reloaded

micha21:09:10

so one solution is to make sure you always remove any existing handlers before adding a new one

micha21:09:18

that's why definterval

micha21:09:22

because now it has a name

micha21:09:38

(definterval foo 100 (prn 42))

micha21:09:44

that would print 42 every 100 ms

micha21:09:16

but since it now is associated with a var, foo, the definterval macro actually emits code that removes any existing interval named foo

micha21:09:21

before adding the new one

micha21:09:36

seems like this approach could also work with event handlers

juhoteperi21:09:56

goog.events/listen returns a key which can be used to remove the listener using goog.events/unlisten

micha21:09:57

you could add a property to the history object that has some handle you can use to remove the previous event

micha21:09:25

yeah so you could add a property to History that holds that key

coyotespike21:09:29

huh...that's really interesting.

juhoteperi21:09:31

you can save the value to a atom and clear the previous listener before adding new

micha21:09:56

if you save it to the History object itself then you avoid any interaction with code reloading

juhoteperi21:09:16

* goog.events/unlistenByKey

micha21:09:17

because if the History object itself is destroyed then you don't care about events on it

micha21:09:01

(set! (.-rmMyEvent js/History) (goog/listen ...))

coyotespike21:09:49

I'm a little surprised other people haven't hit this issue with Boot and Secretary/goog.events...that's an interesting solution

micha21:09:41

(when-let [key (aget js/History "rmMyEvent")]
  (goog/unlisten key))
(set! (.-rmMyEvent js/History) (goog/listen ...))

micha21:09:23

seems like a macro would be straightforward:

coyotespike21:09:19

I mean, to the degree that macros are straightforward 😉

micha21:09:48

(defmacro defhandler
  [name obj event handler]
  (let [prop (str "_defhandler_" name)]
    `(let [obj# ~obj]
       (when-let [k# (aget obj# ~prop)]
         (events/unlisten k#))
       (aset obj# ~prop (events/listen obj# ~event ~handler))
       obj#)))

micha21:09:49

(defhandler hist-nav js/History EventType.NAVIGATE (fn [x] ...))

juhoteperi21:09:20

Most apps need only one such event listener and then a macro is overkill

micha21:09:00

it's a pain to debug such things

micha21:09:08

which is where the macro is useful, imho

micha21:09:27

the macro establishes good hygeine

micha21:09:50

otherwise it could be that the code that does the unhooking itself gets reloaded and you lose your state

micha21:09:58

then you can have multiple handlers again

micha21:09:32

with messy things like patching code in place i think it makes sense to spend some time to get it right up front

micha21:09:02

because there are few things i get more annoyed at than debugging race conditions or state stuff like this

micha21:09:05

in my own apps

juhoteperi21:09:23

simples thing is to just keep the listener key in atom which is defonce'd and there is never problems

micha21:09:34

but that atom could be part of the code that gets reloaded

micha21:09:19

the handler itself is stored on the object

coyotespike21:09:10

tbh I'm not quite sure what usage would look like

coyotespike21:09:17

I'll play around with it

micha21:09:18

actually hte macro is pointless lol

micha21:09:36

the events/listen function should allow you to specify the key

micha21:09:45

like clojure.core/add-watch does

micha21:09:56

that way it would just replace the handler itself

micha21:09:25

(events/listen js/History event-type "my-event-key" (fn [x] ...))

juhoteperi21:09:57

the second parameter is event type

micha21:09:12

that's what i'd do anyway

coyotespike21:09:17

(def history
    (events/listen js/History EventType.NAVIGATE
                   (fn [event] (secretary/dispatch! (.-token event))))
    (.setEnabled true)))

juhoteperi21:09:45

Solution used by several project templates is to just not call this code after reload

coyotespike21:09:03

Like put it in another file and sift it out?

juhoteperi21:09:04

just don't call it in init function

juhoteperi21:09:15

or whatever is the function that called on every function

coyotespike21:09:05

oh, yeah, I took it out of the init function, and now it hot-reloads without gross errors

coyotespike21:09:37

It is now just a var, unmentioned in my init file, like this:

(def history
  (doto (History.)
    (events/listen EventType.NAVIGATE
                   (fn [event] (secretary/dispatch! (.-token event))))
    (.setEnabled true)))

juhoteperi22:09:04

that will still be re-evaluated when that file changes

coyotespike22:09:11

This hot-reloads, and navigates. After a hot-reload, it just starts complaining at me.

juhoteperi22:09:17

but you could change that to defonce and it won't be re-evaluated

coyotespike22:09:30

you're completely right!

coyotespike22:09:38

Errors magically gone 😄