“Leiningen教程中文版”的版本间的差异

来自Dennis的知识库
跳转到: 导航搜索
创建一个项目
 
(未显示1个用户的49个中间版本)
第16行: 第16行:
 
* 运行测试
 
* 运行测试
 
* 运行一个REPL(你不需要再关心如何将依赖加入classpath)
 
* 运行一个REPL(你不需要再关心如何将依赖加入classpath)
* 编译java源码(如果有的话)
+
* 编译Java源码(如果有的话)
 
* 运行项目(如果项目是一个app的话)
 
* 运行项目(如果项目是一个app的话)
* 为项目产生一个maven风格的pom文件
+
* 为项目产生一个Maven风格的pom文件
* 为部署编译和打包项目
+
* 为了部署,编译和打包项目
* 发行类库到maven仓库,例如[http://clojars.org Clojars]  
+
* 发布类库到Maven仓库,例如[http://clojars.org Clojars]  
* 运行clojure编写的自定义的自动化任务(称为leiningen插件)
+
* 运行Clojure编写的自定义的自动化任务(称为Leiningen插件)
  
如果你来自Java世界,Leiningen就是Maven和Ant的无痛结合。如果你是Ruby或者Python世界的人们,则Leiningen
+
如果你来自Java世界,Leiningen就是Maven和Ant的无痛结合。如果你是Ruby或者Python世界的人们,则Leiningen就是那个组合了RubyGems/Bundler/Rake和pip/Fabric等功能的一个单一工具。
就是那个组合了RubyGems/Bundler/Rake和pip/Fabric等功能的一个单一工具。
+
  
 
== 本教程覆盖范围 ==
 
== 本教程覆盖范围 ==
第30行: 第29行:
 
本教程主要覆盖了项目结构、依赖管理、运行测试、REPL以及跟部署相关的话题。
 
本教程主要覆盖了项目结构、依赖管理、运行测试、REPL以及跟部署相关的话题。
  
对于那些从来没有接触过Ant或者Maven的JVM初哥,(忠告是):不要惊慌失措。Leiningen也是为了你们而设计。这个教程
+
对于那些从来没有接触过Ant或者Maven的JVM初哥,(忠告是):不要惊慌失措。Leiningen也是为了你们而设计。这个教程会帮助你快速开始,并解释Leiningen如何处理项目自动化以及JVM的依赖管理。
会帮助你快速开始,并解释Leiningen如何处理项目自动化以及JVM的依赖管理。
+
  
 
== 获得帮助 ==
 
== 获得帮助 ==
第41行: 第39行:
  
 
Leiningen是跟''项目''打交道。一个项目就是一个包含了一组Clojure(可能也有Java)源码文件的目录,同时附带一些元信息。
 
Leiningen是跟''项目''打交道。一个项目就是一个包含了一组Clojure(可能也有Java)源码文件的目录,同时附带一些元信息。
元信息存在根目录的一个称为<code>project.clj</code>(默认名称)的文件。<code>project.clj</code>就是你用来告诉Leiningen
+
元信息存在根目录的一个称为<code>project.clj</code>(默认名称)的文件。<code>project.clj</code>就是你用来告诉Leiningen项目是什么样的,它包括:
项目是什么样的,包括:
+
  
 
*项目名称
 
*项目名称
第49行: 第46行:
 
*使用的Clojure版本是什么
 
*使用的Clojure版本是什么
 
*哪里找到(项目)源码
 
*哪里找到(项目)源码
*App的main namespace是什么
+
*App的主namespace是什么(译注:就跟Java App里会有一个main的public class一样)
  
 
等等。
 
等等。
第79行: 第76行:
 
             `-- core_test.clj
 
             `-- core_test.clj
  
=== 目录层次 ===
+
=== 目录布局 ===
 
+
Here we've got your project's README, a `src/` directory containing the
+
code, a `test/` directory, and a `project.clj` file which describes your
+
project to Leiningen. The `src/my_stuff/core.clj` file corresponds to
+
the `my-stuff.core` namespace.
+
  
 
到这一步,我们得到了项目的README,一个包含了代码的<code>src/</code>目录,一个
 
到这一步,我们得到了项目的README,一个包含了代码的<code>src/</code>目录,一个
 
<code>test/</code>目录,以及一个向Leiningen描述项目的<code>project.clj</code>文件。
 
<code>test/</code>目录,以及一个向Leiningen描述项目的<code>project.clj</code>文件。
<code>src/my_stuff/core.clj</code>文件对应于<code>my-stuff.core</code>命名空间。
+
<code>src/my_stuff/core.clj</code>文件对应<code>my-stuff.core</code>命名空间。
  
 
+
尽管大多数纯Clojure项目都不需要定制目录布局,但是Leiningen允许你这样做。
Even though most pure Clojure projects never need to customize directory layout,
+
it is possible with Leiningen.
+
 
+
尽管大多数纯Clojure项目都不需要定制目录层次,但是Leiningen允许你这样做。
+
  
 
=== 文件名-到-命名空间的映射惯例 ===
 
=== 文件名-到-命名空间的映射惯例 ===
 
Note that we use `my-stuff.core` instead of just `my-stuff` since
 
single-segment namespaces are discouraged in Clojure. Also note that
 
namespaces with dashes in the name will have the corresponding file
 
named with underscores instead since the JVM has trouble loading files
 
with dashes in the name. The intricacies of namespaces are a common
 
source of confusion for newcomers, and while they are mostly outside
 
the scope of this tutorial, you can
 
[read up on them elsewhere](http://blog.8thlight.com/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html).
 
  
 
注意到我们使用<code>my-stuff.core</code>作为命名空间,而不只是<code>my-sutff</code>,
 
注意到我们使用<code>my-stuff.core</code>作为命名空间,而不只是<code>my-sutff</code>,
这是因为Clojure不鼓励只有一段(single-segment,译者注:意思是没有"."连接的的单一名称)的命名空间。
+
这是因为Clojure不鼓励使用只有一段(single-segment,译者注:意思是没有"."连接的的单一名称)的命名空间。
另外我们也注意到含有波折号(也就是减号)的命名空间,会被映射到波折号被下划线替换的文件,这是因为
+
另外我们也注意到含有波折号(也就是减号)的命名空间,会被映射到波折号被下划线替换的文件,这是因为JVM加载含有波折号的文件会遇到问题。
JVM加载含有波折号的文件会遇到问题。命名空间的错综复杂是新手通常感到困扰的一个来源,虽然我们已经偏离本
+
 
教程的范围,你可以在[http://blog.8thlight.com/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html 别处阅读到它们]。
+
命名空间的错综复杂是新手感到困扰的一个常见来源,虽然我们已经偏离本教程的范围,但是你可以在[http://blog.8thlight.com/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html 别处阅读到它们]。
  
 
== project.clj ==
 
== project.clj ==
  
A default project.clj file will start off looking something like the this:
 
 
一个默认的project.clj文件初始看起来类似这样:
 
一个默认的project.clj文件初始看起来类似这样:
  
第125行: 第103行:
 
       :dependencies [[org.clojure/clojure "1.4.0"]])
 
       :dependencies [[org.clojure/clojure "1.4.0"]])
  
If you don't fill in the `:description` with a short sentence, your
+
如果你没有为<code>description</code>填写一个简短的(描述)句子,你的项目会很难在搜索结果里找到,所以请填写一下。也请顺便填写下<code>:url</code>(译注:项目地址URL)。
project will be harder to find in search results, so start there. Be
+
sure to fix the `:url` as well. At some point you'll need to flesh out
+
the README too, but for now let's skip ahead to setting
+
`:dependencies`. Note that Clojure is just another dependency here.
+
Unlike most languages, it's easy to swap out any version of Clojure.
+
  
如果你没有为<code>description</code>填写一个简短的(描述)句子,你的项目
 
会难于被搜索,所以请填写一下。也请填写下<code>:url</code>(译注:项目地址URL)。
 
 
有时候,你也需要充实下README文件,但是现在让我们略过(这一步)去设置下<code>:dependencies</code>。
 
有时候,你也需要充实下README文件,但是现在让我们略过(这一步)去设置下<code>:dependencies</code>。
注意到,不像其他语言那样,Clojure在这里也只是另一个依赖罢了,你可以很容易地转换Clojure版本。
 
  
 +
注意到,不像其他语言那样,Clojure在这里也只是另一个依赖罢了,你可以很容易地切换Clojure版本。
  
 
== 依赖 ==
 
== 依赖 ==
  
 
=== 概览 ===
 
=== 概览 ===
 
Clojure is a hosted language and Clojure libraries are distributed the same
 
way as in other JVM languages: as JARs.
 
  
 
Clojure是一门寄宿语言(译注:寄宿于JVM之上),并且Clojure的类库也像其他JVM语言那样作为JAR发布。
 
Clojure是一门寄宿语言(译注:寄宿于JVM之上),并且Clojure的类库也像其他JVM语言那样作为JAR发布。
  
JARs (`.jar` files) are basically just `.zip` files with a little extra JVM-specific
+
JAR(<code>jar</code>文件)基本上仅仅是是一些附加了JVM特有元信息的<code>.zip</code>文件。
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.
+
 
+
JAR(<code>jar</code>文件)仅仅是是一些附加了JVM特有元信息的<code>.zip</code>文件。
+
 
它们通常包含有<code>.class</code>文件(JVM字节码),和<code>.clj</code>源码文件,
 
它们通常包含有<code>.class</code>文件(JVM字节码),和<code>.clj</code>源码文件,
 
但是它们也可以包含其他一些东西,例如配置文件、JavaScript文件或者含有静态数据的文本文件。
 
但是它们也可以包含其他一些东西,例如配置文件、JavaScript文件或者含有静态数据的文本文件。
  
  
已发布的JVM类库拥有''identifiers''(artifact group, artifact id)和''versions''
+
已发布的JVM类库拥有''identifiers''标识符(artifact group, artifact id)和''versions''版本号。
  
== Artifact IDs, Groups和Versions ==
+
=== Artifact IDs, Groups和Versions ===
 
+
You can [search Clojars](http://clojars.org/search?q=clj-http) using its
+
web interface. On the page for `clj-http` it shows this:
+
  
 
你可以使用web接口[http://clojars.org/search?q=clj-http 搜索Clojars],搜索clj-http的结果页显示为:
 
你可以使用web接口[http://clojars.org/search?q=clj-http 搜索Clojars],搜索clj-http的结果页显示为:
第168行: 第128行:
 
     [clj-http "0.5.5"]
 
     [clj-http "0.5.5"]
  
There are two different ways of specifying a dependency on the latest
+
有两种不同的途径来设置依赖<code>clj-http</code>类库的最新版本,一种是类似于上面所展示的Leiningen格式,另一种是Maven格式。我们现在略过Maven的方式,但是你需要为使用)来自[http://search.maven.org Maven central]的Java类库学习一下(maven方式)。你可以直接拷贝Leiningen版本到<code>project.clj</code>里的<code>:dependencies</code> vector。
stable version of the `clj-http` library, one in Leiningen format
+
shown above and one in Maven format. We'll skip the Maven one for now,
+
though you'll need to learn to read it for Java libraries from
+
[Central](http://search.maven.org). You can copy the Leiningen version
+
directly into the `:dependencies` vector in `project.clj`.
+
  
有两种途径来设置依赖<code>clj-http</code>类库的最新版本,一种是类似于上面所展示
+
在vector里,"clj-http"被称为"artifact id"(译者注:有maven经验的童鞋会很熟悉)。"0.5.5"就是版本号。一些类库还会有"group id",显示成这样:
的Leiningen格式,另一种是Maven格式。我们现在略过Maven的方式,但是你需要为学习(使用)
+
来自[http://search.maven.org Maven central]的Java类库读一下。你可以直接拷贝Leiningen版本
+
到<code>project.clj</code>里的<code>:dependencies</code> vector。
+
 
+
Within the vector, "clj-http" is referred to as the "artifact id".
+
"0.5.5" is the version. Some libraries will also have "group ids",
+
which are displayed like this:
+
 
+
在vector里,"clj-http"被称为"artifact id"(译者注:还记得Maven吗)。"0.5.5"就是版本号。
+
一些类库还会有"group id",显示成这样:
+
  
 
     [com.cedarsoft.utils.legacy/hibernate "1.3.4"]
 
     [com.cedarsoft.utils.legacy/hibernate "1.3.4"]
  
The group-id is the part before the slash. Especially for Java
+
group-id就是斜杠之前的部分。特别是对于Java类库来说,它通常是倒序的域名。而Clojure类库通常使用相同的group-id和artifact-id(例如clj-http),这种情况下你可以忽略group-id。
libraries, it's often a reversed domain name. Clojure libraries often
+
如果某个类库从属于一个很大的分组(例如<code>ring-jetty-adapter</code>是<code>ring</code>项目的一部分),那么通常所有子项目的group-id保持一样。
use the same group-id and artifact-id (as with clj-http), in which case
+
you can omit the group-id. If there is a library that's part of a
+
larger group (such as `ring-jetty-adapter` being part of the `ring`
+
project), the group-id is often the same across all the sub-projects.
+
 
+
group-id就是斜杠之前的部分。特别是对于Java类库来说,它通常是倒序的域名。而Clojure类库
+
通常使用相同的group-id和artifact-id(例如clj-http),这种情况下你可以忽略group-id。
+
如果某个类库从属于一个很大的分组(例如<code>ring-jetty-adapter</code>是<code>ring</code>项目的一部分),
+
那么通常所有子项目的group-id保持一样。
+
  
 
=== Snapshot版本  ===
 
=== Snapshot版本  ===
  
Sometimes versions will end in "-SNAPSHOT". This means that it is not
+
有时候版本号会以 "-SNAPSHOT"结尾。这意味着这不是一个正式的release版本,而是一个开发阶段构建(版本)。不鼓励依赖snapshot版本,但是有时候这也是必须的,例如你需要bug fix等,但还没有将它们发布。尽管如此,snapshot版本不被保证停留(stick around),所以很重要的一点是非开发阶段的release绝不依赖你无法控制的snapshot版本。添加一个snapshot依赖到你的项目里,会导致Leiningen每天主动查找该依赖的最新版本(而正常的release版本会缓存在本地仓库),所以如果你使用了很多snapshot版本,可能会拖慢Leiningen一些。
an official release but a development build. Relying on snapshot
+
dependencies is discouraged but is sometimes necessary if you need bug
+
fixes, etc. that have not made their way into a release yet. However,
+
snapshot versions are not guaranteed to stick around, so it's
+
important that non-development releases never depend upon snapshot versions that
+
you don't control. 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.
+
  
有时候版本号会以 "-SNAPSHOT"结尾。这意味着这不是一个正式的release版本,而是一个开发构建(版本)。
+
请注意,有一些类库会让它们的group-id和artifact-id对应到它们jar里提供的命名空间,但是这一点仅是惯例。并不保证总是能匹配起来,因此,在你写下<code>:require</code>和<code>:import</code>语句之前请翻阅下类库的文档。
不鼓励依赖snapshot版本,但是有时候也是必须的,例如你需要bug fix等还没有将它们发布。尽管如此,
+
snapshot版本不被保证停留,所以很重要的一点是非开发中的release绝不依赖你无法控制的snapshot版本。
+
添加一个snapshot依赖到你的项目里,会导致Leiningen每天主动查找该依赖的最新版本(而正常的release版本
+
会缓存在本地仓库),所以如果你使用了很多snapshot版本,可能会拖慢一些。
+
 
+
 
+
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, so
+
consult the library's documentation before writing your `:require`
+
and `:import` clauses.
+
 
+
请注意,有一些类库会让它们的group-id和artifact-id对应到它们jar里提供的命名空间,但是这一点
+
仅是惯例。并不保证总是能匹配起来,所以在你写下<code>:require</code>和<code>:import</code>语句之前翻阅
+
下类库的文档。
+
  
 
=== 仓库 ===
 
=== 仓库 ===
  
Dependencies are stored in a *maven repository* (or, more formally, "maven
+
依赖库存储在''maven仓库''(正式的名称是maven artifact仓库,允许一点模棱两可的话也可以简称为仓库)。
artifact repository", or just "repository" if there's little chance of
+
ambiguity).
+
 
+
If you are familiar with Perl's CPAN, Python's Cheeseshop (aka PyPi), Ruby's rubygems.org,
+
or Node.js's NPM, it's the same thing.
+
Leiningen reuses existing JVM repositories infrastructure. There
+
are several popular open source repositories. Leiningen by default will use two of them: [clojars.org](http://clojars.org)
+
and [Maven Central](http://search.maven.org/).
+
  
[Clojars](https://clojars.org/) is the Clojure community's centralized maven repository,
+
如果你熟悉Perl的CPAN,Python的Cheeseshop(也就是PyPi),Ruby的rubygems.org或者Node.js的NPM,它们其实是一类东西。
while [Central](http://search.maven.org/) is for the wider JVM community.
+
Leiningen重用已有的JVM仓库设施。已经有一些很流行的开源仓库。Leiningen默认使用它们中的两个:
 +
[http://clojars.org clojars.org]和[http://search.maven.org/ Maven Central]。
  
You can add third-party repositories by setting the `:repositories` key
+
[http://clojars.org Clojars]是Clojure社区的中心Maven仓库,而[http://search.maven.org/ Central]是更广泛的JVM社区的中心Maven仓库。
in project.clj. See the
+
[sample.project.clj](https://github.com/technomancy/leiningen/blob/rc/sample.project.clj).
+
  
=== 检查依赖 ===
+
你可以添加第三方仓库,通过设置project.clj里的<code>:repositories</code>关键字。查看[https://github.com/technomancy/leiningen/blob/master/sample.project.clj sample.project.clj]示范。
  
Sometimes it is necessary to develop two projects in parallel but it
+
=== checkout依赖 ===
is very inconvenient to run `lein install` and restart your repl all
+
the time to get your changes picked up. Leiningen provides a solution
+
called *checkout dependencies* (or just *checkouts*). To use it,
+
create a directory called `checkouts` in the project root, like so:
+
  
 +
有时候需要并行地开发两个项目,但是为了让变更生效,总是要运行<code>lein install</code>并重启你的REPL,这样做非常不方便。Leiningen提供了一种称为''checkout依赖''(简称''checkouts'')的解决办法。为了使用它,你需要在项目根目录下创建一个名为<code>checkouts</code>的目录,类似:
 
     .
 
     .
 
     |-- project.clj
 
     |-- project.clj
第270行: 第169行:
 
             `-- core_test.clj
 
             `-- core_test.clj
  
Then, under the checkouts directory, create symlinks to projects you need.
+
然后,在checkouts目录里,创建你需要的项目的符号链接:
  
 
     .
 
     .
第285行: 第184行:
 
             `-- core_test.clj
 
             `-- core_test.clj
  
Libraries located under the `checkouts` directory take precedence
+
<code>checkouts</code>目录里的类库比从仓库拉取的类库优先级更高,但是这不是用来替代罗列在项目project.clj里的<code>:dependencies</code>,而只是为了方便使用的一种补充手段。
over libraries pulled from repositories, but this is not a replacement
+
for listing the project in your main project's `:dependencies`; it
+
simply supplements that for convenience.
+
  
The checkouts feature is not transitive: in other words, Leiningen
+
checkouts特性是非传递性的(译注:A依赖B,B依赖C,那么A将依赖C,这称为依赖传递):换句话说,Leiningen不会查找一个checkout依赖库的checkout依赖。
will not find checkout dependencies of a checkout dependency.
+
  
 
== 运行代码 ==
 
== 运行代码 ==
  
Enough setup; let's see some code running. Start with a REPL
+
配置够了,让我们看看代码运行。启动一个REPL(读取-求值-打印循环:read-eval-print loop):
(read-eval-print loop):
+
  
 
     $ lein repl
 
     $ lein repl
第312行: 第206行:
 
               (user/clojuredocs name-here)
 
               (user/clojuredocs name-here)
 
               (user/clojuredocs "ns-here" "name-here")
 
               (user/clojuredocs "ns-here" "name-here")
 
 
     user=>
 
     user=>
  
The REPL is an interactive prompt where you can enter arbitrary code
+
REPL是一种你可以输入任意代码运行在项目上下文里的交互式提示。因为我们已经添加了<code>clj-http</code>依赖到<code>:dependencies</code>里,我们就可以从项目的<code>src/</code>目录的<code>my-stuff.core</code>命名空间里加载它:
to run in the context of your project. Since we've added `clj-http` to
+
 
`:dependencies`, we are able to load it here along with code from the
+
`my-stuff.core` namespace in your project's own `src/` directory:
+
  
 
     user=> (require 'my-stuff.core)
 
     user=> (require 'my-stuff.core)
第332行: 第223行:
 
     (:trace-redirects :status :headers :body)
 
     (:trace-redirects :status :headers :body)
  
The call to `-main` shows both println output ("Hello, World!") and
+
调用<code>-main</code>显示一起打印了输出("Hello, World!")和返回值nil。
the return value (nil) together.
+
  
Built-in documentation is available via `doc`, while `clojuredocs`
+
通过<code>doc</code>可以使用内置文档,并且<code>clojuredocs</code>提供更全面的例子,来自[http://clojuredocs.org ClojureDocs]站点:
offers more thorough examples from the
+
[ClojureDocs](http://clojuredocs.org) site:
+
  
 
     user=> (doc reduce)
 
     user=> (doc reduce)
第352行: 第240行:
 
       applying f to that result and the 2nd item, etc. If coll contains no
 
       applying f to that result and the 2nd item, etc. If coll contains no
 
       items, returns val and f is not called.
 
       items, returns val and f is not called.
 +
  
 
     user=> (user/clojuredocs pprint)
 
     user=> (user/clojuredocs pprint)
第364行: 第253行:
 
       user=> *map*
 
       user=> *map*
 
       {:e {:e 4, :d 3, :c 2, :b 1, :a 0}, :d {:e 4, :d 3, :c 2, :b 1, :a 0}, :c {:e 4, :d 3, :c 2, :b 1, :a 0}, :b {:e 4, :d 3, :c 2, :b 1, :a 0}, :a {:e 4, :d 3, :c 2, :b 1, :a 0}}
 
       {:e {:e 4, :d 3, :c 2, :b 1, :a 0}, :d {:e 4, :d 3, :c 2, :b 1, :a 0}, :c {:e 4, :d 3, :c 2, :b 1, :a 0}, :b {:e 4, :d 3, :c 2, :b 1, :a 0}, :a {:e 4, :d 3, :c 2, :b 1, :a 0}}
 
 
       user=> (clojure.pprint/pprint *map*)
 
       user=> (clojure.pprint/pprint *map*)
 
       {:e {:e 4, :d 3, :c 2, :b 1, :a 0},
 
       {:e {:e 4, :d 3, :c 2, :b 1, :a 0},
第375行: 第263行:
 
     1 example found for clojure.pprint/pprint
 
     1 example found for clojure.pprint/pprint
  
You can even examine the source of functions:
+
你甚至能查看函数的源码:
  
 
     user=> (source my-stuff.core/-main)
 
     user=> (source my-stuff.core/-main)
第382行: 第270行:
 
       [& args]
 
       [& args]
 
       (println "Hello, World!"))
 
       (println "Hello, World!"))
 
 
     user=> ; use control+d to exit
 
     user=> ; use control+d to exit
  
If you already have code in a `-main` function ready to go and don't
+
如果已经编写了<code>-main</code>函数的代码等待执行,你并不需要交互式地键入代码,执行<code>run</code>任务更简单:
need to enter code interactively, the `run` task is simpler:
+
  
 
     $ lein run -m my-stuff.core
 
     $ lein run -m my-stuff.core
 
     Hello, World!
 
     Hello, World!
  
Providing an alternate `-m` argument will tell Leiningen to look for
+
提供替代的<code>-m</code>参数来告诉Leiningen在另一个命名空间里查找<code>-main</code>函数。设置project.clj里默认的<code>:main</code>关键字,可以让你省略<code>-m</code>参数。
the `-main` function in another namespace. Setting a default `:main` in
+
`project.clj` lets you omit `-m`.
+
  
For long-running `lein run` processes, you may wish to save memory
+
对于长时间运行的<code>lein run</code>进程,可能你希望利用高阶的trampoline任务来节省内存,它允许Leiningen的JVM进程在启动项目的JVM之前退出。(译注,可以通过lein help trampoline来查看任务的详细说明):
with the higher-order trampoline task, which allows the Leiningen JVM
+
process to exit before launching your project's JVM.
+
  
 
     $ lein trampoline run -m my-stuff.server 5000
 
     $ lein trampoline run -m my-stuff.server 5000
第403行: 第285行:
 
== 测试 ==
 
== 测试 ==
  
We haven't written any tests yet, but we can run the failing tests
+
我们还没有编写任何测试,但是我们能运行从项目模板里包含过来的失败测试:
included from the project template:
+
  
 
     $ lein test
 
     $ lein test
第418行: 第299行:
 
     1 failures, 0 errors.
 
     1 failures, 0 errors.
  
Once we fill it in the test suite will become more useful. Sometimes
+
一旦我们填充了测试套件,会更有用一些。有时候你有一个很大的测试套件,但是你一次只想运行其中一到两个命名空间,<code>lein test my.test.stuff</code>可以帮你做到。也许你也想要使用测试选择器(test selector)拆散(break up)测试,请看<code>lein help test</code>获取详情。
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.
+
  
Running `lein test` from the command-line is suitable for regression
+
从命令行运行<code>lein test</code>适合回归测试,但是JVM的缓慢启动时间让它和那种需要紧凑反馈循环的测试风格配合不良。在这种情况下,可以通过保持运行一个开启的repl,用来跑
testing, but the slow startup time of the JVM makes it a poor fit for
+
[http://clojuredocs.org/clojure_core/1.3.0/clojure.test/run-tests clojure.test/run-tests],或者使用编辑器整合插件如[https://github.com/technomancy/clojure-mode clojure-test-mode]。
testing styles that require tighter feedback loops. In these cases,
+
either keep a repl open for running the appropriate call to
+
[clojure.test/run-tests](http://clojuredocs.org/clojure_core/1.3.0/clojure.test/run-tests)
+
or look into editor integration such as
+
[clojure-test-mode](https://github.com/technomancy/clojure-mode).
+
  
Keep in mind that while keeping a running process around is convenient,
+
请谨记在心,虽然保持一个运行的进程是很方便,但是这很容易让进程进入这样一种状态:没有反射磁盘上的文件——函数被加载,接着从文件删除,却仍然保存在内存里,让你很容易错过那些由丢失的函数引起的问题(通常被称为"getting slimed",和稀泥,捣糨糊)。正因为这个原因,任何情况下都建议你定期使用一个"新鲜"的instance运行<code>lein test</code>,或许在你提交之前。
it's easy for that process to get into a state that doesn't reflect
+
the files on disk—functions that are loaded and then deleted from the
+
file will remain in memory, making it easy to miss problems arising
+
from missing functions (often referred to as "getting
+
slimed"). Because of this it's advised to do a `lein test` run with a
+
fresh instance periodically in any case, perhaps before you commit.
+
  
 
== 使用项目 ==
 
== 使用项目 ==
  
Generally speaking, there are three different goals that are typical
+
通常来讲,Leiningen项目的有三种典型的不同用途:
of Leiningen projects:
+
  
* An application you can distribute to end-users
+
* 可以分发给终端用户的应用
* A server-side application
+
* 服务端应用
* A library for other Clojure projects to consume
+
* 其他Clojure项目可以使用的类库
  
For the first, you typically build an uberjar. For libraries,
+
对于第一种用途,通常会构建一个uberjar。对于类库,你会想将它们发布到Clojars或者私有的某个仓库。对于服务端应用,会如下文描述的那样有所变化。利用<code>lein new app myapp</code>生成一个项目,会让你从拥有一些额外默认(配置)适合非类库项目开始。
you will want to have them published to a repository like Clojars or a
+
private repository. For server-side applications it varies as
+
described below. Generating a project with `lein new app myapp` will
+
start you out with a few extra defaults suitable for non-library projects.
+
  
 
=== Uberjar ===
 
=== Uberjar ===
  
The simplest thing to do is to distribute an uberjar. This is a single
+
最简单的事情是分发一个uberjar。这是一个单一的独立的可执行jar文件,特别适合给非技术用户。为了让它工作,你必须设置命名空间,作为project.clj里的<code>:main</code>。此时,我们的project.clj看起来像这样:
standalone executable jar file most suitable for giving to
+
nontechnical users. For this to work you'll need to specify a
+
namespace as your `:main` in `project.clj`. By this point our
+
`project.clj` file should look like this:
+
  
```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.3.0"]
 
                [org.apache.lucene/lucene-core "3.0.2"]
 
                [clj-http "0.4.1"]]
 
  :profiles {:dev {:dependencies [[midje "1.3.1"]]}}
 
  :test-selectors {:default (complement :integration)
 
                  :integration :integration
 
                  :all (fn [_] true)}
 
  :main my.stuff)
 
```
 
  
The namespace you specify will need to contain a `-main` function that
+
    (defproject my-stuff "0.1.0-SNAPSHOT"
will get called when your standalone jar is run. This namespace should
+
      :description "FIXME: write description"
have a `(:gen-class)` declaration in the `ns` form at the top. The
+
      :url "http://example.com/FIXME"
`-main` function will get passed the command-line arguments. Let's try
+
      :license {:name "Eclipse Public License"
something simple in `src/my/stuff.clj`:
+
                :url "http://www.eclipse.org/legal/epl-v10.html"}
 +
      :dependencies [[org.clojure/clojure "1.3.0"]
 +
                    [org.apache.lucene/lucene-core "3.0.2"]
 +
                    [clj-http "0.4.1"]]
 +
      :profiles {:dev {:dependencies [[midje "1.3.1"]]}}
 +
      :test-selectors {:default (complement :integration)
 +
                      :integration :integration
 +
                      :all (fn [_] true)}
 +
      :main my.stuff)
  
```clj
+
你设置的命名空间必须包含一个<code>-main</code>函数,它将在独立的可执行文件运行的时候被调用。这个命名空间必须在顶层的<code>ns</code> form里有一个<code>:gen-class</code>声明,<code>-main</code>函数会接收命令行参数,让我们在<code>src/my/stuff.clj</code>尝试点简单的玩意:
(ns my.stuff
+
  (:gen-class))
+
  
(defn -main [& args]
+
    (ns my.stuff
  (println "Welcome to my project! These are your args:" args))
+
      (:gen-class))
```
+
    (defn -main [& args]
 +
      (println "Welcome to my project! These are your args:" args))
  
Now we're ready to generate your uberjar:
+
现在我们已经准备好产生一个uberjar:
  
 
     $ lein uberjar
 
     $ lein uberjar
第505行: 第354行:
 
     Created /home/phil/src/leiningen/my-stuff/target/my-stuff-0.1.0-SNAPSHOT-standalone.jar
 
     Created /home/phil/src/leiningen/my-stuff/target/my-stuff-0.1.0-SNAPSHOT-standalone.jar
  
This creates a single jar file that contains the contents of all your
+
这样将创建一个包含了所有依赖内容的单一jar文件。用户可以通过<code>java</code>调用运行它,或者在某些操作系统上,可以通过双击该jar文件来运行。
dependencies. Users can run it with a simple `java` invocation,
+
 
or on some systems just by double-clicking the jar file.
+
  
 
     $ java -jar my-stuff-0.1.0-standalone.jar Hello world.
 
     $ java -jar my-stuff-0.1.0-standalone.jar Hello world.
 
     Welcome to my project! These are your args: (Hello world.)
 
     Welcome to my project! These are your args: (Hello world.)
  
You can run a regular (non-uber) jar with the `java`
+
你可以利用<code>java</code>命令行运行一个普通的(非uber)jar,但是这需要你自己构建classpath,对于终端用户来说这不是一个好办法。
command-line tool, but that requires constructing the classpath
+
yourself, so it's not a good solution for end-users.
+
  
Of course if your users already have Leiningen installed, you can
+
当然,如果你的用户已经安装了Leiningen,你可以教导他们使用上文介绍的<code>lein run</code>。
instruct them to use `lein run` as described above.
+
  
 
=== 框架 (Uber)jars ===
 
=== 框架 (Uber)jars ===
  
Many Java frameworks expect deployment of a jar file or derived archive
+
大多数Java框架要求一个部署(deployment)配置,来自一个jar文件或者衍生的打包子格式,包含应用必需的依赖库的一个子集。框架希望能在运行时能自己提供缺失的依赖库。框架提供的这种方式的依赖库,通常在<code>:provided</code>配置里。这样的依赖库将在编译、测试等阶段有效,但是默认不会被包含在<code>uberjar</code>任务或者创建稳定部署artifacts的插件任务里。(译注:熟悉maven的朋友一定知道provided scope吧)
sub-format containing a subset of the application's necessary
+
dependencies.  The framework expects to provide the missing dependencies
+
itself at run-time.  Dependencies which are provided by a framework in
+
this fashion may be specified in the `:provided` profile.  Such
+
dependencies will be available during compilation, testing, etc., but
+
won't be included by default by the `uberjar` task or plugin tasks
+
intended to produce stable deployment artifacts.
+
  
For example, Hadoop job jars may be just regular (uber)jar files
+
例如,Hadoop job的jar可能仅仅是一个普通的(uber)jar文件,包含了所有的依赖,除了Hadoop类库自身:
containing all dependencies except the Hadoop libraries themselves:
+
  
```clj
+
    (project example.hadoop "0.1.0"
(project example.hadoop "0.1.0"
+
      ...
  ...
+
      :profiles {:provided
  :profiles {:provided
+
                {:dependencies
            {:dependencies
+
                  [[org.apache.hadoop/hadoop-core "0.20.2-dev"]]}}
              [[org.apache.hadoop/hadoop-core "0.20.2-dev"]]}}
+
      :main example.hadoop)
  :main example.hadoop)
+
```
+
  
 
     $ lein uberjar
 
     $ lein uberjar
第553行: 第388行:
 
     ...
 
     ...
  
Plugins are required to generate framework deployment jar derivatives
+
插件被要求产生框架部署jar的衍生物(例如WAR文件),包含了额外的元信息;但是<code>:provided</code>配置提供了更通用的机制来处理框架依赖。
(such as WAR files) which include additional metadata, but the
+
`:provided` profile provides a general mechanism for handling the
+
framework dependencies.
+
  
 
=== 服务端项目 ===
 
=== 服务端项目 ===
  
There are many ways to get your project deployed as a server-side
+
有很多种方式可以让你的项目作为服务端应用来部署。除了明显的uberjar方式之外,简单的程序可以使用[https://github.com/technomancy/lein-tar lein-tar]插件伴随着shell脚本打包成[http://en.wikipedia.org/wiki/Tarball tarball],然后使用[http://hugoduncan.github.com/pallet/ pallet],[http://opscode.com/chef/ chef]或者其他机制部署。Web应用可以作为uberjar部署,利用<code>ring-jetty-adapter</code>使用内嵌的Jetty,或者是使用[https://github.com/weavejester/lein-ring lein-ring插件]创建的.war文件。在uberjar之外,服务端部署是如此多变,以至于更好使用插件而非Leiningen内置的任务来处理。
application. Aside from the obvious uberjar approach, simple
+
programs can be packaged up as tarballs with accompanied shell scripts
+
using the [lein-tar plugin](https://github.com/technomancy/lein-tar)
+
and then deployed using
+
[pallet](http://hugoduncan.github.com/pallet/),
+
[chef](http://opscode.com/chef/), or other mechanisms.
+
Web applications may be deployed as uberjars using embedded Jetty with
+
`ring-jetty-adapter` or as .war (web application archive) files
+
created by the
+
[lein-ring plugin](https://github.com/weavejester/lein-ring). For
+
things beyond uberjars, server-side deployments are so varied that they
+
are better-handled using plugins rather than tasks that are built-in
+
to Leiningen itself.
+
  
If you do end up involving Leiningen in production via something like
+
如果你最终在生产环境里使用了类似<code>lein trampoline run</code>的任务来运行,那么确保你在部署之前采取了冻结所有依赖的措施,否则最终很容易会以[https://github.com/technomancy/leiningen/wiki/Repeatability 不可重复的部署]终结。考虑在你的部署单元里,连同项目代码包含<code>~/.m2/repository</code>。使用Leiningen在一个持续的集成配置里创建一个可部署的artifact是推荐的做法。例如,你可以用[http://jenkins-ci.org Jenkins]作为持续集成服务器(CI)来跑项目的所有测试套件,如果测试通过,上传tarball到S3(译者注:amazon的Simple Storage Service)。那么部署就变成只是在你的生产服务器下载并解压一个已知是良好的tarball的事情。
`lein trampoline run`, it's very important to ensure you take steps to
+
freeze all the dependencies before deploying, otherwise it could be
+
easy to end up with
+
[unrepeatable deployments](https://github.com/technomancy/leiningen/wiki/Repeatability).
+
Consider including `~/.m2/repository` in your unit of deployment along
+
with your project code. It's recommended to use Leiningen to create a
+
deployable artifact in a continuous integration setting. For example,
+
you could have a [Jenkins](http://jenkins-ci.org) CI server run your
+
project's full test suite, and if it passes, upload a tarball to S3.
+
Then deployment is just a matter of pulling down and extracting the
+
known-good tarball on your production servers.
+
  
Also remember that the `run` task defaults to including the `user`, `dev`,
+
另外,请记住<code>run</code>任务默认会包括<code>user</code>,<code>dev</code>和<code>default</code>配置项,这些并不适合生产环境。使用<code>lein trampoline with-profile production run -m myapp.main</code>是更推荐做法的。默认情况下,生产配置是空的,但是如果你的部署包含了<code>~/.m2/repository</code>,这个部署是从产生tarball的CI运行得来的,那么你应该在<code>:production</code>配置里添加它的路径作为<code>:local-repo</code>,并且和<code>:offline? true</code>一起。保持离线,可以让已经部署的项目跟CI环境里测试的版本彻底分开。
and `default` profiles, which are not suitable for production. Using
+
`lein trampoline with-profile production run -m myapp.main` is
+
recommended. By default the production profile is empty, but if your
+
deployment includes the `~/.m2/repository` directory from the CI run
+
that generated the tarball, then you should add its path as
+
`:local-repo` along with `:offline? true` to the `:production`
+
profile. Staying offline prevents the deployed project from diverging
+
at all from the version that was tested in the CI environment.
+
  
 
=== 发行类库 ===
 
=== 发行类库 ===
  
If your project is a library and you would like others to be able to
+
如果你的项目是一个类库,并且你想让别人可以在他们的项目里将它作为依赖类库来使用,你就需要把它放到一个公开的仓库里。虽然你可以[https://github.com/technomancy/leiningen/blob/master/doc/DEPLOY.md 维护一个你自己的私有仓库],或者放到[http://search.maven.org Central]仓库,但是最简单的办法还是发布到[http://clojars.org Clojars]。当你[https://clojars.org/register 创建一个帐号]后,发布变得很简单:
use it as a dependency in their projects, you will need to get it into
+
a public repository. While it's possible to
+
[maintain your own private repository](https://github.com/technomancy/leiningen/blob/rc/doc/DEPLOY.md)
+
or get it into [Central](http://search.maven.org), the easiest way is
+
to publish it at [Clojars](http://clojars.org). Once you have
+
[created an account](https://clojars.org/register) there, publishing
+
is easy:
+
  
 
     $ lein deploy clojars
 
     $ lein deploy clojars
第629行: 第422行:
 
         to https://clojars.org/repo/
 
         to https://clojars.org/repo/
  
Once that succeeds it will be available as a package on which other
+
一旦发布成功,这个类库将将作为一个包(package),可以在别的项目里依赖它。通过保存你凭证的指令,你就不需要每次都重新输入它们,查看<code>lein help deploying</code>。当发布一个非snapshot的release的时候,Leiningen会尝试使用[http://gnupg.org GPG]来对它进行签名,证明你对这个release的著作权。查看[https://github.com/technomancy/leiningen/blob/rc/doc/DEPLOY.md 部署指南]获取详细的设置信息。部署指南也包括了如何部署到其他仓库的指令。
projects may depend. For instructions on storing your credentials so
+
 
they don't have to be re-entered every time, see `lein help
+
== That's It! ==
deploying`. When deploying a release that's not a snapshot, Leiningen
+
 
will attempt to sign it using [GPG](http://gnupg.org) to prove your
+
现在可以开始coding你的下一个项目了!
authorship of the release. See the
+
[deploy guide](https://github.com/technomancy/leiningen/blob/rc/doc/DEPLOY.md).
+
for details of how to set that up. The deploy guide includes
+
instructions for deploying to other repositories as well.
+
  
== That's It!
+
== 代理配置 ==
  
Now go start coding your next project!
+
参考 [http://blog.fnil.net/blog/6e5dd7697ccaab8b15355be30918e338/ Leiningen 代理设置]。
现在可以开始编码你的下一个项目了!
+

2016年11月28日 (一) 06:10的最后版本

目录

[编辑] Leiningen教程

[编辑] 声明

  • 本Wiki上的任何文字信息均在GNU自由文档许可证1.3或更高版本下发布,如果用于任何商业用途都需经本人同意。任何转载都请注明出处。
  • 本Wiki上的内容来自本人的学习笔记,来源可能包括原创、书籍、网页、链接等,如果侵犯了您的知识产权,请与本人联系,我将及时删除。
  • 我的联系方式 killme2008@gmail.com

[编辑] Leiningen是什么

Leiningen是一个用于自动化(构建)clojure项目的工具,使你免于心急火燎的窘境。

它处理各种各样项目相关的任务,可以做到:

  • 创建新项目
  • 管理你的项目的依赖关系
  • 运行测试
  • 运行一个REPL(你不需要再关心如何将依赖加入classpath)
  • 编译Java源码(如果有的话)
  • 运行项目(如果项目是一个app的话)
  • 为项目产生一个Maven风格的pom文件
  • 为了部署,编译和打包项目
  • 发布类库到Maven仓库,例如Clojars
  • 运行Clojure编写的自定义的自动化任务(称为Leiningen插件)

如果你来自Java世界,Leiningen就是Maven和Ant的无痛结合。如果你是Ruby或者Python世界的人们,则Leiningen就是那个组合了RubyGems/Bundler/Rake和pip/Fabric等功能的一个单一工具。

[编辑] 本教程覆盖范围

本教程主要覆盖了项目结构、依赖管理、运行测试、REPL以及跟部署相关的话题。

对于那些从来没有接触过Ant或者Maven的JVM初哥,(忠告是):不要惊慌失措。Leiningen也是为了你们而设计。这个教程会帮助你快速开始,并解释Leiningen如何处理项目自动化以及JVM的依赖管理。

[编辑] 获得帮助

另外,记住Leiningen发行附带了相当全面的帮助;lein help可以获得一个任务列表,同时lein help task可以获取任务的详细信息。 更多的文档,例如readme,示范配置以及本教程也同时提供给您。

[编辑] Leiningen项目

Leiningen是跟项目打交道。一个项目就是一个包含了一组Clojure(可能也有Java)源码文件的目录,同时附带一些元信息。 元信息存在根目录的一个称为project.clj(默认名称)的文件。project.clj就是你用来告诉Leiningen项目是什么样的,它包括:

  • 项目名称
  • 项目描述
  • 你的项目依赖哪些类库
  • 使用的Clojure版本是什么
  • 哪里找到(项目)源码
  • App的主namespace是什么(译注:就跟Java App里会有一个main的public class一样)

等等。

大多数Leiningen的任务仅在项目的上下文里才有意义(译者注:也就是大多数任务不能脱离某个项目使用)。但是有些任务如 lein repl也可以从任何目录全局地工作。

接下来,让我们看看项目是如何创建的。

[编辑] 创建一个项目

我们假设你已经按照README安装了Leiningen。 创建一个项目很简单:

   $ lein new my-stuff

基于'default'模板,生成了一个名为my-stuff的项目:

   $ cd my-stuff
   $ tree
   .
   |-- project.clj
   |-- README.md
   |-- src
   |   `-- my_stuff
   |       `-- core.clj
   `-- test
       `-- my_stuff
           `-- core_test.clj

[编辑] 目录布局

到这一步,我们得到了项目的README,一个包含了代码的src/目录,一个 test/目录,以及一个向Leiningen描述项目的project.clj文件。 src/my_stuff/core.clj文件对应my-stuff.core命名空间。

尽管大多数纯Clojure项目都不需要定制目录布局,但是Leiningen允许你这样做。

[编辑] 文件名-到-命名空间的映射惯例

注意到我们使用my-stuff.core作为命名空间,而不只是my-sutff, 这是因为Clojure不鼓励使用只有一段(single-segment,译者注:意思是没有"."连接的的单一名称)的命名空间。 另外我们也注意到含有波折号(也就是减号)的命名空间,会被映射到波折号被下划线替换的文件,这是因为JVM加载含有波折号的文件会遇到问题。

命名空间的错综复杂是新手感到困扰的一个常见来源,虽然我们已经偏离本教程的范围,但是你可以在别处阅读到它们

[编辑] project.clj

一个默认的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")

如果你没有为description填写一个简短的(描述)句子,你的项目会很难在搜索结果里找到,所以请填写一下。也请顺便填写下:url(译注:项目地址URL)。

有时候,你也需要充实下README文件,但是现在让我们略过(这一步)去设置下:dependencies

注意到,不像其他语言那样,Clojure在这里也只是另一个依赖罢了,你可以很容易地切换Clojure版本。

[编辑] 依赖

[编辑] 概览

Clojure是一门寄宿语言(译注:寄宿于JVM之上),并且Clojure的类库也像其他JVM语言那样作为JAR发布。

JAR(jar文件)基本上仅仅是是一些附加了JVM特有元信息的.zip文件。 它们通常包含有.class文件(JVM字节码),和.clj源码文件, 但是它们也可以包含其他一些东西,例如配置文件、JavaScript文件或者含有静态数据的文本文件。


已发布的JVM类库拥有identifiers标识符(artifact group, artifact id)和versions版本号。

[编辑] Artifact IDs, Groups和Versions

你可以使用web接口搜索Clojars,搜索clj-http的结果页显示为:

   [clj-http "0.5.5"]

有两种不同的途径来设置依赖clj-http类库的最新版本,一种是类似于上面所展示的Leiningen格式,另一种是Maven格式。我们现在略过Maven的方式,但是你需要为使用)来自Maven central的Java类库学习一下(maven方式)。你可以直接拷贝Leiningen版本到project.clj里的:dependencies vector。

在vector里,"clj-http"被称为"artifact id"(译者注:有maven经验的童鞋会很熟悉)。"0.5.5"就是版本号。一些类库还会有"group id",显示成这样:

   [com.cedarsoft.utils.legacy/hibernate "1.3.4"]

group-id就是斜杠之前的部分。特别是对于Java类库来说,它通常是倒序的域名。而Clojure类库通常使用相同的group-id和artifact-id(例如clj-http),这种情况下你可以忽略group-id。 如果某个类库从属于一个很大的分组(例如ring-jetty-adapterring项目的一部分),那么通常所有子项目的group-id保持一样。

[编辑] Snapshot版本

有时候版本号会以 "-SNAPSHOT"结尾。这意味着这不是一个正式的release版本,而是一个开发阶段构建(版本)。不鼓励依赖snapshot版本,但是有时候这也是必须的,例如你需要bug fix等,但还没有将它们发布。尽管如此,snapshot版本不被保证停留(stick around),所以很重要的一点是非开发阶段的release绝不依赖你无法控制的snapshot版本。添加一个snapshot依赖到你的项目里,会导致Leiningen每天主动查找该依赖的最新版本(而正常的release版本会缓存在本地仓库),所以如果你使用了很多snapshot版本,可能会拖慢Leiningen一些。

请注意,有一些类库会让它们的group-id和artifact-id对应到它们jar里提供的命名空间,但是这一点仅是惯例。并不保证总是能匹配起来,因此,在你写下:require:import语句之前请翻阅下类库的文档。

[编辑] 仓库

依赖库存储在maven仓库(正式的名称是maven artifact仓库,允许一点模棱两可的话也可以简称为仓库)。

如果你熟悉Perl的CPAN,Python的Cheeseshop(也就是PyPi),Ruby的rubygems.org或者Node.js的NPM,它们其实是一类东西。 Leiningen重用已有的JVM仓库设施。已经有一些很流行的开源仓库。Leiningen默认使用它们中的两个: clojars.orgMaven Central

Clojars是Clojure社区的中心Maven仓库,而Central是更广泛的JVM社区的中心Maven仓库。

你可以添加第三方仓库,通过设置project.clj里的:repositories关键字。查看sample.project.clj示范。

[编辑] checkout依赖

有时候需要并行地开发两个项目,但是为了让变更生效,总是要运行lein install并重启你的REPL,这样做非常不方便。Leiningen提供了一种称为checkout依赖(简称checkouts)的解决办法。为了使用它,你需要在项目根目录下创建一个名为checkouts的目录,类似:

   .
   |-- project.clj
   |-- README.md
   |-- checkouts
   |-- src
   |   `-- my_stuff
   |       `-- core.clj
   `-- test
       `-- my_stuff
           `-- core_test.clj

然后,在checkouts目录里,创建你需要的项目的符号链接:

   .
   |-- project.clj
   |-- README.md
   |-- checkouts
   |   `-- superlib2 [link to ~/code/oss/superlib2]
   |   `-- superlib3 [link to ~/code/megacorp/superlib3]
   |-- src
   |   `-- my_stuff
   |       `-- core.clj
   `-- test
       `-- my_stuff
           `-- core_test.clj

checkouts目录里的类库比从仓库拉取的类库优先级更高,但是这不是用来替代罗列在项目project.clj里的:dependencies,而只是为了方便使用的一种补充手段。

checkouts特性是非传递性的(译注:A依赖B,B依赖C,那么A将依赖C,这称为依赖传递):换句话说,Leiningen不会查找一个checkout依赖库的checkout依赖。

[编辑] 运行代码

配置够了,让我们看看代码运行。启动一个REPL(读取-求值-打印循环:read-eval-print loop):

   $ lein repl
   nREPL server started on port 40612
   Welcome to REPL-y!
   Clojure 1.4.0
       Exit: Control+D or (exit) or (quit)
   Commands: (user/help)
       Docs: (doc function-name-here)
             (find-doc "part-of-name-here")
     Source: (source function-name-here)
             (user/sourcery function-name-here)
    Javadoc: (javadoc java-object-or-class-here)
   Examples from clojuredocs.org: [clojuredocs or cdoc]
             (user/clojuredocs name-here)
             (user/clojuredocs "ns-here" "name-here")
   user=>

REPL是一种你可以输入任意代码运行在项目上下文里的交互式提示。因为我们已经添加了clj-http依赖到:dependencies里,我们就可以从项目的src/目录的my-stuff.core命名空间里加载它:


   user=> (require 'my-stuff.core)
   nil
   user=> (my-stuff.core/-main)
   Hello, World!
   nil
   user=> (require '[clj-http.client :as http])
   nil
   user=> (def response (http/get "http://leiningen.org"))
   #'user/response
   user=> (keys response)
   (:trace-redirects :status :headers :body)

调用-main显示一起打印了输出("Hello, World!")和返回值nil。

通过doc可以使用内置文档,并且clojuredocs提供更全面的例子,来自ClojureDocs站点:

   user=> (doc reduce)
   -------------------------
   clojure.core/reduce
   ([f coll] [f val coll])
     f should be a function of 2 arguments. If val is not supplied,
     returns the result of applying f to the first 2 items in coll, then
     applying f to that result and the 3rd item, etc. If coll contains no
     items, f must accept no arguments as well, and reduce returns the
     result of calling f with no arguments.  If coll has only 1 item, it
     is returned and f is not called.  If val is supplied, returns the
     result of applying f to val and the first item in coll, then
     applying f to that result and the 2nd item, etc. If coll contains no
     items, returns val and f is not called.


   user=> (user/clojuredocs pprint)
   Loading clojuredocs-client...
   ========== vvv Examples ================
     user=> (def *map* (zipmap
                         [:a :b :c :d :e]
                         (repeat
                           (zipmap [:a :b :c :d :e]
                             (take 5 (range))))))
     #'user/*map*
     user=> *map*
     {:e {:e 4, :d 3, :c 2, :b 1, :a 0}, :d {:e 4, :d 3, :c 2, :b 1, :a 0}, :c {:e 4, :d 3, :c 2, :b 1, :a 0}, :b {:e 4, :d 3, :c 2, :b 1, :a 0}, :a {:e 4, :d 3, :c 2, :b 1, :a 0}}
     user=> (clojure.pprint/pprint *map*)
     {:e {:e 4, :d 3, :c 2, :b 1, :a 0},
      :d {:e 4, :d 3, :c 2, :b 1, :a 0},
      :c {:e 4, :d 3, :c 2, :b 1, :a 0},
      :b {:e 4, :d 3, :c 2, :b 1, :a 0},
      :a {:e 4, :d 3, :c 2, :b 1, :a 0}}
     nil
   ========== ^^^ Examples ================
   1 example found for clojure.pprint/pprint

你甚至能查看函数的源码:

   user=> (source my-stuff.core/-main)
   (defn -main
     "I don't do a whole lot."
     [& args]
     (println "Hello, World!"))
   user=> ; use control+d to exit

如果已经编写了-main函数的代码等待执行,你并不需要交互式地键入代码,执行run任务更简单:

   $ lein run -m my-stuff.core
   Hello, World!

提供替代的-m参数来告诉Leiningen在另一个命名空间里查找-main函数。设置project.clj里默认的:main关键字,可以让你省略-m参数。

对于长时间运行的lein run进程,可能你希望利用高阶的trampoline任务来节省内存,它允许Leiningen的JVM进程在启动项目的JVM之前退出。(译注,可以通过lein help trampoline来查看任务的详细说明):

   $ lein trampoline run -m my-stuff.server 5000

[编辑] 测试

我们还没有编写任何测试,但是我们能运行从项目模板里包含过来的失败测试:

   $ 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.

一旦我们填充了测试套件,会更有用一些。有时候你有一个很大的测试套件,但是你一次只想运行其中一到两个命名空间,lein test my.test.stuff可以帮你做到。也许你也想要使用测试选择器(test selector)拆散(break up)测试,请看lein help test获取详情。

从命令行运行lein test适合回归测试,但是JVM的缓慢启动时间让它和那种需要紧凑反馈循环的测试风格配合不良。在这种情况下,可以通过保持运行一个开启的repl,用来跑 clojure.test/run-tests,或者使用编辑器整合插件如clojure-test-mode

请谨记在心,虽然保持一个运行的进程是很方便,但是这很容易让进程进入这样一种状态:没有反射磁盘上的文件——函数被加载,接着从文件删除,却仍然保存在内存里,让你很容易错过那些由丢失的函数引起的问题(通常被称为"getting slimed",和稀泥,捣糨糊)。正因为这个原因,任何情况下都建议你定期使用一个"新鲜"的instance运行lein test,或许在你提交之前。

[编辑] 使用项目

通常来讲,Leiningen项目的有三种典型的不同用途:

  • 可以分发给终端用户的应用
  • 服务端应用
  • 其他Clojure项目可以使用的类库

对于第一种用途,通常会构建一个uberjar。对于类库,你会想将它们发布到Clojars或者私有的某个仓库。对于服务端应用,会如下文描述的那样有所变化。利用lein new app myapp生成一个项目,会让你从拥有一些额外默认(配置)适合非类库项目开始。

[编辑] Uberjar

最简单的事情是分发一个uberjar。这是一个单一的独立的可执行jar文件,特别适合给非技术用户。为了让它工作,你必须设置命名空间,作为project.clj里的:main。此时,我们的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.3.0"]
                    [org.apache.lucene/lucene-core "3.0.2"]
                    [clj-http "0.4.1"]]
     :profiles {:dev {:dependencies midje "1.3.1"}}
     :test-selectors {:default (complement :integration)
                     :integration :integration
                     :all (fn [_] true)}
     :main my.stuff)

你设置的命名空间必须包含一个-main函数,它将在独立的可执行文件运行的时候被调用。这个命名空间必须在顶层的ns form里有一个:gen-class声明,-main函数会接收命令行参数,让我们在src/my/stuff.clj尝试点简单的玩意:

   (ns my.stuff
     (:gen-class))
   (defn -main [& args]
     (println "Welcome to my project! These are your args:" args))

现在我们已经准备好产生一个uberjar:

   $ 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

这样将创建一个包含了所有依赖内容的单一jar文件。用户可以通过java调用运行它,或者在某些操作系统上,可以通过双击该jar文件来运行。


   $ java -jar my-stuff-0.1.0-standalone.jar Hello world.
   Welcome to my project! These are your args: (Hello world.)

你可以利用java命令行运行一个普通的(非uber)jar,但是这需要你自己构建classpath,对于终端用户来说这不是一个好办法。

当然,如果你的用户已经安装了Leiningen,你可以教导他们使用上文介绍的lein run

[编辑] 框架 (Uber)jars

大多数Java框架要求一个部署(deployment)配置,来自一个jar文件或者衍生的打包子格式,包含应用必需的依赖库的一个子集。框架希望能在运行时能自己提供缺失的依赖库。框架提供的这种方式的依赖库,通常在:provided配置里。这样的依赖库将在编译、测试等阶段有效,但是默认不会被包含在uberjar任务或者创建稳定部署artifacts的插件任务里。(译注:熟悉maven的朋友一定知道provided scope吧)

例如,Hadoop job的jar可能仅仅是一个普通的(uber)jar文件,包含了所有的依赖,除了Hadoop类库自身:

   (project example.hadoop "0.1.0"
     ...
     :profiles {:provided
                {:dependencies
                 org.apache.hadoop/hadoop-core "0.20.2-dev"}}
     :main example.hadoop)
   $ lein uberjar
   Compiling example.hadoop
   Created /home/xmpl/src/example.hadoop/example.hadoop-0.1.0.jar
   Including example.hadoop-0.1.0.jar
   Including clojure-1.4.0.jar
   Created /home/xmpl/src/example.hadoop/example.hadoop-0.1.0-standalone.jar
   $ hadoop jar example.hadoop-0.1.0-standalone.jar
   12/08/24 08:28:30 INFO util.Util: resolving application jar from found main method on: example.hadoop
   12/08/24 08:28:30 INFO flow.MultiMapReducePlanner: using application jar: /home/xmpl/src/example.hadoop/./example.hadoop-0.1.0-standalone.jar
   ...

插件被要求产生框架部署jar的衍生物(例如WAR文件),包含了额外的元信息;但是:provided配置提供了更通用的机制来处理框架依赖。

[编辑] 服务端项目

有很多种方式可以让你的项目作为服务端应用来部署。除了明显的uberjar方式之外,简单的程序可以使用lein-tar插件伴随着shell脚本打包成tarball,然后使用pallet,chef或者其他机制部署。Web应用可以作为uberjar部署,利用ring-jetty-adapter使用内嵌的Jetty,或者是使用lein-ring插件创建的.war文件。在uberjar之外,服务端部署是如此多变,以至于更好使用插件而非Leiningen内置的任务来处理。

如果你最终在生产环境里使用了类似lein trampoline run的任务来运行,那么确保你在部署之前采取了冻结所有依赖的措施,否则最终很容易会以不可重复的部署终结。考虑在你的部署单元里,连同项目代码包含~/.m2/repository。使用Leiningen在一个持续的集成配置里创建一个可部署的artifact是推荐的做法。例如,你可以用Jenkins作为持续集成服务器(CI)来跑项目的所有测试套件,如果测试通过,上传tarball到S3(译者注:amazon的Simple Storage Service)。那么部署就变成只是在你的生产服务器下载并解压一个已知是良好的tarball的事情。

另外,请记住run任务默认会包括user,devdefault配置项,这些并不适合生产环境。使用lein trampoline with-profile production run -m myapp.main是更推荐做法的。默认情况下,生产配置是空的,但是如果你的部署包含了~/.m2/repository,这个部署是从产生tarball的CI运行得来的,那么你应该在:production配置里添加它的路径作为:local-repo,并且和:offline? true一起。保持离线,可以让已经部署的项目跟CI环境里测试的版本彻底分开。

[编辑] 发行类库

如果你的项目是一个类库,并且你想让别人可以在他们的项目里将它作为依赖类库来使用,你就需要把它放到一个公开的仓库里。虽然你可以维护一个你自己的私有仓库,或者放到Central仓库,但是最简单的办法还是发布到Clojars。当你创建一个帐号后,发布变得很简单:

   $ lein deploy clojars
   Created ~/src/my-stuff/target/my-stuff-0.1.0-SNAPSHOT.jar
   Wrote ~/src/my-stuff/pom.xml
   No credentials found for clojars
   See `lein help deploying` for how to configure credentials.
   Username: me
   Password: 
   Retrieving my-stuff/my-stuff/0.1.0-SNAPSHOT/maven-metadata.xml (1k)
       from https://clojars.org/repo/
   Sending my-stuff/my-stuff/0.1.0-SNAPSHOT/my-stuff-0.1.0-20120531.032047-14.jar (5k)
       to https://clojars.org/repo/
   Sending my-stuff/my-stuff/0.1.0-SNAPSHOT/my-stuff-0.1.0-20120531.032047-14.pom (3k)
       to https://clojars.org/repo/
   Retrieving my-stuff/my-stuff/maven-metadata.xml (1k)
       from https://clojars.org/repo/
   Sending my-stuff/my-stuff/0.1.0-SNAPSHOT/maven-metadata.xml (1k)
       to https://clojars.org/repo/
   Sending my-stuff/my-stuff/maven-metadata.xml (1k)
       to https://clojars.org/repo/

一旦发布成功,这个类库将将作为一个包(package),可以在别的项目里依赖它。通过保存你凭证的指令,你就不需要每次都重新输入它们,查看lein help deploying。当发布一个非snapshot的release的时候,Leiningen会尝试使用GPG来对它进行签名,证明你对这个release的著作权。查看部署指南获取详细的设置信息。部署指南也包括了如何部署到其他仓库的指令。

[编辑] That's It!

现在可以开始coding你的下一个项目了!

[编辑] 代理配置

参考 Leiningen 代理设置

个人工具
名字空间

变换
操作
导航
工具箱