В этой статье не будет сравнения Clojure с другими языками, так как отталкиваясь от скромного опыта сравнивать не с чем.
Основная информация о Clojure
Clojure — это мультипарадигмальный язык программирования общего назначения поощряющий функциональное программирование. Основой этого языка и его синтаксиса является Lisp и его S-expressions. В отличие от Lisp'a Clojure включает и другие типы данных (коллекции), такие как: векторы, ассоциативные массивы, множества и очень удобные в использовании ключевые-слова. Код Clojure компилируется в JVM байткод, что позволяет развертывать приложения на большом количестве платформ и само-собой использовать все доступные Java библиотеки.
В интернетах полно информации о структурах Clojure, поэтому я пропущу описание оных.
Комюнити
Нельзя не сказать о сложившимся вокруг этого языка программирования сообществе, которое не столь огромно, но очень дружелюбно к новичкам и не только, они с готовностью помогают решать возникшие проблемы. Имеется большое количество видеоматериалов по самым разным аспектам использования Clojure, только вот практически все они на английском языке (рай для самореализации переводчиков). В конце статьи будут ссылки.
Leiningen
Альтернатива Maven. Используется для управления зависимостями проекта, настройками библиотек и глобальными настройками проекта. Предоставляет консольные команды для создания, тестирования, компиляции и запуска приложений.
Проекты на Clojure создаются с помощью команды: $ lein new <название шаблона проекта (app|compojure|luminus...)> <название проекта>
Это создает каталог с проектом Clojure, имеющую все необходимые файлы и каталоги. И конечно же создает файл project.clj в котором и подключаются все библиотеки и выставляются глобальные настройки: библиотек, проекта, компиляции, repl и т.п…
(defproject test "0.1.0-SNAPSHOT"
:description "Описание"
:url "http://test.ru"
:dependencies [[org.clojure/clojure "1.7.0"]
[selmer "0.8.2"]
[com.taoensso/timbre "4.0.2"]
[com.taoensso/tower "3.0.2"]
[markdown-clj "0.9.67"]
[environ "1.0.0"]
[compojure "1.3.4"]
[ring/ring-defaults "0.1.5"]
[ring/ring-session-timeout "0.1.0"]
[metosin/ring-middleware-format "0.6.0"]
[metosin/ring-http-response "0.6.2"]
[bouncer "0.3.3"]
[prone "0.8.2"]
[org.clojure/tools.nrepl "0.2.10"]
[buddy "0.6.0"]
[com.novemberain/monger "2.0.1"]
[org.immutant/web "2.0.2"]
[clojure.joda-time "0.6.0"]]
:min-lein-version "2.0.0"
:uberjar-name "test.jar"
:jvm-opts ["-server"]
;;enable to start the nREPL server when the application launches
;:env {:repl-port 7001}
:main test.core
:plugins [[lein-environ "1.0.0"]
[lein-ancient "0.6.5"]]
:profiles {:uberjar {:omit-source true
:env {:production true}
:aot :all}
:dev {:dependencies [[ring-mock "0.1.5"]
[ring/ring-devel "1.3.2"]
[pjstadig/humane-test-output "0.7.0"]]
:repl-options {:init-ns test.core}
:injections [(require 'pjstadig.humane-test-output)
(pjstadig.humane-test-output/activate!)]
:env {:dev true}}})
Ring
Ring — представляет из себя слой абстракции над HTTP, предоставляя взаимодействие с ним через простой API. Очень успешно применяется при создании модульных приложений.
Примеры использования, можно увидеть на их странице github (ссылка в конце статьи). Я же использую еще одну абстракцию над Ring, которая на мой взгляд более упрощает работу с маршрутами Ring и называется Compojure.
Compojure
Библиотека для маршрутизации Ring, с её помощью можно удобно упаковывать маршруты и использовать их в handler'e проекта.
(defroutes auth-routes
; Очистка сессии
(GET "/logout"
; Мы можем передать запрос контроллеру
; полностью
request
(-> logout-controller))
; Обработчик авторизации через POST запрос
(POST "/login"
; Или передать определенные параметры
[login password]
(login-controller login password))
; Авторизация
; Указан GET запрос и можно вызвать
; представление страницы напрямую
; или упаковать все в контроллер
; из которого будет вызвана функция
; представления страницы
(GET "/login"
request
(view/login-page)))
(defroutes users-routes
; Страница просмотра профиля
(GET "/profile/:login"
; В request в :params уже будет доступно
; значение login с ключом указанным выше :login
request
; Синтаксический сахар под названием ->
; передает первый входящий аргумент
; функции обработчику.
(-> profile-view-controller-GET)))
Не стану приводить пример request'a так как вы все прекрасно понимаете как он выглядит. На этом о Compojure закончено.
Buddy
Библиотека для авторизации и аутентификации пользователей. Упаковывает сессии авторизованных пользователей в HTTP заголовок в свой backend, имеет функции для шифрования паролей и т.п…
Пример шифрования пароля:
(buddy.hashers/encrypt "qwerty")
(defn login-controller
"Авторизация пользователя"
[request]
(let [
; Получить данные из формы
form {:login (get-in request [:form-params "login"])
:password (get-in request [:form-params "password"])}
; Проверить данные на валидность
validate (bouncer/validate form valid/login-validator)
; Обработать ошибки
errors (first validate)
return-errors (fn [message]
(util/return-messages
view/login-page
:error-message message
:data validate))]
; Ошибки при валидации
(if-not errors
; Наличие пользователя с указанным логином
(if (true? (db/user-exist? {:login (:login form)}))
; Получить структуру пользователя
(let [user (db/get-user {:login (:login form)}
[:password])]
; Соответсвие паролей
(if (hashers/check (:password form) (:password user))
(do
; Обновить :visited
(db/update-user
{:login (:login form)}
{:visited (util/date-time)})
; Создать новую сессию
(util/create-session request (:login form) "/"))
; Если пароли не совпали
(return-errors "Неверный пароль")))
; Если логин не найден
(return-errors "Логин не найден"))
; Ошибка при валидации
(return-errors "Проверьте правильность введенных данных"))))
Так-же Buddy позволяет настраивать доступ к страницам в middleware.
(def rules
[{:pattern #"^/user/edit$"
:handler authenticated-user}
(defn on-error
[request response]
{:status 403
:headers {"Content-Type" "text/html"}
:body (str "Нет доступа к " (:uri request) ".<br>" response)})
(defn wrap-restricted
[handler]
(restrict handler {:handler authenticated?
:on-error on-error}))
(defn wrap-identity
[handler]
(fn [request]
(binding [*identity* (or (get-in request [:session :identity]) nil)]
(handler request))))
(defn wrap-auth
[handler]
(-> handler
wrap-identity
(wrap-authentication (session-backend))))
; И пример middleware base:
(defn wrap-base
[handler]
(-> handler
wrap-dev
; Наши правила доступа
(wrap-access-rules {:rules rules :on-error on-error})
; Сама авторизация
wrap-auth
; Сессия
(wrap-idle-session-timeout
{:timeout (* 60 30)
:timeout-response (redirect "/")})
wrap-formats
(wrap-defaults
(-> site-defaults
(assoc-in [:security :anti-forgery] false)
(assoc-in [:session :store] (memory-store session/mem))))
wrap-servlet-context
wrap-internal-error
wrap-uri))
Selmer
HTML шаблонизатор, вдохновленный Django. Позволяет очень гибко работать с данными в HTML шаблонах.
(defn registration-page
"Страница регистрации пользователя"
[]
(render "registration.html"
{:foo [1 2 3 4 5]})))
И сам шаблон:
<ul>
{% for i in foo %}
{{i}}
{% endfor %}
</ul>
Monger
Библиотека для работы с mongodb, вообще Clojure очень удобный инструмент для работы с базами данных, все благодаря его коллекциям. Monger это Clojure MongoDB клиент (суть библиотека), предоставляющий функции, высокого и низкого уровня для взаимодействия с API MongoDB. Библиотека написана весьма лаконично и в то же время предоставляет все необходимое для полноценного использования MongoDB в приложениях. Нельзя не заметить огромный плюс — это очень развернутая и подробная документация на официальном сайте.
(ns test.users.db
(:require monger.joda-time
[monger.collection :as m]
[test.db :refer [db]]))
(def collection "users")
(defn get-user
"Найти пользователя"
([query]
(m/find-one-as-map db collection query))
([query fields]
(m/find-one-as-map db collection query fields)))
; Пример использования:
; Вернет нужные нам поля
(get-user {:login "test"} [:first-name :last-name])
; Вернет весь документ
(get-user {:login "test"})
Про обилие скобок
Скобок много, нужно привыкнуть, но внимательный человек обратит внимание, что их не больше чем фигурных скобок в том-же JavaScript.
Ссылки на описанные библиотеки
Дополнительные ссылки
- Список библиотек Clojure
- Документация к функциям и ядру
- Clojure howto
- Clojure docs
- Задачки по Clojure (на любой уровень сложности)
На этом пожалуй все, в дальнейших статьях если к ним проявится интерес я расскажу про каждую библиотеку в отдельности, про замечательный веб-сервер immutant, ну и конечно же про ClojureScript который удобно использовать при разработке front-end приложений и компилируется он в javascript. Так-же хотелось бы осветить фреймфорк Luminus, который очень сильно помог мне разобраться с веб-разработкой на Clojure. Надеюсь моя, хоть и не всеобъемлющая статья заинтересует вас просмотреть возможности этого замечательного инструмента.
Благодарю, всего вам лучшего!
Комментарии (9)
biophreak
20.07.2015 18:52Отлично, спасибо за статью, я сам задумывался о том, что недостаточно информации про clojure(script) на хабре.
Начал было неспешно писать про то, как готовить изоморфные «фуллстек» приложения на Clojure + ClojureScript + Datomic (Component, Immutant, Om, Weasel, tao, Nashorn, etc...).
Пока руки не доходят дописать. Плюс само приложение есть желание перепилить на om-next. Надеюсь, что осилю до конца лета.
Dominis
20.07.2015 19:00Мне было бы интересно прочитать про использование ClojureScript, сравнение с CoffeeScript/JavaScript и, конечно же, впечатление программиста от использования. Так что, автор, если напишите — лично я буду признателен.
Yeshua Автор
20.07.2015 19:17Это вопрос времени, к сожалению в настоящий момент моих познаний в ClojureScript явно не достаточно, чтобы написать полноценную статью и описать в ней что-либо полезное для уважаемых Хабражителей. Следующая статья будет про mongodb а точнее про библиотеку Monger и про её использование в веб-разработке.
Что касается ClojureScript'а и его сравнения с JavaScript, дабы не плодить здесь ссылок отправил вам сообщением. Для интересующихся — очень содержательно и с юмором о ClojureScript рассказывает Александр Соловьев в своей презентации по ClojureScript.
corvette
Я бы хотел добавить, что библиотеки на Clojure довольно компактные по размерам и доступные для понимания. В отличии от, например, Spring из Java код Ring и Compojure приятно читать и он, что удивительно, простой. Мне лично для работы с Clojure помог сайт http://www.4clojure.com/ всем начинающим я рекомендую порешать задачки оттуда.
NikitOS9
Это точно, пришлось копаться в прожекте на Spring, ощущение что там пишут код ради кода, вначале все «по полочкам разложат», тучу классов хелперов, dao, репозиториев наколбасят, есть несколько разных аннотаций которые делают одно и тоже, но разные типа чтобы понимать для каких мест они используются и тд… в итоге логики самого приложения не видно за всем этим
vsb
А мне код Spring показался одним из самых продуманных и хорошо структурированных исходников, которые я когда-либо видел. На вкус и цвет товарищей нет, видимо. Сколько я в Spring ни залазил, всегда легко разбирался, что и как там надо сделать.
Вот про Hibernate могу сказать обратное. Написано вроде неплохо, но очень много кода плохо документировано, по крайней мере было лет 5 назад.