Is it possible to grab the project information within the clojure repl?
For example, if there was a project defined:
(defproject blahproject "0.1.2" ....)
When running a repl in the project directory, is there a function like this?
> (project-version)
;=> 0.1.2
While you can parse project.clj yourself, this may be annoying. It's also a lot of work. Instead, you can just do:
(System/getProperty "projectname.version")
Leiningen project files are just Clojure data :)
(-> "/path/to/project.clj" slurp read-string (nth 2))
I use environ (https://github.com/weavejester/environ) which sucks in settings from a number of sources, including system properties. The project version appears as :<project-name>-version:
foobar.repl=> (require '[environ.core :refer [env]])
nil
foobar.repl=> (:foobar-version env)
"0.1.0-SNAPSHOT"
Add the below code to the end of project.clj:
(def project (assoc-in project [:repl-options :init]
`(~'def ~'project-version ~(project :version))))
Now you will have a var called project-version in the initial namespace for the repl.
As described in this discussion.
(ns myproject.example
(:require [clojure.java.io :as io])
(:import java.util.Properties))
(defn get-version [dep]
(let [path (str "META-INF/maven/" (or (namespace dep) (name dep))
"/" (name dep) "/pom.properties")
props (io/resource path)]
(when props
(with-open [stream (io/input-stream props)]
(let [props (doto (Properties.) (.load stream))]
(.getProperty props "version"))))))
(get-version 'myproject) ; => 0.1.0
(get-version 'org.clojure/clojure) ; => 1.3.0
As vemv said, Leiningen project files are just Clojure data. So, it's easy to access your project as an ordinary hash-map:
(->> "project.clj"
slurp
read-string
(drop 2)
(cons :version)
(apply hash-map)
(def project))
If you need this variable only in your repl, you can add it to repl-options to your project.clj:
(defproject yourproject "0.1.0"
:description ""
:url ""
:dependencies [ [org.clojure/clojure "1.4.0"]]
:repl-options { :init (->> "project.clj"
slurp
read-string
(drop 2)
(cons :version)
(apply hash-map)
(def project))})
Now, you have project variable in your repl. So, to access the version of your project you can simply type (:version project).
Of course, you can simply use native Leiningen code to parse you project file:
(defproject yourproject "0.1.0"
:description ""
:url ""
:dependencies [ [org.clojure/clojure "1.4.0"]
[leiningen-core "2.1.3"]]
:repl-options { :init (do (require 'leiningen.core.project)
(def project
(leiningen.core.project/read)))})
But, if you need only the version of your project and nothing more, then it's best to use Ankur's solution.
For a more fully-featured approach, you might want to take a look at the configleaf plugin for Leiningen (https://github.com/davidsantiago/configleaf). It will make the project map, with active profiles merged in, available to project code in a namespace of your choosing.
In case you need to do this from clojurescript you could create a macro (from another clj file) and call it from the cljs code :
;;ex: macro.clj
(defmacro get-project-version []
(System/getProperty "penelope.version"))
;;my_logic_code.cljs
(ns my-logic-code
(:require-macros [macros :as m]))
(def project-version (m/get-project-version))
Related
I need to read a clojure function from an edn file which outputs hiccup to generate html content.
But I'm stuck at the part where the function needs to be evaluated.
I receive the error message:
java.lang.RuntimeException: Unable to resolve symbol: fn in this context, compiling:(null:1:1)
((eval (read-string "(fn [] (list [:div\"Hello\"]))")))
and
((load-string "(fn [] (list [:div\"Hello\"]))"))
are working inside a clojure REPL and output the expected result
([:div "Hello"])
project.clj
(defproject infocenter "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.225"]
[hiccup "1.0.5"]]
:plugins [[lein-figwheel "0.5.4-7"]]
:clean-targets ^{:protect false} [:target-path "out" "resources/public/cljs"]
:cljsbuild {
:builds [{:id "dev"
:source-paths ["src"]
:figwheel true
:compiler {:main "templ.core"
:asset-path "cljs/out"
:output-to "resources/public/cljs/main.js"
:output-dir "resources/public/cljs/out"}}]}
:figwheel {
:css-dirs ["resources/public/template"]})
core.cljs
(ns templ.core
(:require-macros [templ.edn :refer [read-edn]]))
(let [div (. js/document getElementById "content")]
(set! (. div -innerHTML) (read-edn "fn.edn")))
edn.clj
(ns templ.edn
(:use [hiccup core form]))
(defmacro read-edn
"Read template from file in resources/"
[edn-name]
;(slurp edn-name)
;((eval (read-string "(fn [] (list [:div\"Hello\"]))")))
((load-string "(fn [] (list [:div\"Hello\"]))"))
)
Try this:
(ns xyz.core
(:gen-class))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "main - enter")
(println (eval '(+ 2 3)))
(println ((eval (read-string "(fn [] (list [:div\"Hello\"]))"))))
(println "main - exit")
)
with results:
> lein run
main - enter
5
([:div Hello])
main - exit
This is just plain Clojure, and I see you are using CLJS as well. There may be some problem in your setup with the CLJS part of it. What are you trying to do with read-edn and fn.edn? I would avoid macros completely whenever possible.
I want to produce a command line version of a clojure library using clojure/tools.cli and lein bin. This works fine except I am getting output to stderr when I run the script. This particular library has functions that override some basic functions, so naturally there are warnings when the clojure code is compiled. I know this is generally a bad idea but after careful consideration in this case I believe it is the best way to go. How do I stop these messages from appearing every time I run the script?
I added slf4j-nop to the dependencies as recommended in the monger documentation to suppress unwanted messages from monger, and this works, but has no effect on the warnings from the clojure compiler.
I have also tried using slf4j-log as detailed here: suppress output from `clojure.tools.logging` but without success.
Here is some of the code:
project.clj
(defproject image-search "0.1.0-SNAPSHOT"
:description "Search for images containing specific metadata"
:url "http://github.com/soulflyer/image-search"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/tools.cli "0.3.3"]
[org.slf4j/slf4j-nop "1.7.21"]
[image-lib "0.1.1-SNAPSHOT"]]
:main image-search.command-line
:bin {:name "image-search"
:bin-path "~/bin"})
and command_line.clj:
(ns image-search.command-line
(:require [image-search.core :refer [open all-images ifeq ifin ]]
[clojure.tools.cli :refer :all])
(:gen-class))
(def cli-options
[["-c" "--count" "Counts the results"]
["-D" "--database DATABASE" "specifies database to use"
:default "photos"]
["-I" "--image-collection IMAGE-COLLECTION" "specifies the image collection"
:default "images"]
["-K" "--keyword-collection KEYWORD-COLLECTION" "specifies the keyword collection"
:default "keywords"]
["-h" "--help"]
["-i" "--iso ISO" "Search on ISO value"]
["-s" "--shutter SHUTTER-SPEED" "search on SHUTTER-SPEED"]
["-f" "--aperture APERTURE" "search on APERTURE"]
["-y" "--year YEAR" "search on YEAR"]
["-m" "--month MONTH" "search on MONTH"]
["-M" "--model MODEL" "search by camera model"]
["-p" "--project PROJECT" "search photos in PROJECT"]
["-k" "--keyword KEYWORD" "search for KEYWORD"]])
(defn print-count [pics]
(println (count pics)))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(let [{:keys [options arguments errors summary]} (parse-opts args cli-options)
output-function (if (:count options) print-count open)]
(-> all-images
(ifeq :ISO-Speed-Ratings (:iso options))
(ifeq :Year (:year options))
(ifeq :Month (:month options))
(ifin :Project (:project options))
(ifeq :F-Number (:aperture options))
(ifin :Keywords (:keyword options))
(ifin :Model (:model options))
(output-function))))
I run lein bin, then run the executable it produces and get something like this:
(master) image-search: image-search -i 640 -c
WARNING: or already refers to: #'clojure.core/or in namespace: image-search.core, being replaced by: #'image-search.core/or
WARNING: and already refers to: #'clojure.core/and in namespace: image-search.core, being replaced by: #'image-search.core/and
Note that the 2 warning are referring to functions I don't use in the command line version. I am not even including them. They are not in the list given to refer when I :require the library. They are, however, important to the library when I'm using it from the repl or cider. So I really don't want to rename them.
You can remove the warning by adding following to your (ns image-search.core) declaration:
(ns image-search.core
(:refer-clojure :exclude [or and])
With that change you can still use the original Clojure's or and and by fully qualifying them:
`clojure.core/or`
`clojure.core/and`
Without that change the compiler warns you that you are overriding your namespace binding of or and and as they are by default bound to functions from clojure.core/or and clojure/and vars.
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/"]}}
I am trying to :gen-class a Servlet
This is my code:
(ns test.test
(:import (java.io PrintWriter) (javax.servlet.http HttpServlet))
(:gen-class :name test.TestServlet :extends javax.servlet.http.HttpServlet))
(defn -doGet[request response]
(let [wrtr (.getWriter response)]
(.println wrtr "hello from clojure")))
it can't be compiled by Lein
it said Exception in thread "main" java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet (Test.clj:1)
I already modified the :library-path in Lein as ":library-path "/home/long/workspaces/spring/LongHDi/war/WEB-INF/lib" but it didn't work.
Do you have any idea why?
I am trying to work with Google App Engine. The servlet class I want to extend is already in the lib folder I specified.
Which version of lein are you using ?
I downloaded jetty from here, and lein version1 worked for me with project.clj
(defproject st2 "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:library-path "/Users/Niko/Downloads/jetty-hightide-8.1.7.v20120910/lib"
:aot [st2.core]
:dependencies [[org.clojure/clojure "1.3.0"]])
with st2.core the same as your code:
(ns st2.core
(:import (java.io PrintWriter) (javax.servlet.http HttpServlet))
(:gen-class :name test.TestServlet :extends javax.servlet.http.HttpServlet))
(defn -doGet[request response]
(let [wrtr (.getWriter response)]
(.println wrtr "hello from clojure")))
If you are using lein2, :library-path is not supported so I suspect you would have to add the dependencies "a-la-maven" and add them to your project dependencies.
I have a clojure application called mr1; its project.clj is located in ./mr1/, and mr1.clj is located in ./mr1/src.
I cannot figure out how to structure the directories, namespace, and project.clj file correctly, so I can build a binary using cake bin.
Enclosed are my current project.clj and the head and tail of mr1.clj.
src/mr1.clj loads fine into cake repl and executes. cake bin produces an mr1 file that contains no -main.
(defproject mr1 "0.0.1-SNAPSHOT"
:description "TODO: add summary of your project"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.clojure/tools.cli "0.1.0"]]
:main mr1)
(ns mr1
(:use [clojure.string :only [split]]
[clojure.string :only [join]]))
.
.
.
(defn -main
[& args]
(do
(reset! grid-dim (prompt-for-grid-dim))
(reset! mr1-pos (prompt-for-rover-pos 1))
(let [moves (prompt-for-rover-moves)]
(execute-each-move moves #mr1-pos))
(reset! mr2-pos (prompt-for-rover-pos 2))
(let [moves (prompt-for-rover-moves)]
(execute-each-move moves #mr2-pos))
)
)
As I mentioned in answer to your first question, you have to enable AOT-compilation of your namespace. I've copied that answer here in case if someone would have same problem.
As sample.project.clj file says, :main key should have as an assigned value a namespace which contains -main function. So you should have such function
(defn -main [& args]
(do-things-you-want-to-do-on-program-start))
in your mr1.clj. Also AFAIR if you want to use your program as a standalone jar you have to have this namespace gen-classed. By this I mean that you have to:
Include :gen-class option in your namespace definition like this:
(ns mr1
(:gen-class)
...other options...)
Make the namespace AOT-compiled (AOT stands for Ahead Of Time). To do this you need to specify your namespace in the list of AOT-compiled namespaces in project.clj:
(defproject mr1 "0.0.1-SNAPSHOT"
...other definitions...
:aot [mr1]
:main mr1)
After you've done this, you can use cake to generate executable jar for you.