查看Leiningen教程中文版的源代码
←
Leiningen教程中文版
跳转到:
导航
、
搜索
因为以下原因,你没有权限编辑本页:
您刚才请求的操作只有这个用户组中的用户才能使用:
用户
您可以查看并复制此页面的源代码:
= Leiningen教程 = * 原文:https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md * 译者:dennis (killme2008@gmail.com),转载请注明出处 * 本文链接:http://wiki.fnil.net/index.php?title=Leiningen_tutorial中文版 {{版权声明}} == Leiningen是什么 == Leiningen是一个用于自动化(构建)clojure项目的工具,使你免于心急火燎的窘境。 它处理各种各样项目相关的任务,可以做到: * 创建新项目 * 管理你的项目的依赖关系 * 运行测试 * 运行一个REPL(你不需要再关心如何将依赖加入classpath) * 编译java源码(如果有的话) * 运行项目(如果项目是一个app的话) * 为项目产生一个maven风格的pom文件 * 为部署编译和打包项目 * 发行类库到maven仓库,例如[http://clojars.org Clojars] * 运行clojure编写的自定义的自动化任务(称为leiningen插件) 如果你来自Java世界,Leiningen就是Maven和Ant的无痛结合。如果你是Ruby或者Python世界的人们,则Leiningen 就是那个组合了RubyGems/Bundler/Rake和pip/Fabric等功能的一个单一工具。 == 本教程覆盖范围 == 本教程主要覆盖了项目结构、依赖管理、运行测试、REPL以及跟部署相关的话题。 对于那些从来没有接触过Ant或者Maven的JVM初哥,(忠告是):不要惊慌失措。Leiningen也是为了你们而设计。这个教程 会帮助你快速开始,并解释Leiningen如何处理项目自动化以及JVM的依赖管理。 == 获得帮助 == 另外,记住Leiningen发行附带了相当全面的帮助;<code>lein help</code>可以获得一个任务列表,同时<code>lein help task</code>可以获取任务的详细信息。 更多的文档,例如readme,示范配置以及本教程也同时提供给您。 == Leiningen项目 == Leiningen是跟''项目''打交道。一个项目就是一个包含了一组Clojure(可能也有Java)源码文件的目录,同时附带一些元信息。 元信息存在根目录的一个称为<code>project.clj</code>(默认名称)的文件。<code>project.clj</code>就是你用来告诉Leiningen 项目是什么样的,包括: *项目名称 *项目描述 *你的项目依赖哪些类库 *使用的Clojure版本是什么 *哪里找到(项目)源码 *App的main namespace是什么 等等。 大多数Leiningen的任务仅在项目的上下文里才有意义(译者注:也就是大多数任务不能脱离某个项目使用)。但是有些任务如 <code>lein repl</code>也可以从任何目录全局地工作。 接下来,让我们看看项目是如何创建的。 == 创建一个项目 == 我们假设你已经按照[https://github.com/technomancy/leiningen/blob/master/README.md 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,一个包含了代码的<code>src/</code>目录,一个 <code>test/</code>目录,以及一个向Leiningen描述项目的<code>project.clj</code>文件。 <code>src/my_stuff/core.clj</code>文件对应<code>my-stuff.core</code>命名空间。 尽管大多数纯Clojure项目都不需要定制目录布局,但是Leiningen允许你这样做。 === 文件名-到-命名空间的映射惯例 === 注意到我们使用<code>my-stuff.core</code>作为命名空间,而不只是<code>my-sutff</code>, 这是因为Clojure不鼓励使用只有一段(single-segment,译者注:意思是没有"."连接的的单一名称)的命名空间。 另外我们也注意到含有波折号(也就是减号)的命名空间,会被映射到波折号被下划线替换的文件,这是因为 JVM加载含有波折号的文件会遇到问题。命名空间的错综复杂是新手感到困扰的一个常见来源,虽然我们已经偏离本 教程的范围,但是你可以在[http://blog.8thlight.com/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html 别处阅读到它们]。 == 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"]]) 如果你没有为<code>description</code>填写一个简短的(描述)句子,你的项目 会难在搜索结果里找到,所以请填写一下。也请填写下<code>:url</code>(译注:项目地址URL)。 有时候,你也需要充实下README文件,但是现在让我们略过(这一步)去设置下<code>:dependencies</code>。 注意到,不像其他语言那样,Clojure在这里也只是另一个依赖罢了,你可以很容易地切换Clojure版本。 == 依赖 == === 概览 === Clojure是一门寄宿语言(译注:寄宿于JVM之上),并且Clojure的类库也像其他JVM语言那样作为JAR发布。 JAR(<code>jar</code>文件)基本上仅仅是是一些附加了JVM特有元信息的<code>.zip</code>文件。 它们通常包含有<code>.class</code>文件(JVM字节码),和<code>.clj</code>源码文件, 但是它们也可以包含其他一些东西,例如配置文件、JavaScript文件或者含有静态数据的文本文件。 已发布的JVM类库拥有''identifiers''(artifact group, artifact id)和''versions''。 === Artifact IDs, Groups和Versions === 你可以使用web接口[http://clojars.org/search?q=clj-http 搜索Clojars],搜索clj-http的结果页显示为: [clj-http "0.5.5"] 有两种不同的途径来设置依赖<code>clj-http</code>类库的最新版本,一种是类似于上面所展示 的Leiningen格式,另一种是Maven格式。我们现在略过Maven的方式,但是你需要为使用) 来自[http://search.maven.org Maven central]的Java类库学习一下(maven方式)。你可以直接拷贝Leiningen版本 到<code>project.clj</code>里的<code>:dependencies</code> 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。 如果某个类库从属于一个很大的分组(例如<code>ring-jetty-adapter</code>是<code>ring</code>项目的一部分), 那么通常所有子项目的group-id保持一样。 === Snapshot版本 === 有时候版本号会以 "-SNAPSHOT"结尾。这意味着这不是一个正式的release版本,而是一个开发阶段构建(版本)。 不鼓励依赖snapshot版本,但是有时候这也是必须的,例如你需要bug fix等,但还没有将它们发布。尽管如此, snapshot版本不被保证停留,所以很重要的一点是非开发阶段的release绝不依赖你无法控制的snapshot版本。 添加一个snapshot依赖到你的项目里,会导致Leiningen每天主动查找该依赖的最新版本(而正常的release版本 会缓存在本地仓库),所以如果你使用了很多snapshot版本,可能会拖慢Leiningen一些。 请注意,有一些类库会让它们的group-id和artifact-id对应到它们jar里提供的命名空间,但是这一点仅是惯例。 并不保证总是能匹配起来,因此,在你写下<code>:require</code>和<code>:import</code>语句之前请翻阅 下类库的文档。 === 仓库 === 依赖库存储在''maven仓库''(正式的名称是maven artifact仓库,允许一点模棱两可的话也可以简称为仓库)。 如果你熟悉Perl的CPAN,Python的Cheeseshop(也就是PyPi),Ruby的rubygems.org或者Node.js的NPM,它们其实是一类东西。 Leiningen重用已有的JVM仓库设施。已经有一些很流行的开源仓库。Leiningen默认使用它们中的两个: [http://clojars.org clojars.org]和[http://search.maven.org/ Maven Central]。 [http://clojars.org Clojars]是Clojure社区的中心Maven仓库,而[http://search.maven.org/ Central]是更广泛的JVM社区的中心Maven仓库。 你可以添加第三方仓库,通过设置project.clj里的<code>:repositories</code>关键字。查看[https://github.com/technomancy/leiningen/blob/rc/sample.project.clj sample.project.clj]示范。 === checkout依赖 === 有时候需要并行地开发两个项目,但是为了让变更生效,总是要运行<code>lein install</code>并重启你的REPL, 这样做非常不方便。Leiningen提供了一种称为''checkout依赖''(简称''checkouts'')的解决办法。为了使用它,你需要 在项目根目录创建一个名为<code>checkouts</code>的目录,类似: . |-- 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 <code>checkouts</code>目录里的类库比从仓库拉取的类库优先级更高,但是这不是用来替代 罗列在项目project.clj里的<code>:dependencies</code>,而只是为了方便使用的一种补充。 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是一种你可以输入任意代码运行在项目上下文里的交互式提示。因为我们已经添加了<code>clj-http</code>依赖到<code>:dependencies</code>里,我们就可以从项目的<code>src/</code>目录的<code>my-stuff.core</code>命名空间里加载它: 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) 调用<code>-main</code>显示一起打印了输出("Hello, World!")和返回值nil。 通过<code>doc</code>可以使用内置文档,并且<code>clojuredocs</code>提供更全面的例子,来自[http://clojuredocs.org 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 如果已经编写了<code>-main</code>函数的代码等待执行,你并不需要交互式地键入代码,执行<code>run</code>任务更简单: $ lein run -m my-stuff.core Hello, World! 提供替代的<code>-m</code>参数来告诉Leiningen在另一个命名空间里查找<code>-main</code>函数。设置project.clj里默认的<code>:main</code>关键字,可以让你省略<code>-m</code>参数。 对于长时间运行的<code>lein run</code>进程,可能你希望利用高阶的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. 一旦我们填充了测试套件,会更有用一些。有时候你有一个很大的测试套件,但是你一次只想运行其中一到两个命名空间,<code>lein test my.test.stuff</code>可以帮你做到。也许你也想要使用测试选择器(test selector)拆散(break up)测试,请看<code>lein help test</code>获取详情。 从命令行运行<code>lein test</code>适合回归测试,但是JVM的缓慢启动时间让它和那种需要紧凑反馈循环的测试风格配合不良。在这种情况下,可以通过保持运行一个开启的repl,用来跑 [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]。 请谨记在心,虽然保持一个运行的进程是很方便,但是这很容易让进程进入这样一种状态:没有反射磁盘上的文件——函数被加载,接着从文件删除,却仍然保存在内存里,让你很容易错过那些由丢失的函数引起的问题(通常被称为"getting slimed",和稀泥,捣糨糊)。正因为这个原因,任何情况下都建议你定期使用一个"新鲜"的instance运行<code>lein test</code>,或许在你提交之前。 == 使用项目 == 通常来讲,Leiningen项目的有三种典型的不同用途: * 可以分发给终端用户的应用 * 服务端应用 * 其他Clojure项目可以使用的类库 对于第一种用途,通常会构建一个uberjar。对于类库,你会想将它们发布到Clojars或者私有的某个仓库。对于服务端应用,会如下文描述的那样有所变化。利用<code>lein new app myapp</code>生成一个项目,会让你从拥有一些额外默认(配置)适合非类库项目开始。 === Uberjar === 最简单的事情是分发一个uberjar。这是一个单一的独立的可执行jar文件,特别适合给非技术用户。为了让它工作,你必须设置命名空间,作为project.clj里的<code>:main</code>。此时,我们的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) The namespace you specify will need to contain a `-main` function that will get called when your standalone jar is run. This namespace should have a `(:gen-class)` declaration in the `ns` form at the top. The `-main` function will get passed the command-line arguments. Let's try something simple in `src/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] (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 This creates a single jar file that contains the contents of all your 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. Welcome to my project! These are your args: (Hello world.) You can run a regular (non-uber) jar with the `java` 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 instruct them to use `lein run` as described above. === 框架 (Uber)jars === Many Java frameworks expect deployment of a jar file or derived archive 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 containing all dependencies except the Hadoop libraries themselves: ```clj (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 ... Plugins are required to generate framework deployment jar derivatives (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 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 `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`, 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. === 发行类库 === 如果你的项目是一个类库,并且你想让别人可以在他们的项目里将它作为依赖类库来使用,你就需要把它放到一个公开的仓库里。虽然你可以[https://github.com/technomancy/leiningen/blob/rc/doc/DEPLOY.md 维护一个你自己的私有仓库],或者放到[http://search.maven.org Central]仓库,但是最简单的办法还是发布到[http://clojars.org Clojars]。当你[https://clojars.org/register 创建一个帐号]后,发布变得很简单: $ 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),可以在别的项目里依赖它。通过保存你凭证的指令,你就不需要每次都重新输入它们,查看<code>lein help deploying</code>。当发布一个非snapshot的release的时候,Leiningen会尝试使用[http://gnupg.org GPG]来对它进行签名,证明你对这个release的著作权。查看[https://github.com/technomancy/leiningen/blob/rc/doc/DEPLOY.md 部署指南]获取详细的设置信息。部署指南也包括了如何部署到其他仓库的指令。 == That's It! == 现在可以开始coding你的下一个项目了!
该页面使用的模板:
模板:版权声明
(
查看源代码
)
返回到
Leiningen教程中文版
。
个人工具
登录
名字空间
页面
讨论
变换
查看
阅读
查看源代码
查看历史
操作
搜索
导航
首页
社区专页
新闻动态
最近更改
随机页面
帮助
工具箱
链入页面
相关更改
特殊页面