Въведение в Leiningen за Clojure

1. Въведение

Leiningen е модерна система за изграждане на нашите проекти Clojure. Освен това е написано и конфигурирано изцяло в Clojure.

Той работи подобно на Maven, като ни дава декларативна конфигурация, която описва нашия проект, без да е необходимо да конфигурираме точните стъпки, които да бъдат изпълнени.

Нека да влезем и да видим как да започнем с Leiningen за изграждането на нашите проекти Clojure.

2. Инсталиране на Leiningen

Leiningen се предлага като самостоятелно изтегляне, както и от голям брой мениджъри на пакети за различни системи.

Налични са самостоятелни изтегляния за Windows, както и за Linux и Mac. Във всички случаи изтеглете файла, направете го изпълним, ако е необходимо, и след това е готов за използване.

При първото стартиране на скрипта той ще изтегли останалата част от приложението Leiningen и след това ще бъде кеширано от този момент нататък:

$ ./lein Downloading Leiningen to /Users/user/.lein/self-installs/leiningen-2.8.3-standalone.jar now... ..... Leiningen is a tool for working with Clojure projects. Several tasks are available: ..... Run `lein help $TASK` for details. .....

3. Създаване на нов проект

След като Leiningen е инсталиран, можем да го използваме, за да създадем нов проект, като извикаме lein new .

Това създава проект, използвайки определен шаблон от набор от опции:

  • приложение - Използва се за създаване на приложение
  • по подразбиране - Използва се за създаване на обща структура на проекта, обикновено за библиотеки
  • плъгин - Използва се за създаване на плъгин Leiningen
  • шаблон - Използва се за създаване на нови шаблони на Leiningen за бъдещи проекти

Например, за да създадем ново приложение, наречено „my-project“, ще изпълним:

$ ./lein new app my-project Generating a project called my-project based on the 'app' template.

Това ни дава проект, съдържащ:

  • Дефиниция на компилация - project.clj
  • Изходна директория - src - включително първоначален изходен файл - src / my_project / core.clj
  • Тестова директория - тест - включително първоначален тестов файл - test / my_project / core_test.clj
  • Някои допълнителни файлове с документация - README.md, LICENSE, CHANGELOG.md и doc / intro.md

Разглеждайки нашата дефиниция на компилация, ще видим, че тя ни казва какво да изградим, но не и как да я изградим:

(defproject my-project "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "//example.com/FIXME" :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "//www.eclipse.org/legal/epl-2.0/"} :dependencies [[org.clojure/clojure "1.9.0"]] :main ^:skip-aot my-project.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})

Това ни казва:

  • Подробностите за проекта, състоящи се от името на проекта, версията, описанието, началната страница и данните за лиценза.
  • Основното пространство от имена, което да се използва при изпълнение на приложението
  • Списъкът на зависимостите
  • Целевият път за вграждане на изхода
  • Профил за изграждане на uberjar

Имайте предвид, че основното пространство на имена на източника е my-project.core и се намира във файла my_project / core.clj. В Clojure не се препоръчва да се използват едносегментни пространства от имена - еквивалент на класове от най-високо ниво в проект на Java.

Освен това имената на файловете се генерират с долни черти вместо с тирета, тъй като JVM има някои проблеми с тирета в имената на файловете.

Генерираният код е доста прост:

(ns my-project.core (:gen-class)) (defn -main "I don't do a whole lot ... yet." [& args] (println "Hello, World!"))

Също така забележете, че Clojure тук е само зависимост. Това прави тривиалното писане на проекти, използвайки желаната версия на библиотеките Clojure , и особено да има множество различни версии, работещи в една и съща система.

Ако променим тази зависимост, вместо това ще получим алтернативната версия.

4. Изграждане и бягане

Нашият проект не струва много, ако не можем да го изградим, стартираме и опаковаме за разпространение, така че нека разгледаме това по-нататък.

4.1. Стартиране на REPL

След като имаме проект, можем да стартираме REPL вътре в него, използвайки lein repl . Това ще ни даде REPL, който има всичко в проекта, което вече е налично в пътя на класа - включително всички файлове на проекта, както и всички зависимости.

Той също така ни стартира в дефинираното основно пространство от имена за нашия проект:

$ lein repl nREPL server started on port 62856 on host 127.0.0.1 - nrepl://127.0.0.1:62856 []REPL-y 0.4.3, nREPL 0.5.3 Clojure 1.9.0 Java HotSpot(TM) 64-Bit Server VM 1.8.0_77-b03 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e my-project.core=> (-main) Hello, World! nil

Това изпълнява функцията -main в текущото пространство от имена, което видяхме по-горе.

4.2. Стартиране на приложението

Ако работим по проект на приложение - създаден с помощта на lein new app - тогава можем просто да стартираме приложението от командния ред. Това се прави с помощта на lein run :

$ lein run Hello, World!

Това ще изпълни функцията, наречена -main в пространството от имена, дефинирано като : main в нашия файл project.clj .

4.3. Изграждане на библиотека

Ако работим по проект на библиотека - създаден с помощта на lein new default - тогава можем да изградим библиотеката в JAR файл за включване в други проекти .

Имаме два начина да постигнем това - използвайки lein jar или lein install . Разликата е просто в това къде е поставен изходният JAR файл.

Ако използваме lein jar, той ще го постави в локалната целева директория :

$ lein jar Created /Users/user/source/me/my-library/target/my-library-0.1.0-SNAPSHOT.jar

Ако използваме lein install , той ще изгради JAR файла, ще генерира pom.xml файл и след това ще ги постави в локалното хранилище на Maven (обикновено под .m2 / хранилище в домашната директория на потребителите)

$ lein install Created /Users/user/source/me/my-library/target/my-library-0.1.0-SNAPSHOT.jar Wrote /Users/user/source/me/my-library/pom.xml Installed jar and pom into local repo.

4.4. Изграждане на Uberjar

Ако работим по проект за кандидатстване, Leiningen ни дава възможност да изградим това, което се нарича uberjar . Това е JAR файл, съдържащ самия проект и всички зависимости и настроен да позволява да се изпълнява както е.

$ lein uberjar Compiling my-project.core Created /Users/user/source/me/my-project/target/uberjar/my-project-0.1.0-SNAPSHOT.jar Created /Users/user/source/me/my-project/target/uberjar/my-project-0.1.0-SNAPSHOT-standalone.jar

Файлът my-project-0.1.0-SNAPSHOT.jar е JAR файл, съдържащ точно локалния проект, а файлът my-project-0.1.0-SNAPSHOT-standalone.jar съдържа всичко необходимо за стартиране на приложението.

$ java -jar target/uberjar/my-project-0.1.0-SNAPSHOT-standalone.jar Hello, World!

5. Зависимости

Whilst we can write everything needed for our project ourselves, it's generally significantly better to re-use the work that others have already done on our behalf. We can do this by having our project depend on these other libraries.

5.1. Adding Dependencies to Our Project

To add dependencies to our project, we need to add them correctly to our project.clj file.

Dependencies are represented as a vector consisting of the name and version of the dependency in question. We've already seen that Clojure itself is added as a dependency, written in the form [org.clojure/clojure “1.9.0”].

If we want to add other dependencies, we can do so by adding them to the vector next to the :dependencies keyword. For example, if we want to depend on clj-json we would update the file:

 :dependencies [[org.clojure/clojure "1.9.0"] [clj-json "0.5.3"]]

Once done, if we start our REPL – or any other way to build or run our project – then Leiningen will ensure that the dependencies are downloaded and available on the classpath:

$ lein repl Retrieving clj-json/clj-json/0.5.3/clj-json-0.5.3.pom from clojars Retrieving clj-json/clj-json/0.5.3/clj-json-0.5.3.jar from clojars nREPL server started on port 62146 on host 127.0.0.1 - nrepl://127.0.0.1:62146 REPL-y 0.4.3, nREPL 0.5.3 Clojure 1.9.0 Java HotSpot(TM) 64-Bit Server VM 1.8.0_77-b03 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e my-project.core=> (require '(clj-json [core :as json])) nil my-project.core=> (json/generate-string {"foo" "bar"}) "{\"foo\":\"bar\"}" my-project.core=>

We can also use them from inside our project. For example, we could update the generated src/my_project/core.clj file as follows:

(ns my-project.core (:gen-class)) (require '(clj-json [core :as json])) (defn -main "I don't do a whole lot ... yet." [& args] (println (json/generate-string {"foo" "bar"})))

And then running it will do exactly as expected:

$ lein run {"foo":"bar"}

5.2. Finding Dependencies

Often, it can be difficult to find the dependencies that we want to use in our project. Leiningen comes with a search functionality built in to make this easier. This is done using lein search.

For example, we can find our JSON libraries:

$ lein search json Searching central ... [com.jwebmp/json "0.63.0.60"] [com.ufoscout.coreutils/json "3.7.4"] [com.github.iarellano/json "20190129"] ..... Searching clojars ... [cheshire "5.8.1"] JSON and JSON SMILE encoding, fast. [json-html "0.4.4"] Provide JSON and get a DOM node with a human representation of that JSON [ring/ring-json "0.5.0-beta1"] Ring middleware for handling JSON [clj-json "0.5.3"] Fast JSON encoding and decoding for Clojure via the Jackson library. .....

This searches all of the repositories that our project is working with – in this case, Maven Central and Clojars. It then returns the exact string to put into our project.clj file and, if available, the description of the library.

6. Testing Our Project

Clojure has built-in support for unit testing our application, and Leiningen can harness this for our projects.

Our generated project contains test code in the test directory, alongside the source code in the src directory. It also includes a single, failing test by default – found in test/my_project/core-test.clj:

(ns my-project.core-test (:require [clojure.test :refer :all] [my-project.core :refer :all])) (deftest a-test (testing "FIXME, I fail." (is (= 0 1))))

This imports the my-project.core namespace from our project, and the clojure.test namespace from the core Clojure language. We then define a test with the deftest and testing calls.

We can immediately see the names of the test, and the fact that it's deliberately written to fail – it asserts that 0 == 1.

Let's run this using the lein test command, and immediately see the tests running and failing:

$ lein test lein test my-project.core-test lein test :only my-project.core-test/a-test FAIL in (a-test) (core_test.clj:7) FIXME, I fail. expected: (= 0 1) actual: (not (= 0 1)) Ran 1 tests containing 1 assertions. 1 failures, 0 errors. Tests failed.

If we instead fix the test, changing it to assert that 1 == 1 instead, then we'll get a passing message instead:

$ lein test lein test my-project.core-test Ran 1 tests containing 1 assertions. 0 failures, 0 errors.

This is a much more succinct output, only showing what we need to know. This means that when there are failures, they immediately stand out.

If we want to, we can also run a specific subset of the tests. The command line allows for a namespace to be provided, and only tests in that namespace are executed:

$ lein test my-project.core-test lein test my-project.core-test Ran 1 tests containing 1 assertions. 0 failures, 0 errors. $ lein test my-project.unknown lein test my-project.unknown Ran 0 tests containing 0 assertions. 0 failures, 0 errors.

7. Summary

This article has shown how to get started with the Leiningen build tool, and how to use it to manage our Clojure based projects – both executable applications and shared libraries.

Защо не го изпробвате на следващия проект и да видите колко добре може да работи.