ClojureのWebアプリケヌション。 パヌト2

こんにちは、芪愛なるナヌザヌずHabrの蚪問者。 Clojure Webアプリケヌションの最初の蚘事では、 Clojure Webプロゞェクトを構築するための基本的なツヌルずラむブラリに぀いお怜蚎したした。 すなわち、ラむンニンゲン、リング、コンポゞュレ、モンガヌ、バディ、セルマヌ。 ここでは、実際のアプリケヌションに焊点を圓おたす。



自己開発甚に構成されおいる堎合は、蚘事の読み取りをバむパスするコヌドを理解するこずにより、ペヌゞの最埌でGithubぞのプロゞェクトリンクを求めたす。



はじめに


それでは、順番に始めたしょう。 もっず面癜くするために、私は倚かれ少なかれ蚘事の焊点を圓おるこずにしたした。 今日は、Clojureで簡単なWebアプリケヌションを䜜成したす。これにより、メモが管理されたす。 Leiningen、Clojure、MongoDBが既にむンストヌルされおいるず想定しおいたす有効にするこずを忘れないでください。 コンテンツの倧郚分は、コヌド内のコメントに盎接含たれおおり、䟿宜䞊、ネタバレの䞭に隠されおいたす。






IDE


Clojureにはさたざたな゚ディタヌずIDEがありたすが、この蚘事では長所ず短所を説明せず、䞀床も時間がありたせん。 奜みはそれぞれ異なりたすが、䜿甚するものはあなた次第です。 私はClojureScriptで曞かれおおり、それに完党に満足しおいるLightTableを䜿甚したす。倚くのモゞュヌルがありたす。ClojureずClojureScriptで開発を開始するために必芁なものはすべお揃っおおり、ParEditモゞュヌルがありたす。 replによっおプロゞェクトにリモヌトたたはロヌカルで接続するために、䜕も構成する必芁はありたせん。 LightTableのreplずの盞互䜜甚は非垞に独特で、私の䞻芳的な意芋では非垞に䟿利です-関数を呌び出しお、ラむブモヌドたずえば、Emacsや他のすべおのIDEで別のりィンドりに結果を衚瀺したり、コヌドで同じこずを盎接実行したり、倉換したりできたす匏の最初たたは最埌のブラケットにカヌ゜ルを眮き、cmd + EnterMacOSを抌したす。LightTableがrepl接続を䜜成しおこの匏をコンパむルした埌、コンパむルされた関数の名前たたは䞋の倉数行を入力し、その結果をコヌドで盎接衚瀺するだけです 。






バック゚ンド



プロゞェクト


たず、プロゞェクトを䜜成したしょう $ lein new compojure notes



これで、アプリケヌションが空癜のカタログができたした。 そこに行き、project.cljファむルを゚ディタヌで開きたしょう。 䜿甚するラむブラリぞの䟝存関係を远加する必芁がありたす。



project.clj
(defproject notes "0.1.0-SNAPSHOT" :description " " :min-lein-version "2.0.0" :dependencies [; -,  Clojure   ;   [org.clojure/clojure "1.6.0"] ;   GET  POST  [compojure "1.3.1"] ;  (middleware)   ;  [ring/ring-defaults "0.1.5"] ;  [selmer "0.8.2"] ;  Monger [com.novemberain/monger "2.0.1"] ;    [clojure.joda-time "0.6.0"]] ;  -     ;  ,     ; Ring'     - Jetty :plugins [[lein-ring "0.8.13"]] ;    Ring  ;   app  ;        :ring {:handler notes.handler/app} :profiles {:dev {:dependencies [[javax.servlet/servlet-api "2.5"] [ring-mock "0.1.5"]]}})
      
      









プロゞェクトを開始するず、Leiningenは指定されたすべおの䟝存関係を自動的にむンストヌルしたす。必芁なのは、それらをproject.cljで指定し、むンタヌネットに接続するこずだけです。 次に、アプリケヌションのサヌバヌ偎の䜜成に進みたしょう。 衚瀺機胜を配眮し、デヌタベヌス、ハンドラヌ、およびルヌトを別々のファむルに保存しお、名前の競合や頭痛の皮の䞍䟿がないようにする方が論理的ですが、これはあなたが奜きなこずです。






ハンドラヌメむンハンドラヌ


handler.cljから始めたしょう。Leinは既に䜜成しおくれおおり、 / src / notesディレクトリにありたすClojureコヌドもすべお含たれおいたす。 これはアプリケヌションの重芁な郚分です。アプリケヌション倉数ず、HTTPの基本的なミドルりェア芁求および応答ラッパヌを含むapp倉数が含たれおいたす。 アプリケヌションのルヌトを名前空間に远加するず、次のコヌドが取埗されたす。



handler.clj
 (ns notes.handler (:require ;   [notes.routes :refer [notes-routes]] ;   middleware [ring.middleware.defaults :refer [wrap-defaults site-defaults]])) ;    middleware (def app (wrap-defaults notes-routes site-defaults))
      
      












ルヌト


次に、routes.cljファむルを䜜成し、その䞭にルヌトを配眮したす。これは、指定されたURIでGETおよびPOSTメ゜ッドによっおプロンプトが衚瀺されるず、フォヌムハンドラヌの機胜を呌び出し、ペヌゞを衚瀺したす。 アプリケヌションのこの郚分では、Compojure APIを䜿甚したす。 気になる人のためにコヌドの膚倧な郚分をすぐに謝眪したすが、圌らの仕事の論理を理解しやすくするために、コメントをたくさん远加したした



routes.clj
 (ns notes.routes (:require ;    [compojure.core :refer [defroutes GET POST]] [compojure.route :as route] ;   [notes.controllers :as c] ;   [notes.views :as v] ;      [notes.db :as db])) ;   (defroutes notes-routes ;    (GET "/note/:id" [id] ;      ObjectId ;      (let [note (db/get-note id)] (v/note note))) ;      ObjectId (GET "/delete/:id" [id] (c/delete id)) ;    (POST "/edit/:id" request (-> c/edit)) ;    ;  ,   ; ObjectId    ;  ,    ;  . (GET "/edit/:id" [id] ;      ObjectId ;      (let [note (db/get-note id)] (v/edit note))) ;    (POST "/create" ;      ;   [title text],    ; request    ;      request ;     ; : (create-controller request) (-> c/create)) ;    (GET "/create" [] (v/create)) ;    (GET "/" [] ;     ;    fn  (let [notes (db/get-notes)] (v/index notes))) ;  404 (route/not-found "  "))
      
      












コントロヌラヌフォヌム凊理


POST、堎合によっおはGETリク゚ストを凊理するために、䞊蚘のルヌトではいわゆる「コントロヌラヌ」関数ハンドラヌを䜿甚し、それらを別のファむルに配眮したす。 ここでは、別の蚘事に倀するため、入力デヌタの完党な怜蚌を意図的に省略したす。 このファむルの名前はcontrollers.cljで、その内容は次のずおりです。



controllers.clj
 (ns notes.controllers (:require ;   [ring.util.response :refer [redirect]] ;      [notes.db :as db])) (defn delete "  " [id] (do (db/remove-note id) (redirect "/"))) (defn edit "  " [request] ;     (let [note-id (get-in request [:form-params "id"]) note {:title (get-in request [:form-params "title"]) :text (get-in request [:form-params "text"])}] ;   (if (and (not-empty (:title note)) (not-empty (:text note))) ;    ;     ;   ;    (do (db/update-note note-id note) (redirect "/")) ;      "   "))) (defn create "  " [request] ;     ;     ;    hash-map ; ( ) (let [note {:title (get-in request [:form-params "title"]) :text (get-in request [:form-params "text"])}] ;   (if (and (not-empty (:title note)) (not-empty (:text note))) ;    ;     ;   ;    (do (db/create-note note) (redirect "/")) ;      "   ")))
      
      












DBMongoDBずの盞互䜜甚


アプリケヌションの非垞に興味深い郚分であるMongerラむブラリは、その構築に圹立ちたす。 db.cljファむルを䜜成し、MongoDBず察話するための関数を保存したす。 もちろん、ルヌトずコントロヌラヌでMonger関数を盎接呌び出すこずもできたすが、このために、デバッグ、拡匵、倚数の重耇コヌドで報埩を受け、最終的なコヌド行数を増やしたす。 Mongerでは、DSLク゚リリレヌショナルDBMS甚の優れたsqlcormaラむブラリがありたすを䜿甚しおMongoDBにク゚リを䜜成するこずもできたす。これは耇雑なク゚リには非垞に䟿利ですが、この蚘事では説明したせん。 db.cljに関数を远加したしょう



db.clj
 (ns notes.db (:require ;  Monger monger.joda-time ;      [monger.core :as mg] [monger.collection :as m] [monger.operators :refer :all] ;    [joda-time :as t]) ;    Java  (:import org.bson.types.ObjectId org.joda.time.DateTimeZone)) ;        (DateTimeZone/setDefault DateTimeZone/UTC) ;      (defonce db (let [uri "mongodb://127.0.0.1/notes_db" {:keys [db]} (mg/connect-via-uri uri)] db)) ;        (defn- date-time "   " [] (t/date-time)) (defn remove-note "    ObjectId" [id] ;    ObjectId (let [id (ObjectId. id)] (m/remove-by-id db "notes" id))) (defn update-note "    ObjectId" [id note] ;    ObjectId (let [id (ObjectId. id)] ;     $set ;        ;       ;      ;   hash-map +    ;       ;     . ; -    ;   ObjectId   ;  update-by-id, ;      ;    (m/update db "notes" {:id id} ;    ;    {$set (assoc note :created (date-time))}))) (defn get-note "    ObjectId" [id] ;      :_id ;      ;     ObjectId ;   ,  ;     ObjectId (let [id (ObjectId. id)] ;    hash-map   (m/find-map-by-id db "notes" id))) (defn get-notes "  " [] ; Find-maps    ;     hash-map (m/find-maps db "notes")) (defn create-note "   " ;      ;    hash-map c : ; {:title "" :text ""} [note] ; Monger    ObjectId ;     ;     (let [object-id (ObjectId.)] ;     hash-map ;   ,  ;     ObjectId ;       (m/insert db "notes" (assoc note :_id object-id :created (date-time)))))
      
      












ビュヌHTMLテンプレヌトの衚瀺


views.cljファむルには、HTMLテンプレヌトを衚瀺しおデヌタを送信する関数を配眮したす。 Djangoテンプレヌトのデヌタ衚珟システムに觊発されたSelmerラむブラリは、この問題に圹立ちたす。 Selmerでは、テンプレヌト自䜓のデヌタタグを凊理するためのフィルタヌ関数を远加するこずもでき、ナヌザヌ自身に柔軟な蚭定を提䟛したす。 ペヌゞ衚瀺関数を曞きたしょう



views.clj
 (ns notes.views (:require ; "" [selmer.parser :as parser] [selmer.filters :as filters] ;    [joda-time :as t] ;  HTTP  [ring.util.response :refer [content-type response]] ;  CSRF  [ring.util.anti-forgery :refer [anti-forgery-field]])) ;  Selmer     (parser/set-resource-path! (clojure.java.io/resource "templates")) ;     -  (defn format-date-and-time "   " [date] (let [formatter (t/formatter "yyyy-MM-dd  H:m:s" :date-time)] (when date (t/print formatter date)))) ;       (filters/add-filter! :format-datetime (fn [content] [:safe (format-date-and-time content)])) ;           ;     anti-forgery  (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) (defn render [template & [params]] "     html      " (-> template (parser/render-file ;      ;      ;    (assoc params :title " " :page (str template))) ;     HTTP  response (content-type "text/html; charset=utf-8"))) (defn note "  " [note] (render "note.html" ;     {:note note})) (defn edit "  " [note] (render "edit.html" ;     {:note note})) (defn create "  " [] (render "create.html")) (defn index "  .  " [notes] (render "index.html" ;     ;  notes   false {:notes (if (not-empty notes) notes false)}))
      
      












フロント゚ンド



HTMLテンプレヌト


アプリケヌションはほが準備ができおおり、デヌタを衚瀺するHTMLファむルを䜜成したす。 / resourcesディレクトリは、静的ファむルをホストするために必芁です。 .jarたたは.warファむルでアプリケヌションをコンパむルした埌でも、その䞭のファむルを眮き換えるこずができたす。 次回の蚘事では、CSSテヌブルを远加したす。 それたでの間、HTMLファむルを配眮するテンプレヌトディレクトリを䜜成したす。








たず、すべおのテンプレヌトの基本ファむルを䜜成したす。これには、すべおのペヌゞのメむンマヌクアップず、残りのセクションのマヌクアップが配眮されるコンテンツブロックが含たれたす。 始めたしょう



base.html
 <!DOCTYPE html> <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>{{title}}</title> </head> <body> <ul> <li> {% ifequal page "index.html" %} <strong> </strong> {% else %} <a href="/"> </a> {% endifequal %} </li> <li> {% ifequal page "create.html" %} <strong> </strong> {% else %} <a href="/create"> </a> {% endifequal %} </li> </ul> {% block content %} {% endblock %} </body> </html>
      
      












次に、メモを䜜成するためのフォヌムでテンプレヌトを䜜成したす。 その䞭に、アプリケヌションのすべおのフォヌムず同様に、view.cljで䜜成したタグ{csrf-field}を远加する必芁がありたす。远加しないず、フォヌムを送信するず、゚ラヌInvalid anti-forgery tokenが衚瀺されたす。 続行



create.html
 {% extends "base.html" %} {% block content %} <h1> </h1> <form action="POST"> {% csrf-field %} <p> <label></label><br> <input type="text" name="title" placeholder=""> </p> <p> <label></label><br> <textarea name="text"></textarea> </p> <input type="submit" value=""> </form> {% endblock %}
      
      












すでにルヌト、ビュヌ、およびノヌ​​ト線集゚ディタヌがありたす。これらすべおのフォヌムでテンプレヌトを䜜成したしょう。



edit.html
 {% extends "base.html" %} {% block content %} <h1> </h1> <form method="POST"> {% csrf-field %} <input type="hidden" name="id" value="{{note._id}}"> <p> <label></label><br> <input type="text" name="title" value="{{note.title}}"> </p> <p> <label></label><br> <textarea name="text">{{note.text}}</textarea> </p> <input type="submit" value=""> </form> {% endblock %}
      
      












次に、ノヌトを衚瀺するためのテンプレヌトを䜜成したす。小さなタグ内の泚意深いリヌダヌは、デヌタの奇劙な衚珟を芋るこずになりたす。これは、view.cljで䜜成されたフィルタヌ以倖のものです。 Selmerでフィルタヌを呌び出すには、通垞{{variable | filter}}ず蚘述したす。 次に、コヌド自䜓



note.html
 {% extends "base.html" %} {% block content %} <h1>{{note.title}}</h1> <small>{{note.created|format-datetime}}</small> <p>{{note.text}}</p> {% endblock %}
      
      












そしお最埌に、メモのリストが衚瀺されるメむンペヌゞ



note.html
 {% extends "base.html" %} {% block content %} <h1></h1> {% if notes %} <ul> {% for note in notes %} <li> <h4><a href="/note/{{note._id}}">{{note.title}}</a></h4> <small>{{note.created|format-datetime}}</small> <hr> <a href="/edit/{{note._id}}"></a> | <a href="/delete/{{note._id}}"></a> </li> {% endfor %} </ul> {% else %} <strong>  </strong> {% endif %} {% endblock %}
      
      












おわりに


これでアプリケヌションの準備が敎いたした。もちろん、非垞に重芁なステップを芋逃したした-replで関数をテストし、結果を衚瀺したすが、次の蚘事では詳现に説明したす。



実行したしょう $ lein ring server



このコマンドは、lein runず同様に、すべおの䟝存関係をむンストヌルし、localhost3000のコアJetty Webサヌバヌでアプリケヌションを起動したす。 これたでのずころ、アプリケヌションを.jarたたは.warファむルにコンパむルしたり、lein runで実行したりするこずはできたせん。






サむトリンク






次の蚘事では、䞍倉のWebサヌバヌをWebアプリケヌションに远加し、コヌドの自動曎新をオンザフラむで実装したす。぀たり、 ファむルが゚ディタヌに保存されるず、サヌバヌは自動的にファむルを曎新し、珟圚のサヌバヌではなく、ペヌゞがリロヌドされた埌にコヌド倉曎の結果を確認できたす。 珟時点では、オンザフラむで倉曎できるのはHTMLファむルのみです。 たた、HTMLブヌトストラップにクラスを远加したす。これは、蚘事の本質が矎しいデザむンではないにもかかわらず、レむアりトが本圓に面癜くないため、WEB 1.0に戻るべきではないず思うからです。



蚘事を公開する前に綿密なチェックを行っおいたすが、説明の誀りや文法の誀りに気づいおメッセヌゞでお知らせいただければ幞いです。 たた、私の最初の蚘事に興味を瀺しおくれた人々にも感謝したいず思いたす。ClojureでのWeb開発に぀いおお䌝えし続けたす。 これで私はあなたに別れを告げ、あなたにすべおの最高を願っおいたす



All Articles