构建和部署Clojure应用程序的最佳实践:好的教程?

时间:2010-03-06 07:21:20

标签: deployment lisp clojure leiningen

我是Clojure的新手,我开始尝试构建应用程序。

到目前为止,我所看到的有关编译Clojure程序的教程的所有内容都涉及交互性。例如,“加载REPL并键入(加载文件”this-or-that“)以运行。这很好,但这还不够。

我已经习惯了C或Delphi等语言的编辑 - 编译 - 运行习惯,我本能地驱使编辑,然后点击“M-x compile”。

问题在于,“lein uberjar”,我理解相当于“制造”,即使对于一个你好的世界也很难执行。因此,我将不得不弄清楚这种“交互式开发”的东西是如何运作的,停止使用uberjar,就像它的快速制作一样,并且只在一天结束时保存它。

我在构建时注意到的另一件事(使用lein uberjar)是我正在处理的小GUI应用程序在编译过程中弹出帧,就好像它们在编译时正在执行一样。这对我来说似乎有点违反直觉;它并不像我想象的那样类似于“制造”。

我知道Lisp的开发方式是在REPL中交互式工作,我并不是要改变它:我想适应这种生活方式。不幸的是,我没有看到关于如何这样做的文档形式。例如,如何重置机器的当前状态。只是在不能进行某种重置的情况下继续编译各个片段似乎有点麻烦。

我在Clojure(和Lisp)上看到的大多数教程似乎都专注于在REPL中进行黑客攻击。应用程序部署的最佳实践对我来说仍然是一个谜。我的用户只是用户;他们不会成为将文件加载到REPL的开发人员。

所以这是我的问题:任何有关构建Clojure应用程序的整个过程(包括部署)的良好信息或教程的资源?

(注意:我已经安装并运行了所有先决条件(例如Emacs,Slime,Leiningen等),因此这不是一个问题)。

2 个答案:

答案 0 :(得分:26)

一些快速提示,然后是一些链接:

在开发过程中不要使用lein uberjar;更喜欢lein jar。区别在于lein uberjar将所有依赖项放在生成的jar中(包括Clojure本身),这样您的单个jar就是一个完全自包含的包,里面有你的app; lein jar只会生成您自己的代码。 uberjar方法对于部署有明显的好处,但是对于开发,您应该能够在运行应用程序时使用适当的类路径,从而节省准备uberjar所需的时间。如果您不想手动管理测试运行的类路径,请查看lein run plugin

此外,很可能大多数代码实际上不应该是AOT编译的。在某些Java互操作方案中,AOT是必需的,但大多数情况下,它会使启动速度略有提升,并且与不同版本的Clojure的二进制兼容性会产生令人讨厌的问题。我认为后一个问题与uberjar - 独立应用程序类型的项目无关,但是如果可能的话,至少应该留下任何库代码进行JIT编辑。使用Leiningen,您可以在:namespaces中的defproject表单中放置一个project.clj子句来确定要编译的命名空间;你遗漏的任何内容都将默认为JIT编辑。较早版本的Leiningen默认用于编译所有内容,这实际上是升级的一个很好的理由!

至于在编译期间弹出的窗口,我猜你在宏扩展时或者在任何函数定义或类似构造之外运行窗口弹出代码。 (最顶层的事情就像(println "Foo!")。)我认为这是你不应该做的事情 - 除非你打算将你的代码作为脚本运行,无论如何。要避免此问题,请在函数定义中包含副作用代码,并使用:main中的project.clj子句为应用程序提供入口点。 (如果您说:main foo,则-main命名空间中的foo函数将用作您应用的入口点。这是默认值,无论如何,至少上面提到的{ {1}}似乎有硬编码的名称 - 不确定lein本身。)

至于重置REPL的状态 - 你可以重新启动它。使用SLIME,M-x slime-restart-inferior-lisp将在保持Emacs会话的所有其他状态的同时做到这一点。

另见Clojure Google小组的这些讨论:

  1. Clojure for system administration
  2. Prepping clojure for packaging (was: Re: Clojure for system administration)
  3. Leiningen, Clojure and libraries: what am I missing?

答案 1 :(得分:13)

不,您不在REPL上输入功能。

您可以像平常一样编辑源文件。 Lisp的优势在于您可以同时在后台运行系统,因此您可以从源文件中编译各个函数并将它们放入正在运行的系统中,甚至可以在那里替换它们。

如果您使用Slime,则在源文件中按C-c C-c以编译并加载该功能。然后,您可以切换到REPL进行测试和探索,但是您希望将任何内容保留为源,并将其放入源文件中。

教程通常首先在REPL上输入内容,因为您没有太多需要为此设置,但严肃的开发集成了正在运行的系统和源文件管理。


为了说明,我通常的工作流程(我使用Common Lisp,但Clojure类似)是这样的:

  • 启动Emacs
  • M-x slime启动Slime,Lisp系统,并通过Swank连接两个
  • ,(命令)load-system foo将当前项目(仅在必要时进行编译)加载到图像中
  • C-x b切换到源缓冲区
  • C-c ~使源目录成为当前目录,将源包作为REPL的当前包。

现在,我设置了我的系统在后台运行。然后工作:

  • 更改或添加功能或类定义
  • C-c C-c编译并将其加载到图像
  • 切换到REPL,测试
  • 调试

没有重要的编译暂停,因为我从不编译整个事物,只是个别定义。