èªå·±éçºçšã«æ§æãããŠããå Žåã¯ãèšäºã®èªã¿åãããã€ãã¹ããã³ãŒããç解ããããšã«ãããããŒãžã®æåŸã§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éçºã«ã€ããŠãäŒããç¶ããŸãã ããã§ç§ã¯ããªãã«å¥ããåããããªãã«ãã¹ãŠã®æé«ãé¡ã£ãŠããŸãïŒ