Недавно я познакомился с интересным языком — clojure. Мне сразу понравились ленивые и иммутабельные коллекции, stm, макросы, обилие скобочек и dsl на все случаи жизни.
И я решил попробовать сделать web-приложение, используя только clojure.
Приложение
Было задумано создать простую искалку субтитров, которая:
- каждые 5 минут индексирует новые субтитры на addicted, notabenoid и других сервисах;
- имеет одностраничный web-интерфейс с поиском без перезагрузки страницы;
- показывает в web-интерфейсе количество проиндексированных субтитров и меняет его при появлении новых;
- имеет простое api для взаимодействия с десктопным клиентом.
Парсеры
На удивление парсеры было писать просто и удобно. Сначала казалось, что скобочек уж очень много, но threading макросы (->, ->>, -<> и -<>> — передача результата аргументом следующему выражению) очень помогали.
Например, кусок парсера notabenoid, делающий одно и тоже на python и clojure:
clojure | python |
|
|
16 скобочек | 14 скобочек |
Для запуска парсеров используется библиотека at-at, для парсинга html — enlive. Результат записывается в elasticsearch.
Серверная часть
Сервер
Как сервер я выбрал http-kit, в основном из-за того, что мне захотелось web-сокетов. И их тут очень просто использовать, например, отправка всем клиентам количества проиндексированных субтитров после обновления будет выглядеть так:
(add-watch total-count :notifications #(doseq [con @subscribers] (send! con (prn-str {:total-count %4}))))
Роутинг
Для роутинга — compojure. Тут нет никаких отличий от django и других популярных фреймворков:
(defroutes main-routes (GET "/" [] (views/index-page)) (GET "/api/list-languages/" {params :params} (api/list-languages params)) (GET "/notifications/" [] push/notifications) (route/resources const/static-path))
API
Так как мы везде используем clojure, то наше api должно возвращать результат в родных структурах данных и в json (для десктопного клиента на python). Библиотеки, которая так может, я не нашёл (уже нашёл), поэтому пришлось немного повелосипедить и изобрести свой мини-dsl:
(defn- get-writer "Get writer from params" [params] (if (= (:format params) "json") json/write-str prn-str)) (defmacro defapi "Define api method" [name doc args & body] `(defn ~name ~args ((get-writer (first ~args)) ~@body)))
И как простой пример использования:
(defapi list-languages "List all available languages" [params] (models/list-languages))
View
Для рендеринга html я воспользовался специальным dsl — hiccup, шаблон с ним выглядит немного «марсианским»:
(defn index-page [] (html5 [:head [:title "Subman - subtitle search service"] [:body [:h1 "Welcome to subman!"]]))
Стили
Для стилей в clojure тоже есть свой dsl — garden. Код с ним выглядит тоже странно:
(defstyles main [:.search-input {:z-index 100 :background-color "#fff"}] [:.info-box {:text-align "center" :font-size (px 18)}] [:.search-result-holder {:padding-left 0 :padding-right 0}])
Клиентская часть
Клиентскую часть я писал не совсем на clojure, а на clojurescript, который в итоге компилируется в javascript. Как фреймворк я использовал reagent — биндинг к react.js для clojure, непроверяющий каждую секунду объекты на изменения (благодаря atom’ам) и использующий hiccup-подобный dsl для описания компонентов:
(defn info-box "Show info box" [text] [:div.container.col-xs-12.info-box [:h2 text]])
Тут всё очень даже хорошо, пока не нужно напрямую работать с js-библиотеками. Например, код для подключения typeahead к полю поиска:
(defn init-autocomplete "Initiale autocomplete" [query langs sources] (let [input ($ "#search-input")] (.typeahead input (js-obj "highlight" true) (js-obj "source" (fn [query cb] (cb (apply array (take const/autocomplete-limit (map #(js-obj "value" %) (get-completion query @langs @sources)))))))) (.on input "typeahead:closed" (fn [] (reset! query (.val input))))))
И даже размер “скомпилированного” файла оказался не таким уж большим — всего 290кб.
Как огромный плюс использования clojure вместе с clojurescript — можно писать один код для клиента и сервера при помощи cljx.
Выводы
Хоть clojure и позволяет разрабатывать web-приложения без знания и использования html, css и javascript, но продакшен-проекты я бы так делать не решился.
ссылка на оригинал статьи http://habrahabr.ru/post/222737/
Добавить комментарий