使用IDEA和Leiningen开发 Clojure应用
开发环境
OS: Mac OS X 10.11.1
IDEA 14.1.3 Community Edition
Leiningen
Clojure-1.8.0
JDK 1.8.0_40
安装插件La Clojure
安装IDEA插件La Clojure。进行IDEA后,点左上角的IntelliJ IDEA, 选preferences, 然后左边选Plugins, 点Browse Repositories, 搜索Clojure, 下载La Clojure。
新建项目
建立clojure工程。输入
lein new groupId/artifactId
groupId和artifactId和Maven里的概念一致.
比如我输入
$ lein new org.lightsword/clj_web
Generating a project called org.lightsword/clj_web based on the 'default' template.
The default template is intended for library projects, not applications.
To see other templates (app, plugin, etc), try `lein help new`.
就会依照模版建立一个clojure工程。标准目录如下:
.
|____.gitignore
|____.hgignore
|____CHANGELOG.md
|____doc
| |____intro.md
|____LICENSE
|____project.clj
|____README.md
|____resources
|____src
| |____org
| | |____lightsword
| | | |____clj_web.clj
|____test
| |____org
| | |____lightsword
| | | |____clj_web_test.clj
导入IDEA
这个工程直接导入IDEA不会被正确识别,因此需要在项目工程根目录clj-web下执行
lein pom
生成相应的pom.xml
我们来看一下 project.clj 个 pom.xml 的内容:
project.clj
(defproject org.lightsword/clj-web "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.8.0"]])
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.lightsword</groupId>
<artifactId>clj-web</artifactId>
<packaging>jar</packaging>
<version>0.1.0-SNAPSHOT</version>
<name>clj-web</name>
<description>FIXME: write description</description>
<url>http://example.com/FIXME</url>
<licenses>
<license>
<name>Eclipse Public License</name>
<url>http://www.eclipse.org/legal/epl-v10.html</url>
</license>
</licenses>
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory>
<resources>
<resource>
<directory>resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>resources</directory>
</testResource>
</testResources>
<directory>target</directory>
<outputDirectory>target/classes</outputDirectory>
<plugins/>
</build>
<repositories>
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>clojars</id>
<url>https://clojars.org/repo/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
</project>
<!-- This file was autogenerated by Leiningen.
Please do not edit it directly; instead edit project.clj and regenerate it.
It should not be considered canonical data. For more information see
https://github.com/technomancy/leiningen -->
pom 里面关键的就是 clojure 的依赖:
<dependencies>
<dependency>
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
很显然,project.clj 配置更加简洁.
然后在IDEA中import project,
选择Import project from external model,
选择Maven,然后一路点下去。
配置main函数
打开的IDEA工程目录如下:
打开src目录,在org.lightsword包中有clj_web.clj文件:
(ns org.lightsword.clj_web)
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))
这个clojure文件中,并没有main函数,因此在IDEA中执行run, 什么也不会输出。
若是在命令行执行lein run会输出 没有 main namespace specified:
$ lein run
No :main namespace specified in project.clj.
把这个文件的内容改一下
(ns org.lightsword.clj_web)
(defn -main [& args]
(println "Hello, World!"))
然后在IDEA中最上边的菜单中点Run, 选择Edit Configurations
在Run菜单中执行Run "clj-web", 程序预期会打印出"Hello, World!"。但是笔者在运行过程发现,什么也没输出.奇怪.
代码改成这样:
(ns org.lightsword.clj-web)
(defn -main [& args]
(println "Hello, World!"))
(prn "Hello,Clojure!")
只输出了"Hello,Clojure!".不正常.
这时候,如果执行lein run, 会说“No :main namespace specified in project.clj”.
需要修改project.clj,添加:main函数的配置:
:main org.lightsword.clj_web
完整的 project.clj 如下:
(defproject org.lightsword/clj-web "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"}
:main org.lightsword.clj_web
:dependencies [[org.clojure/clojure "1.8.0"]])
保存后,执行lein run,输出
"Hello,Clojure!"
Hello, World!
我们发现一个有趣的现象,
(ns org.lightsword.clj_web)
;; A
(defn -main [& args]
(println "Hello, World!"))
;; B
(prn "Hello,Clojure!")
程序是先执行 B 代码行,然后执行的 A 代码行.
还有一点就是
(prn "Hello,Clojure!")
原样输出"Hello,Clojure!"(带双引号),而使用 println 不带.
再写一个ns
在src 目录下面右击新建Clujure Namespace
fib_seq.clj
(ns org.lightsword.fib_seq)
;; 定义斐波那契数列
(defn fib []
(defn fib-iter [a b]
(lazy-seq (cons a (fib-iter b (+ a b)))))
(fib-iter 0 1))
(println (take 10 (fib)))
然后Run - Edit Configuration
按照如图所示配置, Run
Run - clj_web, 输出
/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/bin/java -Didea.launcher.port=7543 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA 14.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath "/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/tools.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Users/jack/pl/clojure/clj-web/resources:/Users/jack/pl/clojure/clj-web/src:/Users/jack/pl/clojure/clj-web/test:/Users/jack/pl/clojure/clj-web/target/classes:/Users/jack/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:/Applications/IntelliJ IDEA 14.app/Contents/lib/idea_rt.jar" com.intellij.rt.execution.application.AppMain clojure.main /Users/jack/pl/clojure/clj-web/src/org/lightsword/fib_seq.clj
(0 1 1 2 3 5 8 13 21 34)
Process finished with exit code 0
光剑说
这个 IDEA 集成 Clojure 的插件不怎么好用. 单个 clj 文件执行每次需要 Edit Configuration, 太麻烦了.
lein run 可以指定 ns 执行一个 clj 文件里的代码吗?类似:
lein run org.lightsword.fib_seq
经测试, 这样写是没用的.
只有在 fib_seq.clj 里面写一个 main 函数,然后在 project.clj 里面指定
:main org.lightsword.fib_seq
然后执行
lein run
输出
$ lein run
(0 1 1 2 3 5 8 13 21 34)
"Hello, Fib!"
在 clojure repl 中执行
其实在 IDE 中,调试 clj 代码确实比较麻烦, 不如直接在 repl 中简单直接:
user=> (load-file "/Users/jack/pl/clojure/clj-web/src/org/lightsword/clj_web.clj")
Hello,Clojure!
nil
user=> (org.lightsword.clj_web/-main)
"Hello, World!"
nil
user=> (load-file "/Users/jack/pl/clojure/clj-web/src/org/lightsword/fib_seq.clj")
(0 1 1 2 3 5 8 13 21 34)
nil
user=> (org.lightsword.fib_seq/-main)
"Hello, Fib!"
想必这些插件实现的机制也是这些.