1 What's Leiningen
Leiningen is a tool for automating Clojure projects without setting your hair on fire.
If you come from the Java world, Leiningen is "Maven meets Ant without the pain". For Ruby and Python folks, Leiningen combines RubyGems/Bundler/Rake and pip/Fabric in a single tool.
Clojure项目自动化创建, 编译, 打包和部署的工具集.
具体可以提供如下功能,
It manages various project-related tasks, and can:
- create new projects
- manage dependencies for your project
- run tests
- run a REPL (without you having to worry about adding dependencies to the classpath)
- compile Java sources (if any)
- run the project (if the project is an app)
- generate a maven-style "pom" file for the project
- compile and package projects for deployment
- publish libraries to maven artifact repositories such as Clojars
- run custom automation tasks written in Clojure (leiningen plug-ins)
2 Basic Usage
help, 帮助手册
Use lein help
to see a complete list. lein help $TASK
shows the usage for a specific task.
new, 创建新项目
$ lein new [TEMPLATE] NAME # generate a new project skeleton
run, 执行, 以-main函数会入口函数
$ lein run -m my.namespace # run the -main function of a namespace
test, 执行test namespace目录里面的所有testcase
$ lein test [TESTS] # run the tests in the TESTS namespaces, or all tests
repl, launch repl
$ lein repl # launch an interactive REPL session
uberjar, 将project打包成jar
$ lein uberjar # package the project and dependencies as standalone jar
串联执行
You can also chain tasks together in a single command by using the do
task with comma-separated tasks:
$ lein do clean, test foo.test-core, jar
在project directory内执行命令
Most tasks need to be run from somewhere inside a project directory to work, but some (new
, help
, search
, version
, andrepl
) may run from anywhere.
3 Create Project
使用lein new来创建新的clojure项目, 会在当前目录创建项目目录my-stuff.
$ lein new my-stuff
Generating a project called my-stuff based on the 'default' template.
$ cd my-stuff
$ find .
.
./.gitignore
./doc
./doc/intro.md
./project.clj
./README.md
./src
./src/my_stuff
./src/my_stuff/core.clj
./test
./test/my_stuff
./test/my_stuff/core_test.clj
Namespace Mapping Convention
创建project时, 会自动创建namespace
my-stuff.core
my-stuff.core
instead of justmy-stuff
since single-segment namespaces are discouraged in Clojure.
Namespace在FP里面比较重要, 比在OO里面重要, 因为对于OO还有class, 其实就是做了一层的namespace封装, 有效的避免冲突
但是对于FP, 直接就是function, 如果不加合理的namespace, 那就太容易冲突了
所以这儿使用my-stuff.core, 在project内部需要namespace的分层, 除了core, 肯定应该有其他的子namespace……
4 Project.clj, Configuration
首先可以看出data=code, 配置文件一样可以用clojure文件来写, 只要实现macro, 配置文件直接就可以run, 很牛!
Default自动生成的project.clj的模板如下:
(defproject my-stuff "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"]])
一个简单的例子,
(defproject myproject "0.5.0-SNAPSHOT" :description "A project for doing things." :license "Eclipse Public License 1.0" :url "http://github.com/technomancy/myproject" :dependencies [[org.clojure/clojure "1.4.0"]] :plugins [[lein-ring "0.4.5"]])
实际使用的会比这个复杂很多, 参考下面的sample.project.clj
See the sample.project.clj file (also available via lein help sample
) for a detailed listing of configuration options.
通过profiles可以根据不同的情况设定不同的configuration
The project.clj
file can be customized further with the use of profiles.
Dependencies
Overview
Clojure is a hosted language and Clojure libraries are distributed the same way as in other JVM languages: as JARs.
JARs (.jar
files) are basically just .zip
files with a little extra JVM-specific metadata. They usually contain .class
files (JVM bytecode) and .clj
source files, but they can also contain other things like config files, JavaScript files or text files with static data.
必须要指明, project和其他包的依赖关系, 这样leiningen才能自动的下载配置相关包, 以保证project可以被正常的编译和运行.
Artifact IDs, Groups, and Versions
Artifact IDs, 包的名字, 如clojure, hibernate
Groups, 一般是reversed域名, 如com.cedarsoft.utils.legacy
Versions, 版本号, 1.3.4 [com.cedarsoft.utils.legacy/hibernate "1.3.4"]
Clojure libraries often use the same group-id and artifact-id (as with clj-http), in which case you can omit the group-id.
[clj-http "0.5.5"]
Snapshot Versions
Sometimes versions will end in "-SNAPSHOT". This means that it is not an official release but a development build.
所以自动生成的project.clj的版本是带"-SNAPSHOT": my-stuff "0.1.0-SNAPSHOT"
当然尽量不要在dependency里面加snapshot的版本, 除了你知道你在干吗, 并确实需要这样...
另外的原因是对于snapshot version, leiningen每天都会去更新最新版, 所以有效率问题...
Adding a snapshot dependency to your project will cause Leiningen to actively go seek out the latest version of the dependency daily (whereas normal release versions are cached in the local repository) so if you have a lot of snapshots it will slow things down.
Artifact IDs, Groups和Namespace的关系
Note that some libraries make their group-id and artifact-id correspond with the namespace they provide inside the jar, but this is just a convention. There is no guarantee they will match up at all.
Repositories
Dependencies are stored in a maven repository (类似PyPi)
There are several popular open source repositories. Leiningen by default will use two of them: clojars.org and Maven Central.
leiningen需要根据配置的dependency, 来自动的找到这些包, 去哪儿找, 默认是clojars.org and Maven Central
可以自行增加third-party repositories
You can add third-party repositories by setting the :repositories
key in project.clj. See the sample.project.clj.
5 Running Code
Lein REPL
The REPL is an interactive prompt where you can enter arbitrary code to run in the context of your project.
lein repl和一般的repl相比, 最大的优势是, project context awareness
对于一般的repl, 能够被require的包都必须要在classpath底下能够找到, 如果没有, 你需要自己去download并配置到classpath里面去
而lein会自动的管理dependency, 只需要在project.clj里面把需要的denpendency写清楚, lein会自动完成download和classpath配置
所以只需要在project目录下执行lein repl, 即可以require当前project namespace的所有代码, 以及所有denpendency的库
这个相当方便, 尤其在测试的时候
需要注意的是, lein repl其实是可以在任何目录下执行的, 但是如果不是project目录, 那就和普通repl没有任何区别.
Lein REPL还有命令提示, 主要有如下命令
函数执行
user=> (require 'my-stuff.core)
nil
user=> (my-stuff.core/foo “me”)
me Hello, World!
nil
如果在denpendency中加了[clj-http "0.5.5"]
user=> (require '[clj-http.client :as http])
nil
user=> (def response (http/get "http://leiningen.org"))
#'user/response
帮助文档, doc, javadoc, clojuredocs
doc, find-doc显示帮助文档
javadoc, 显示java的帮助文档
clojuredocs
offers more thorough examples from the ClojureDocs site.
ClojureDocs is a community-powered documentation and examples repository for the Clojure programming language.
clojuredocs这个挺管用, 比如你不知道reduce怎么用
user=> (user/clojuredocs reduce)
就会给出很多例子, 也可以直接去网站查, 更好看些, http://clojuredocs.org/clojure_core/clojure.core/reduce
查看源代码, source
user=> (source my-stuff.core/foo) (defn foo "I don't do a whole lot." [x] (println x "Hello, World!")) nil
Lein Run
如果要使用lein run去运行namespace, 必须先实现-main函数, 因为lein run -m会在namespace里面找-main, 如果没有, 会报错
(defn -main
"I don't do a whole lot."
[& args]
(println "Hello, World!"))
$ lein run -m my-stuff.core
Hello, World!
Providing an alternate -m
argument will tell Leiningen to look for the -main
function in another namespace.
Setting a default :main
in project.clj
lets you omit -m
.
比如加上如下配置,
:main my-stuff.core
$ lein run
Hello, World!
6 Tests
We haven't written any tests yet, but we can run the failing tests included from the project template:
$ lein test
lein test my.test.stuff
FAIL in (a-test) (stuff.clj:7)
FIXME, I fail.
expected: (= 0 1)
actual: (not (= 0 1))
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Once we fill it in the test suite will become more useful. Sometimes if you've got a large test suite you'll want to run just one or two namespaces at a time; lein test my.test.stuff
will do that. You also might want to break up your tests using test selectors; see lein help test
for more details.
7 Make Jar
Generally speaking, there are three different goals that are typical of Leiningen projects:
- An application you can distribute to end-users
- A library for other Clojure projects to consume
- A server-side application
Uberjar
The simplest thing to do is to distribute an uberjar. This is a single standalone executable jar file most suitable for giving to nontechnical users.
最简单的方式, 直接打包成可执行的jar
1. 在project.clj加上:main, 配置-main存在的namespace
2. 在namespace声明中, 加上:gen-class. 不加会报错(Error: Could not find or load main class my_stuff.core)
(ns my-stuff.core (:gen-class))
$ lein uberjar Compiling my.stuff Compilation succeeded. Created /home/phil/src/leiningen/my-stuff/target/my-stuff-0.1.0-SNAPSHOT.jar Including my-stuff-0.1.0-SNAPSHOT.jar Including clj-http-0.4.1.jar Including clojure-1.3.0.jar Including lucene-core-3.0.2.jar Created /home/phil/src/leiningen/my-stuff/target/my-stuff-0.1.0-SNAPSHOT-standalone.jar
4. 象普通jar一样的执行
$ java -jar my-stuff-0.1.0-standalone.jar Hello world.
Welcome to my project! These are your args: (Hello world.)
Framework (Uber)jars
看下面的例子, 对于hadoop这样framework的包, 无法也无需打包到jar里面
因为必须实际的运行环境里面有hadoop集群否则, 根本运行不了
所以使用:provided, 表示需要, 但是不包含在jar里面, 需要使用者保证运行环境符合.
(project example.hadoop "0.1.0" ... :profiles {:provided {:dependencies [[org.apache.hadoop/hadoop-core "0.20.2-dev"]]}} :main example.hadoop)
Server-side Projects
There are many ways to get your project deployed as a server-side application. Aside from the obvious uberjar approach, simple programs can be packaged up as tarballs with accompanied shell scripts using the lein-tar plugin and then deployed using pallet, chef, or other mechanisms.
Publishing Libraries
If your project is a library and you would like others to be able to use it as a dependency in their projects, you will need to get it into a public repository.
可以将生成的jar发布到public库里面
$ lein deploy clojars
8 Getting Started with Eclipse and Counterclockwise
Eclipse的leiningen插件, 使用更方便
http://dev.clojure.org/display/doc/Getting+Started+with+Eclipse+and+Counterclockwise
ctrl+alt+s, 执行当前cljr文件, 并启动repl
本文章摘自博客园,原文发布日期:2013-03-07