06 февраля 2011

Майкла Сандберг. Начало работы с compojure.


Перевод статьи. Оригинал

Цель данной статьи заключается в написании простого "списка задач"с помощью clojure и compojure. Я буду использовать Lein, поэтому если у Вас его еще нет перейдите на сайт проекта и следуйте инструкциям.
Когда он установлен давайте начнем с:

lein new SimpleTasks

Эта команда создат нам новый проект.
Давайте начнем с добавления некоторых зависимостей, которые нам необходимы. Откройте SimpleTasks/project.clj и добавьте следующие строки:

Сохраните файл и вызовите "lein deps". Команда скачает все необходимые нам зависимости. Если Вы не хотите использовать lein, enclojure в netbeans, например, отлично работает. Прим. переводчика: советую использовать всегда lein, который с помощью плагинов расширяется до генерации файлов проектов для "гламурных" eclipse, netbeans Без lein Вам придеться идти на clojure.org и искать/скачивать/подключать все необходимые зависимости вручную.
:dev-dependencies [[swank-clojure “1.2.1”]]
подключает плагин для lein для запуска swank-clojure сервера и подключения slime из emacs. Не хотите, не надо и заполнять. Прим. переводчика: здесь также размещаются плагины для генерации проектов. Например, [lein-eclipse "1.0.0"].

Теперь давайте программировать!

Откройте src/SimpleTasks/core.clj в вашем любимом редакторе. Я использую emacs с clojure-mode и slime. Но подойдет что угодно.

Вы увидите:

(ns SimpleTasks.core)

Теперь давайте запустим первую вебстраницу!



Это базовая настройка простого вебприложения compojure.
Если вы это запустите и загрузите ваш броузер по ссылке localhost:8080 Вы увидите "Hello!"
Если Вы не знаете как это запустить, простой способ это сделать: запустить "lein repl", подождать, а затем:
clojure.core=> (require ’SimpleTasks.core)

А что собственно делает этот код?

(defonce server (run-jetty #’myroutes
{:join? false
:port 8080}))

Запускает экземпляр jetty на порту 8080
join? false, заставляет вернуться в repl, вместо блокирующего вызова jetty.
#’myroutes сообщает compojure, какая функция должна быть вызвана, когда сервер получает запрос.

(defroutes myroutes
(GET “/” [] (display)))

Здесь мы определяем наши маршруты, как вы уже заметили, которые используются сервером jetty. Все вызовы "/", например "localhost:8080/" перенаправляются к функции display

(defn display []
(html [:h1 “hello!”]))
Обращение к серверу заканчивается в этой функции. Все что она делает, это отображает html страницу с "hello!" в >h1< теге.

Теперь, для лучшего понимания clojure и compojure отредактируйте функцию display так, чтобы она отображала другое, например:
(defn display []
(html [:h1 “hello world!”]))

Для того, чтобы compojure использовал новую версию функции, все что Вам необходимо, это скомпилировать эту функцию, и обновить страницу в броузере.

Ну что же, давайте приступим к "списку задач". Первое, что нам необходимо это форма для добавления задач:

Мы начнем с добавления другой функции к маршрутам:


И вот она render-form функция:


Примечание: Вы должны добавить: hiccup.form-helpers для в список зависимостей в декларации имени пространства.

Теперь, скомпилируйте defroutes и функция render-form будет размещаться по адресу http://localhost:8080/form вашем броузере.
Если вы используете repl вы можете использовать (require ’SimpleTasks.core :reload) для перекомпиляции.
Ваш core.clj сейчас должен выглядеть так: http://gist.github.com/555496

Попробуйте напечатать что-нибудь в редактор и нажать на submit. Вы получите ошибку "Not found" в Вашем броузере.
Это потому, что все маршруты у нас "GET", а форма использует "POST"

Итак мы должны добавить еще один маршрут:

(POST “/form” {params :params} (handle-form (params “task”)))

Примечание: если Вы хотите получать все виды запросов, Вы должны использовать (ANY "/" (myfunction))
{params :params} предаставляет нам словарь параметров из формы. Сейчас нам нам необходим только параметр "task". Мы берем его и посылаем в handle-form.

Давайте проверим, что форма работает и просто выведем "задачу":

(defn handle-form [task]
(html [:h2 task]))

Загрузите http://localhost:8080/form и что-нибудь введите. Вы должны увидеть "задачу, которую ввели.

В нормальном "списке задач", Вы должны сохранять "задачи" в базе данных или текстовом файле. Для упрощения, а также расширения сознания мы будем использовать "сессии".

Мы начнем с сохранения результата из формы ввода:

(defn handle-form [task]
{:body (str “stored” task)
:session {:tasks task}})

Вместо использованной ранее функции (html) мы определяем body и session. После этого, "задача" будет сохранена под ключем :tasks в пользовательской сессии.

Дополнительно мы должны настроить ring/compojure для сохранения данных в сессии.

(wrap! myroutes :session)

Мы также хотим способ просмотра, что сохраненного в сессии, и это очень просто. Мы начнем с добавления нового маршрута:

(GET “/view” {session :session} (view session))

Простой GET маршрут - это машрут, как вы уже заметили, по которому мы берем сессию и отображаем ее.

Функция, которая вызывается:

(defn view [session]
(html [:h1 (str "tasks: " (:tasks session))]))

Мой полный исходный код:
http://gist.github.com/557879

Просто сохранение одной "задачи" долго не праработает. Время для расширения наших задач и сохранения их в вектор.
Это значит, что мы хотим добавить больше "задач" в сессию, а не перезаписывать старую задачу новой.
Мы будем использовать простой вектор в качестве контейнера. Для того, чтобы все получилось нам необходим не только параметр из формы, но и теущая сессия. Мы изменим на POST маршрут:

(POST “/form” {session :session, params :params} (handle-form (params “task”) session)))

Теперь у нас есть сессия и параметр "задача" из формы.
Мы изменим обработчик формы:

Данная функция, кроме печати задачи из формы и задач из сессии, также проверяет если текущее значение сессии это вектор, то задача добавляется в вектор текущих задач, иначе, когда нет задач, создается новый вектор.

Нам ничего не надо менять в нашем маршруте просмотра.
Попробуйте это! Теперь мы можем добавлять несколько "задач" в наш простой "менеджер задач"!

Время сделать так чтобы просмотр вылядел как реальный список задач.
Мы пойдем простым путем. Добавьте hiccup.page-helpers в Ваш проект.
Просто оберните наш вектор "задач" в (unordered-list).



Теперь у нас есть простой список задач. Вот как это у меня выглядит после добавления трех задач.

Tasks


task1
task2
task3

Ну, хорошего по-немножку.
Нам еще осталось чего сделать. Мы не можем пометить задачи как завершенные или удаленные. Позже мы будет также использовать jquery для упрощения нашего кода.

Как перезагрузить пространство, если вы не используете emacs.
(require ’[foo.something :as something] :reload)
(require ’[foo.something :as something] :reload-all)

Комментариев нет:

Отправить комментарий