Clojure: refresh running web app when html files change - clojure

I've set up my project with lein-ring to allow hot code reload. It does work when I change any .clj file while the app is running...
How can I make it the same for change in any html, css and js files. (located in resources/public...)
Here is my project.clj set-up:
(defproject ...
:plugins [[lein-cljsbuild "1.0.4"]
[lein-ring "0.9.2"]]
:ring {:handler votepourca.server/handler
:auto-reload? true
:auto-refresh? true}
:resource-paths ["resources" "markup"]
:source-paths ["src/clj"]
...)
EDIT:
I am using Enlive, and apparently, it needs an extra ring wrapper to allow static file reloading: [com.akolov.enlive-reload "0.1.0"]
So in my server.clj/core.clj/handler.clj, I now have this and it works perfectly!
(:require
[ring.middleware.reload :refer [wrap-reload]]
[com.akolov.enlive-reload :refer [wrap-enlive-reload]])
...
(defn app [routes]
(-> routes
(wrap-params)
(wrap-reload)
(wrap-enlive-reload))))
Thank you to "Kolov" the author of this lib https://github.com/kolov/enlive-reload

Add :reload-paths in addition to :auto-reload?/:auto-refresh?. https://github.com/weavejester/lein-ring/blob/master/src/leiningen/ring/server.clj#L25

Related

Clojure dependencies for tests only

In my project.cli I have a dependency on clj-http that is used for tests only, with clj-http.client.
When I look at the uberjar file created for that project, I see that the class fils associated with this dependency are included. That makes the jar file bigger than it need be.
So, is there a way to define a dependency in clojure such that it is only used during tests, and is not included in the uberjar?
I know that I could do this in a pom.xml, but the pom.xml is generated when using clojure, so I only have recourse to something that works in the project.clj file.
To add more colour, my project.clj looks like this
(defproject aproject "0.1.0-SNAPSHOT"
:description "A project"
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/data.json "0.2.6"]
[compojure "1.5.0"]
[hiccup "1.0.5"]
[http-kit "2.1.18"]
[org.clojure/tools.logging "0.3.1"]
[ch.qos.logback/logback-classic "1.1.7"]
[ring/ring-devel "1.4.0"]]
:plugins [[lein-ring "0.9.7"]]
:ring {:handler aproject.core/app-routes}
:main ^:skip-aot aproject.core
:target-path "target/%s"
:resources-paths ["resources/"]
:profiles {:uberjar {:aot :all}
:dev {:dependencies [[peridot "0.4.3"]
[midje "1.8.3"]]
:plugins [[lein-midje "3.2.1"]]
:aliases {"test" ["midje"]}}
:test {:dependencies [[clj-http "3.5.0"]
[midje "1.8.3"]]
:plugins [[lein-midje "3.2.1"]]}
})
I am running the tests like this:
lein with-profile test midje :filters dt
What I am seeing is:
Exception in thread "main" java.io.FileNotFoundException: Could not locate midje/util/ecosystem__init.class or midje/util/ecosystem.clj on classpath., compiling:(/private/var/folders/7l/0fwd_7ls1m19q3_z1_tgg1w80000gn/T/form-init7253661442775183594.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7391)
The filter probably does not affect this, but just in case the test looks like this:
(ns aproject.deployment.core
(:require [midje.sweet :refer :all]
[clj-http.client :as client]
[peridot.core :as p]
[clojure.data.json :as json]
[front-end.core :as fe]))
(facts "'aproject' deployed" :dt
(let [response (client/get "http://localhost:8080/ping")]
(response :status) => 200
))
I can see that the test profile is being triggered, and I seem to have the dependency for midje, and the plugin, but ...?
Thanks
Nathan
Add it to the :test profile, then run your tests with lein with-profile test midje :filters dt. Generate your uberjar as usual, lein uberjar and it shouldn't include the extra files.

Dynamic leiningen :profiles

I'm trying to use a function as the value for the :profiles key in a defproject form. Starting from a fresh project (lein new app test) this works fine:
:profiles {}
(as you might hope!). But if I change it to:
:profiles (merge {})
then when I run lein repl it explodes:
Caused by: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry
I'm confused by this since if I set :profiles back to the empty map and ask the repl these things are equal:
test.core=> (= {} (merge {}))
true
Where is my misunderstanding? Have I missed something basic? Is this an unfortunate artifact of the defproject macro? Something else?
(clojure 1.8.0, leiningen 2.7.1, java 1.8.0_102)
Edit - working solution with Scott's answer:
(def project-name 'myproj)
(def mains ["foo" "bar"])
...
(defn- lein-alias [main]
{ main ["with-profile" main] })
(defn- lein-profile [main]
(let [jar (str main ".jar")
entry `~(str project-name "." main)]
{(keyword main) {:main entry
:bin {:name main}
:jar-name jar
:uberjar-name jar}}))
(defproject project-name "0.1.0"
...
:profiles ~(apply merge (concat (map lein-profile mains) {:uberjar {:aot :all}}))
:aliases ~(apply merge (map lein-alias mains))
...
So now I can lein foo bin and lein bar bin to my heart's content.
If you unquote your form Leiningen will execute the form before the project map is evaluated. So ~(merge {}) should work.
There is a function called unquote-project in Leiningen
src/leiningen/core/project.clj#L176
"Inside defproject forms, unquoting (~) allows for arbitrary evaluation."
It looks like it looks items to unquote to allow them to be execute. Based on the comment it looks like this might go away in 3.0 and suggests use read-eval syntax
Note: If you are just trying to merge values of different profiles you should look at the Profiles documentation About Merging and Composite Profiles
Composite Profiles
Sometimes it is useful to define a profile as a combination of other profiles. To do this, just use a vector instead of a map as the profile value. This can be used to avoid duplication:
{:shared {:port 9229, :protocol "https"}
:qa [:shared {:servers ["qa.mycorp.com"]}]
:stage [:shared {:servers ["stage.mycorp.com"]}]
:production [:shared {:servers ["prod1.mycorp.com", "prod1.mycorp.com"]}]}
You do not merge the profiles manually. They are merged together by lein either automatically (normal case) or when you use the with-profile keyword (manual control).
For example, consider this project.clj:
(defproject xyz "0.1.0-SNAPSHOT"
:dependencies [ [org.clojure/clojure "1.8.0"]
[tupelo "0.9.19"] ]
:profiles {:dev {:dependencies [ [org.clojure/test.check "0.9.0"]
[criterium "0.4.4"] ] }
:sample {:dependencies [medley "0.8.2"] }
...
)
This project.clj says that the project always requires both org.clojure/clojure and tupelo. During development, the map for :dev will be merged into the root-level, so :dependencies will be updated to include both test.check and criterium. The :dev profile values are not included when a uberjar is created, however, so these libs won't be included in the code delivered to users.
Since :sample is not one of the default profiles, it will only be included if you use a command like:
> lein with-profile sample test
Notice that the leading colon is not included on the command line, although we use a keyword :sample with the colon in the project.clj file.
Full details are here: https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#default-profiles
and here: https://github.com/technomancy/leiningen/blob/master/sample.project.clj
Having said all this, I normally don't need to use :profiles. Unless you have something more complex than usual, you should usually just put all of your dependencies at the root level (i.e. the :dependencies keyword at the first level under (defproject xyz ...) in the project that lein new app xyz gives you.
Another suggestion: the word test is used in many places in a lein project (directory name, file name suffix, and others), so it can be very confusion to name the project itself test! You'll save yourself (and any other readers) some grief if you choose a unique name like xyz, joe, or anything else.

Auto-refresh/ Auto-reload assets

I'm new to clojure and i'm having a hard time figuring out how to reload/ refresh the browser when changes have been made to either html/ js/ css etc.
this is my current setup project.clj
(defproject app2 "0.1.0-SNAPSHOT"
:description "FIXME: write this!"
:url "http://exampl.com/FIXME"
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.89"]
[ring/ring-core "1.5.0"]
[ring/ring-jetty-adapter "1.5.0"]
[enfocus "2.0.0-SNAPSHOT"]]
:plugins [[lein-cljsbuild "1.1.3"]
[lein-ring "0.9.7"]]
:cljsbuild {:builds [{:source-paths ["src/cljs"],
:compiler {
:main "scripts.client"
:output-to "resources/public/js/main.js"
:output-dir "resources/public/js/out"
:asset-path "js/out"
;;:pretty-print true
;;:optimizations :none
}}]}
:main app2.server/app
:ring {:handler app2.server/app :auto-reload? true :auto-refresh? true :reload-paths ["src" "resources"]}
:profiles {
:dev {
:ring {
:nrepl {
:start? true
:port 9000
}
}
}
}
)
This is my server.clj
(ns app2.server
(:use [ring.middleware.resource :only [wrap-resource]]
[ring.middleware.file-info :only [wrap-file-info]]
[ring.middleware.reload :refer [wrap-reload]])
;;(:require app2.repl)
)
(defn handler
[request]
{:status 200}
)
;handling routing "/" -> "/index.html"
(defn wrap-index [handler]
(fn [req]
(println (pr-str req))
(if (= (:uri req) "/")
(handler (assoc req :uri "/index.html"))
(handler req))))
;setting up a simple resource handler for ring
(def app (-> handler
(wrap-resource "public")
(wrap-file-info)
(wrap-index)
(wrap-reload app {:dirs ["src" "resources"]})
))
How can this be accomplished?
I'm used to developing in node and you have tools like browser sync, weinre and supervisor. What are the equivalents in clojure?
I suggest you have a look at figwheel, which lets you do hot reloading of your ClojureScript and CSS in the browser.
There is of course not one good way of setting up your build, but my way to go for languages like SASS etc. is to watch and compile them as a separate process, and have Figwheel watch the generated CSS.
For example, on one of my ClojureScript projects, I had a script file for LESS compilation which used the LESS compiler and the wr utility directly:
#!/usr/bin/env bash
lessc src/styles/main.main.less resources/public/css/main.css --source-map && cp src/styles/*.less resources/public/css
wr "lessc src/styles/main.main.less resources/public/css/main.css --source-map && cp src/styles/*.less resources/public/css" src/**/*.less
Of course you can also use things like Gulp, Webpack - or any tool you're used to.
The alternative is to use Leiningen plugins, see the list here.

Leiningen 2-steps build: how to combine lein-less and lein-asset-minifier

I'm making a Clojure/ClojureScript website that I deploy on Heroku. I'm using Leiningen for project management.
I want to write my stylesheets in LESS. I need the build process to compile the LESS files into CSS, then minify these CSS files; obviously, I do not want the generated CSS files to be under version control.
LESS files --lein-less--> CSS files --lein-asset-minifier--> minified CSS files
I have tried to implement this with the lein-less and lein-asset-minifier Leiningen plugins. My attempt consisted of declaring leiningen.less and minify-assets.plugin/hooks as hooks of the :uberjar task, in proper order (see code below). But running lein uberjar fails with the following error:
Uberjar aborting because jar failed: resources/public/css/site.css (No such file or directory)
So it appears the order of build steps is not enforced.
Is it possible to implement this multi-step build with these Leiningen plugins? If not, how are people doing it?
Code
Here is the relevant part of my project.clj :
(defproject sncf-cljs "0.1.0-SNAPSHOT"
;; ...
:min-lein-version "2.5.0"
:source-paths ["src/clj" "src/cljs"]
:dependencies [
;; ...
]
:plugins [
[lein-cljsbuild "1.0.4"]
[lein-environ "1.0.0"]
[lein-ring "0.9.1"]
[lein-asset-minifier "0.2.2"]
[lein-less "1.7.2"]]
:less {:source-paths ["src/less"]
:target-path "resources/public/css"}
:uberjar-name "sncf-cljs.jar"
:minify-assets {:assets
{"resources/public/css/site.min.css" "resources/public/css/site.css"}}
:cljsbuild {
;; ...
}
:profiles {
;; ...
:uberjar {:hooks [leiningen.less
leiningen.cljsbuild
minify-assets.plugin/hooks]
:env {:production true}
:aot :all
:omit-source true
:cljsbuild {:jar true
:builds {:app
{:source-paths ["env/prod/cljs"]
:compiler
{:optimizations :advanced
:pretty-print false}}}}}
:production {:ring {:open-browser? false
:stacktraces? false
:auto-reload? false}
:cljsbuild {:builds {:app {:compiler {:main "sncf-cljs.prod"}}}}
}})
I did some research because it is an interesting question. So I looked around, and could not find informations how to configure this. So I continued to search and I found a plugin for this task.
https://github.com/kumarshantanu/lein-cascade
The readme on the github page should be sufficient to solve this task.

How to access values on leiningen profiles?

I've got a two profiles defined in project.clj, one locally, one for testing on travis:
:profiles {:dev {:dependencies [[midje "1.6.0"]
[mysql/mysql-connector-java "5.1.25"]]
:plugins [[lein-midje "3.1.3"]]
:user "root" :pass "root"}
:travis {:user "travis" :pass ""}}
I'm hoping to be able to get access to the :user and :pass values in my projects. How can this be done?
Update:
I also want to be able to use the lein with-profile command... so my tests would have:
lein with-profile dev test
-> would use "root", "root" credentials
lein with-profile dev,travis test
-> would use "travis", "" credentials
If you don't need the values defined in project.clj for anything else (IE, you're free to choose the representation) consider Environ.
You can then define the following in your project.clj
:profiles {:dev {:env {:user "root" :pass "root"}}}
and read the values:
(use 'environ.core)
(def creds
{:user (env :user)
:pass (env :pass)})
This has the advantage that you can also specify the values using environment variables and system properties.
Leiningen's build file is Clojure code so you can just read it in:
(->> "project.clj" slurp read-string (drop 3) (partition 2) (map vec) (into {})
:profiles :dev)
; => {:dependencies [[midje "1.5.1"] [ring-server "0.2.8"]], :plugins [[lein-midje "3.1.0"]]}
If you need heavier functionalities (such as access to the final project map) then something like configleaf might be better suited.
Another way to manage this (which I've utilized quite often) is to have a separate config file for profile specific data:
example/profiles/travis/example/config.clj:
(ns example.config)
(def user "travis")
(def pass "")
example/dev-resources/example/config.clj:
(ns example.config)
(def user "root")
(def pass "root")
example/src/example/core.clj:
(ns example.core
(:require [example.config :as config]))
(println config/user)
And you need to add the profile specific resource path to your project.clj:
:profiles {:travis {:resource-paths ["profiles/travis/"]}}