How can I conditionally load functionality in a clojure web-app - clojure

I have a clojure web app (standard ring handlers and compojure routes on a jetty server) for which I enabled live asset recompilation as middleware, which has been very very handy in development. As we get closer to production I would like to find a way not to load that code in production and instead read the pre-compiled assets (which I am able to generate as a lein task).
Currently the asset compilation machinery lives in the project code - it can be loaded from the lein task using eval-in-project, so I am able to reuse the same code in both places. However this means that the un-needed files are compiled and included in the production app.
The other issue is that there is one asset compilation tool I'm using that causes the app to fail to load at initialisation time if uberjar'ed since it makes use of native bindings to v8, which are not available (and not needed) when the precompiled assets are available.
How can I avoid loading this code in a production uberjar, but still benefit from dynamic re-compilation at run-time during development and testing?

Your :source-paths key in Leiningen decides which directories are checked for Clojure source code. With per-environment settings of :source-paths you can prevent unwanted namespaces from being included in your depoloyed uberjar.
The next piece of the puzzle is to ensure your code does not rely on the dev code on the production instance. This can be done with the help of the environ lib.
; excerpt of project.clj
(defproject your-org/your-project "version"
:source-paths ["src"] ; the main source location
:profiles {:dev {:source-paths ["dev-src"] ; added directory
:env {:dev "true"}}}
...)
; excerpt of project code for src/your_org/your_project.clj
(ns your-org.your-project
(:require environ.core :refer [env]))
(def maybe-launch-optional-thing
(if (= (env :dev) "true") ; checking a profile specific value
(do (require 'dev-only-dep.core)
(resolve 'dev-only-dep/launch))
(constantly nil))
...
(defn -main
[& args]
(maybe-launch-optional-thing)
...)
The if wrapped require, and the usage of resolve, ensure that this code is valid whether dev-only-dep.core is an available or not. maybe-launch-optional-thing is bound to the appropriate function in the optional namespace under a :dev profile, and is otherwise a no-op.

Related

Can I start Gorilla REPL from an uberjar?

I play with this great Gorilla REPL powered project ( https://bitbucket.org/probprog/anglican-examples/ to be specific), and want to use it under certain restricted circumstances.
Is there a way to produce an uberjar that can be started using only a JVM?
Well, I know how to create an uberjar for this project, but can I start a Gorilla REPL from it? If not what do I have to add and how do I start it?
EDITED Note on Juraj's answer:
I added a start file src/gorillaproxy/gorillaproxy.clj with the following content:
(ns gorillaproxy.gorillaproxy
(:use [gorilla-repl.core :only [run-gorilla-server]])
(:gen-class))
(defn -main
[& args]
(run-gorilla-server {:port 8990}))
Then I added [gorilla-repl "0.4.0"] to the dependency list (in project.clj), and the line
:main gorillaproxy.gorillaproxy
In that way the uberjar started the Gorilla REPL, and when I put the worksheets (and data, resources, .. if needed) into the same directory, everything worked fine.
Gorilla is typically run via the lein-gorilla plugin and thus isn't a part of an uberjar.
If you really want to create a bundle containing gorilla repl dependencies, then you need to add it this capability manually to your project.
The question is why would you want to do that.
Do you want to distribute these samples to somebody else? If that's the case, you'll still need to have all those worksheets in the current directory from where your uberjar is run because that's how gorilla repl discovers worksheets.
You can look at lein-gorilla source code to see how gorilla repl can be started.
I'd then at the same code to your project (create new src/core.clj file or whatever) and configure it in your project.clj as :main.
You'll also need to add gorilla-repl as a dependency to your project.clj
Notice however, that you'll need to run that uberjar from a directory where your anglican worksheets are (or a parent directory of such a directory).

Distributing a simple library via clojars

I have implemented a hyphenation algorithm (at namespace hyphenator-clj.core), defined it as org.clojars.nikonyrh.hyphenator-clj 0.1.0 at defproject and pushed it to Clojars. Uberjar seems to have files like core__init.class, core.clj and core.class.
However when I try to use it as a dependency on an other project I get this error:
$ lein uberjar
Retrieving org/clojars/nikonyrh/hyphenator-clj/org.clojars.nikonyrh.hyphenator-clj/0.1.0/org.clojars.nikonyrh.hyphenator-clj-0.1.0.pom from clojars
Retrieving org/clojars/nikonyrh/hyphenator-clj/org.clojars.nikonyrh.hyphenator-clj/0.1.0/org.clojars.nikonyrh.hyphenator-clj-0.1.0.jar from clojars
Compiling example.core
java.io.FileNotFoundException: Could not locate org/clojars/nikonyrh/hyphenator_clj__init.class or org/clojars/nikonyrh/hyphenator_clj.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name., compiling:(core.clj:1:1)
Exception in thread "main" java.io.FileNotFoundException: Could not locate org/clojars/nikonyrh/hyphenator_clj__init.class or org/clojars/nikonyrh/hyphenator_clj.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name., compiling:(core.clj:1:1)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3657)
at clojure.lang.Compiler.compile1(Compiler.java:7474)
at clojure.lang.Compiler.compile1(Compiler.java:7464)
at clojure.lang.Compiler.compile(Compiler.java:7541)
at clojure.lang.RT.compile(RT.java:406)
at clojure.lang.RT.load(RT.java:451)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
...
Must I change my project's folder structure so that it matches the expected org/clojars/nikonyrh/hyphenator_clj__init.class, or can I somehow override the current behavior? If there is a good tutorial about this out there I would be happy to read it.
Basically I would like to get this example project to work. project.clj:
(defproject example "0.0.1-SNAPSHOT"
:description ""
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojars.nikonyrh.hyphenator-clj "0.1.0"]]
:javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
:aot [example.core]
:main example.core)
src/example/core.clj:
(ns example.core
(:require [org.clojars.nikonyrh.hyphenator-clj :as h])
(:gen-class))
(defn -main [& argv] (doseq [arg argv] (println (h/hyphenate arg :hyphen \-))))
I'm suspecting I also have the english.txt in a wrong directory, as it isn't contained in the uberjar but resource files are an other topic.
You would probably be better off not using a hyphen in the namespace hyphenator-clj. Why not just use hyphenator? But if you do I suspect that the name of the directory should have an underscore in it rather than a hyphen, so be: hyphenator_clj.
If fixing that issue doesn't help then another thing to look out for, which I can't see from your question, is where exactly is core.clj in the directory structure, and does the project.clj reflect that? For instance is the path for the namespace hyphenator-clj.core in a src directory off the root of your project? The root of your project being defined as where the project.clj is located.
Something else that would be good to see in the question is whether you can get the program to work just locally, without having packed it up into an uberjar and shipped it to clojars. My guess would be that it does work locally, but it would help for that to be stated.
Okay taking a look at your links now. You might like to read a working project deployed to clojars, that has hypens in its name, for instance here. The first difference you might notice is the project name you use is rather long: org.clojars.nikonyrh.hyphenator-clj. It should just be hyphenator-clj. Also I would recommend having an identifier ending in "-SNAPSHOT" as that project does.
But taking a look at the bigger picture, a great idea you suggested in the comments is to test without Clojars being in the mix at all. To do this use lein install on the library you want to use from another lein project.
Ahaa at least I understand the process a bit better now, basically my require has to match the structure of the used JAR file, which may be very different from the project's name. For example cc.qbits/spandex is actually required as qbits.spandex.
The english.txt dependency was fixed by moving it to resources folder, deploying the new version to Clojars and importing the dependency as it exists in the JAR:
(ns example.core
(:require [hyphenator-clj.core :as h])
(:gen-class))
(defn -main [& argv] (doseq [arg argv] (println (h/hyphenate arg :hyphen \-))))

access library functions in leiningen REPL

For the development of a library I started from a lein project, invoked like so:
lein new mylib
if I call lein install now, I can access my library in other projects. But trying to immidiately test the functions I wrote failed:
lein repl
...
(dir mylib.core)
Exception No namespace: mylib.core found clojure.core/the-ns (core.clj:4008)
Do I have to add something to the project.clj file maybe?
In order to use a library you must cause the code to be loaded - that it be on the classpath is not sufficient.
You can do this easily in an ns declaration in a file of course, but in the repl it can be easier to use (require '[my-lib.whatever :as w]) after which one can call (w/foo) (w/bar) etc. as expected. You can also use (in-ns 'my-lib.whatever) in order to switch to the namespace, but this will not give you a good result unless you have previously used require or use or load-file etc. to get the definitions first.
Let's say you created a new library named clj-foo.
% lein new clj-foo
Start your repl.
% cd clj-foo
% lein repl
In the repl, load the main entry point to your library and switch to its namespace.
(load-file "src/clj_foo/core.clj")
(ns clj-foo.core)
Now you're in the clj-foo.core namespace, make sure to add back in the repl ns to get things like doc available.
(use 'clojure.repl)
That's it. You're all set to start calling functions in your library. Note that other library files will be available from the clj-foo.core namespace if they were loaded by namespace declaration at the top of clj_foo/core.clj. If not, then you'll need to invoke load-file with their path as well.
If you make changes in core.clj. You can invoke load-file again to pick up the new code. As you progress, you can use cider to facilitate loading of individual functions and files. But that's for another question. :)
You need to add a dependency to use your library from another project. To do this add a vector (a tuple-2) to the vector that is the value of the :dependencies key in the project.clj file. Here's an example:
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.170"]
[org.clojure/core.async "0.2.371"]
[default-db-format "0.1.0-SNAPSHOT"]
[com.andrewmcveigh/cljs-time "0.3.14"]]
My own local library is called default-db-format. Its really no different to adding a dependency for com.andrewmcveigh/cljs-time.
As you say you can already do this, but are having trouble getting a REPL connection to the project of the library itself. When you go (in-ns 'some-path), you need the single quote in front of some-path. Note that some-path is a different thing to the name of your library.
Rather than use lein repl you can use the figwheel repl - if your project is setup with figwheel. My library has only one entry point and that is lein figwheel devcards. After that I had no problem going to a namespace and trying out a function:
cljs.user=> (in-ns 'default-db-format.core)
nil
default-db-format.core=> (check 1 2)
As noisesmith mentioned having a REPL in your IDE is the best setup. No fiddly typing just bring up pre-configured REPLs (per namespace) with the click of a button (or keystroke). Figwheel/Cursive setup instructions here.
I was also facing the same issue with the following configuration:
Leiningen 2.9.0 on Java 1.8.0_201 Java HotSpot(TM) 64-Bit Server VM
My file looks like this, and from the repl I desired to invoke the foo function
(ns cljtest.test
(:gen-class))
(defn foo [input]
(assoc {} "a" 123))
Both these approaches worked fine for me on the repl.
1)Switch to the appropriate name space:
cljtest.core=> (in-ns 'cljtest.test)
#object[clojure.lang.Namespace 0x90175dd "cljtest.test"]
cljtest.test=> (foo nil)
{"a" 123}
cljtest.test=>
2)Require the appropriate name space:
cljtest.core=> (require '[cljtest.test :as test])
nil
cljtest.core=> (test/foo nil)
{"a" 123}
cljtest.core=>

exclude certain clj namespaces from compilation in leiningen

I have a project that works fine using lein run. Now I want to compile it into a standalone jar using lein uberjar. However, there are a couple of source files in my src/projectname/ directory called e.g. playground.clj and stats.clj that I use for experimenting with emacs & the repl, but that I don't want to compile for the final project.
With something like make, I would specify all files that should be compiled. With clojure/leiningen, it seems, all files are compiled by default - how can I exclude files? I haven't found anything in the leiningen docs.
I am currently using :aot :all. Is this the place to change something? Again, I couldn't find detailed documentation on this.
UPDATE:
The suggestions so far haven't worked. What has worked, however, is to include all desired namespaces instead of excluding the ones that should not be compiled. E.g.:
(defproject myproject "version"
;; ...
:profiles {:uberjar {:aot [myproject.data
myproject.db
myproject.util]}})
Have a look at leiningen's sample project.clj, which describes how to use :jar-exclusions or :uberjar-exclusions to exclude arbitrary paths when creating jars (resp. uberjars).
;; Files with names matching any of these patterns will be excluded from jars.
:jar-exclusions [#"(?:^|/).svn/"]
;; Files with names matching any of these patterns will included in the jar
;; even if they'd be skipped otherwise.
:jar-inclusions [#"^\.ebextensions"]
;; Same as :jar-exclusions, but for uberjars.
:uberjar-exclusions [#"META-INF/DUMMY.SF"]
Old question, but I think I found the answer for those coming after me.
I found the answer in the link to the sample leiningen project from #amalloy's answer, except instead of :jar-exclusions I use source-paths, here.
The idea is to create two separate source directories, one for stuff you don't care to spread around and one for stuff you do:
dev-src/<your-project>/playground.clj
dev-src/<your-project>/stats.clj
src/<your-project>/<everything-else>
Then, in your project.clj, include src in source-paths normally, and include emacs-src in a special profile where your want it visible, say the usual :dev profile:
{
;; ...
:source-paths ["src"]
:profiles {
:dev {
:source-paths ["src" "dev-src"]
}
}
}
That way when you're messing around on your machine those files will be in the jar, and when you deploy to clojars or compile with uberjar they will not be included in the jar, nor compiled.
Try this (ns ^:skip-aot my-ns)
You can also do
(ns ^{:skip-aot true} my-ns
(require [...]))
Source

How to run/debug compojure web app via counterclockwise (or la clojure)

I'm trying to write my first web app in compojure. I'm using ccw, and I File-New-Project, Clojure Project and use the "compojure" leiningen template. End up with project.clj looking like
(defproject asdf "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:dependencies [[org.clojure/clojure "1.4.0"]
[compojure "1.1.5"]]
:plugins [[lein-ring "0.8.2"]]
:ring {:handler asdf.handler/app}
:profiles
{:dev {:dependencies [[ring-mock "0.1.3"]]}})
src/asdf/handler.clj looks like
(ns asdf.handler
(:use compojure.core)
(:require [compojure.handler :as handler]
[compojure.route :as route]))
(defroutes app-routes
(GET "/" [] "Hello World")
(route/not-found "Not Found"))
(def app
(handler/site app-routes))
I found I can run this using lein ring server from the command line, but I'm not sure how to run this from eclipse. I'm of course hoping to be able not only to run it, but also to debug it and set breakpoints and such. Is there a way to do this in eclipse? Or, if not, how about IntelliJ/La-Clojure? (I'm a bit afraid of emacs, for now, but maybe if it's super-simple I'd give it a try).
Or, is this just not the typical development process for a compojure app? (If not, what is? Just run lein ring server and pray?)
If it makes a difference this is on Win7.
Here's a recipe that's work great for me while developing Ring applications:
Ensure you have leiningen support properly configured for your projet (do it once if in doubt):
in the package explorer, select the project, and invoke the contextual command Leiningen > Reset configuration
then also invoke the Leiningen > Update dependencies command
you should see a Leiningen Dependencies virtual node in your project, referencing the direct and transitive dependencies of your project
Select the asdf.handler file, right click and then Debug as > Clojure Application
Open the asdf.handler namespace in an editor
With the cursor currently still in the editor, type Ctrl+Alt+N to jump to the REPL and switch the REPL's current namespace to asdf.handler at the same time
Start the app by typing (app) + Enter (or Ctrl+Enter if your cursor is not at the end of the line)
You can now navigate between the editors and the REPL.
To send editor content to the REPL, select it, and hit Ctrl+Enter
If you hit Ctrl+Enter without a selection, the whole 'top level expression' (e.g. a defn) will be sent to the REPL
To resend the whole file to the REPL, type Ctrl+Alt+S
the whole list of keyboard shortcuts specific to CCW is here: http://code.google.com/p/counterclockwise/wiki/EditorKeyBindingsFeatures
Note that a future version of Counterclockwise will integrate a little bit more with Leiningen 2, but as it currently stands, the very nature of developing ring applications make it not so painful to bootstrap things as described above, IMHO
You can run Compojure/Ring apps on IntelliJ IDEA and La Clojure with the following steps:
Generate pom.xml from leiningen's project.clj using lein pom command.
Import maven project with IntelliJ IDEA as usual. You might want to make sure that you have Clojure jar in classpath.
Once the project is loaded, you can start Clojure REPL using Tools -> Start Clojure Console.
To load a Clojure file to REPL, select Tools -> Clojure REPL -> Load file to REPL.
After that, to start a Ring app you can just load a Clojure file that invokes ring.adapter.jetty/run-jetty.
The code to run a simple route on http://localhost:4004/ would look like this:
(require 'compojure.core)
(require 'ring.adapter.jetty)
(ring.adapter.jetty/run-jetty
(compojure.core/routes (compojure.core/ANY "/" [] "Hello world!"))
{:port 4004 :join? false})
:join? option is important, if it would be set to true (the default), the REPL would not accept more commands. Your routes will usually be more complex and compojure.core/defroutes or other means should be used.
You can put such file in test path, so it wouldn't be loaded when running the project outside of IDEA.
If Clojure facet is not added to your module, you can add it in File -> Project Structure -> Modules.
A complete sample (with jetty reloading) is available here: https://github.com/tlipski/ganelon-demo - development is done with IDEA and real site runs on Heroku: http://ganelon.herokuapp.com.
Debugging Clojure apps run with the technique above is possible as well - you just have to:
Create Remote debugging Run profile in IntelliJ IDEA
Add apropriate JVM options from a profile above (e.g. agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005) to REPL settings in File -> Project Structure -> Modules -> [your module] -> Clojure facet -> JVM Arguments field.
Start REPL with Tools -> Start Clojure Console.
Start Remote debugging profile.
After that, you can add breakpoints, inspect variables, etc.