Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit 14e3059bc4910b32a4f9e44543d5887fd11778d8 0 parents
@mtyaka mtyaka authored
Showing with 277 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +122 −0
  3. +2 −0  project.clj
  4. +149 −0 src/leiningen/oneoff.clj
4 .gitignore
@@ -0,0 +1,4 @@
@@ -0,0 +1,122 @@
+# lein-oneoff
+Dealing with dependencies and the classpath can be a
+pain. [Leiningen]( takes most
+of the pain away, but creating a new leiningen project for a simple
+one-off script may sometimes feel like overkill. This is where
+[lein-oneoff]( comes in.
+With the help of lein-oneoff you can open a file, declare
+dependencies at the top and write the rest of the code as
+usually. lein-oneoff will let you run the file, open a repl or start a swank
+server while taking care of fetching dependencies and constructing the
+classpath automatically.
+## Usage
+lein-oneoff scripts usually consist of a single file. Dependencies
+should be stated at the top using the `defdeps` form. Here's an example:
+ (defdeps
+ [[org.clojure/clojure "1.2.0"]
+ [compojure "0.5.2"]
+ [ring/ring-jetty-adapter "0.3.3"]])
+ (ns example
+ (:use [compojure.core]
+ [ring.adapter.jetty :only [run-jetty]]))
+ (defroutes routes
+ (GET "/" [] "Hello world!"))
+ (def server
+ (run-jetty routes {:port 8080 :join? false}))
+Save this file as `example.clj`, then run it with:
+ $ lein oneoff example.clj
+This command will check the specified dependencies and install them
+into the local maven repository (`~/.m2/repository`) unless already
+installed, and then run `example.clj` with the necessary dependencies
+in the classpath. Note that the dependencies are referenced directly
+from the local maven repository.
+### The defdeps form
+The `defdeps` form must be the first form in the file. It has the following
+ (defdeps dependencies additional-entries?)
+where dependencies should be specified as a vector using the same
+syntax as inside regular leiningen `defproject` form under the
+`:dependencies` key. The second argument is an optional map of
+additional standard `defproject` entries. Please note that not
+all of the available leinigen options make sense for a one-off script
+and might not work correctly.
+One of the entries that can be useful is the `:repositories` entry. Here's
+an example:
+ (defdeps
+ [[org.clojure/clojure "1.3.0-alpha3"]
+ [org.apache.pivot/pivot-web "1.5.2"]]
+ {:repositories
+ {"apache" ""}})
+The `defdeps` form may be omitted in which case the only assumed
+dependency is `org.clojure/clojure` of the same version as your leiningen
+installation is using.
+### repl
+To start a repl in the context of a one-off script, use the `--repl`
+command (or its shorter equivalent, `-r`):
+ $ lein oneoff --repl example.clj
+ $ lein oneoff -r example.clj
+### swank
+A swank server can be started with the `--swank` (or `-s`)
+ $ lein oneoff --swank example.clj
+ $ lein oneoff -s example.clj
+Please note that for the swank command to work, you'll need to have
+`swank-clojure` installed as a global leiningen plugin. At the moment,
+only `swank-clojure 1.3.0-SNAPSHOT` is supported.
+### classpath
+lein-oneoff offers an equivalent to leiningen's built-in `classpath`
+task which prints the project's classpath for one-off scripts:
+ $ lein oneoff --classpath example.clj
+ $ lein oneoff -c example.clj
+## Installation
+This plugin should be installed as a user-level leiningen plugin. Using
+leiningen 1.3.1, the easiest way to get going is to drop the
+lein-oneoff jar into the `$HOME/.lein/plugins` folder. Leiningen 1.4.0
+will come with a special task for installing user-level plugins.
+lein-oneoff has been tested with leiningen 1.3.1 and 1.4.0-RC1.
+### Windows notes
+Windows lein.bat script that comes with leiningen 1.3.1 doesn't
+support user-level leiningen plugins. This was fixed on the master
+branch, so if you're using 1.3.1 on windows you might want to replace your
+lein.bat file with [the latest
+and then change the version in the second line to 1.3.1.
+## License
+Copyright (C) 2010 Matjaz Gregoric
+Distributed under the Eclipse Public License, the same as Clojure.
2  project.clj
@@ -0,0 +1,2 @@
+(defproject lein-oneoff "0.0.1"
+ :description "Dependency management for one-off scripts.")
149 src/leiningen/oneoff.clj
@@ -0,0 +1,149 @@
+(ns leiningen.oneoff
+ (:use [robert.hooke :only [add-hook]]
+ [leiningen.core :only [abort]])
+ (:require
+ [lancet]
+ [clojure.main]
+ [leiningen compile classpath repl deps])
+ (:import
+ (require 'leiningen.swank)
+ (catch e))
+(def lein-swank-ns (find-ns 'leiningen.swank))
+(def swank-form-var
+ (when lein-swank-ns (ns-resolve lein-swank-ns 'swank-form)))
+(def default-deps
+ `[[org.clojure/clojure ~(clojure-version)]])
+(def defdeps-defmacro-form
+ `(defmacro ~'defdeps [& args#]))
+(defn deps-classpath
+ "Resolves and installs dependencies to the local maven repository.
+Returns a sequence of paths referencing jars in the repository."
+ [project]
+ (let [deps-task (leiningen.deps/make-deps-task project :dependencies)
+ _ (.execute deps-task)
+ fileset (.getReference lancet/ant-project
+ (.getFilesetId deps-task))
+ dir-scanner (.getDirectoryScanner fileset lancet/ant-project)
+ base-dir (.getBasedir dir-scanner)]
+ (for [fpath (.getIncludedFiles dir-scanner)]
+ (.getCanonicalPath (File. base-dir fpath)))))
+(defn get-oneoff-classpath
+ "Returns a sequence of paths that constitute the full classpath
+for a one-off project."
+ [project]
+ (concat [(:root project)]
+ (leiningen.classpath/user-plugins)
+ (deps-classpath project)))
+(defn oneoff-deps-hook [deps project]
+ (when-not (:oneoff project) (deps project)))
+(defn oneoff-get-classpath-hook [get-classpath project]
+ (if (:oneoff project)
+ (get-oneoff-classpath project)
+ (get-classpath project)))
+(defn oneoff-eval-in-project-hook
+ [eval-in-project project form & [handler skip-auto-compile init]]
+ (let [skip-auto-compile (or (:oneoff project) skip-auto-compile)]
+ (eval-in-project project form handler skip-auto-compile init)))
+(defn oneoff-repl-server-hook [repl-server project host port]
+ (let [server-form (repl-server project host port)]
+ (if (:oneoff project)
+ `(do ~defdeps-defmacro-form ~server-form)
+ server-form)))
+(defn oneoff-swank-form-hook [swank-form project port host opts]
+ (let [server-form (swank-form project port host opts)]
+ (if (:oneoff project)
+ `(do ~defdeps-defmacro-form ~server-form)
+ server-form)))
+(add-hook #'leiningen.deps/deps oneoff-deps-hook)
+(add-hook #'leiningen.compile/eval-in-project oneoff-eval-in-project-hook)
+(add-hook #'leiningen.classpath/get-classpath oneoff-get-classpath-hook)
+(add-hook #'leiningen.repl/repl-server oneoff-repl-server-hook)
+(when swank-form-var
+ (add-hook swank-form-var oneoff-swank-form-hook))
+(defn parse-defdeps [script]
+ (let [contents (slurp script)
+ forms (load-string (str "(quote [" contents "])"))
+ form (first forms)]
+ (if (= (first form) 'defdeps)
+ [(nth form 1) (nth form 2 {})]
+ [default-deps {}])))
+(defn oneoff-project [script]
+ (let [dir (System/getProperty "user.dir")
+ [deps opts] (parse-defdeps script)]
+ (merge
+ {:oneoff true
+ :name "A oneoff project"
+ :version "1.0.0"
+ :dependencies deps
+ :root dir
+ :compile-path (str dir "/classes")
+ :library-path (str dir "/lib")}
+ opts)))
+(defn print-usage []
+ (abort "Usage: lein oneoff <command> <file>
+ <command> can be one of: --exec, --repl, --classpath, --swank.
+ Short forms (-e, -r, -c, -s) may be used instead.
+ If <command> is omitted, --exec is assumed."))
+(defn execute-script [script]
+ (let [project (oneoff-project script)
+ form `(do
+ ~defdeps-defmacro-form
+ (clojure.main/load-script ~script))]
+ (leiningen.compile/eval-in-project project form)))
+(defn start-repl-server [script]
+ (leiningen.repl/repl (oneoff-project script)))
+(defn start-swank-server [script]
+ (if lein-swank-ns
+ (if swank-form-var
+ (let [swank-fn (ns-resolve lein-swank-ns 'swank)]
+ (swank-fn (oneoff-project script)))
+ (abort "The oneoff swank task only works with
+swank-clojure 1.3.0-SNAPSHOT or newer."))
+ (abort "You'll need to install swank-clojure as a user plugin
+for this task to work.")))
+(defn print-classpath [script]
+ (leiningen.classpath/classpath (oneoff-project script)))
+(defn oneoff
+ "Handles dependencies and execution of one-off scripts when creating a
+proper leiningen project feels like overkill.
+Syntax: lein oneoff <command> <file>
+ <command> can be one of: --exec, --repl, --classpath, --swank.
+ Short forms (-e, -r, -c, -s) may be used instead.
+ If <command> is omitted, --exec is assumed.
+See for more information."
+ ([cmd script]
+ (case cmd
+ ("--exec" "-e") (execute-script script)
+ ("--repl" "-r") (start-repl-server script)
+ ("--classpath" "-c") (print-classpath script)
+ ("--swank" "-s") (start-swank-server script)
+ (print-usage)))
+ ([script]
+ (oneoff "--exec" script))
+ ([]
+ (print-usage)))
Please sign in to comment.
Something went wrong with that request. Please try again.