使用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!"

想必这些插件实现的机制也是这些.

results matching ""

    No results matching ""