Building CRUD Applications In Clojure - Meetup

3y ago
30 Views
2 Downloads
514.07 KB
55 Pages
Last View : 3d ago
Last Download : 3m ago
Upload by : Karl Gosselin
Transcription

Building CRUD applications in Clojurehttps://github.com/danielytics/diy-crud/

What do we need? Request Handler Routing Database Access Templating

DIY CRUD Request Handler Routing Database Access TemplatingRingCompojure, bidi, pedestal, silk, .Yesql, korma, lots of others for noSQL databasesEnlive, enliven, selmer, hiccup, clostache, .Lots of options!

DIY CRUD Request Handler Routing Database Access TemplatingRingBidiYesqlErinite/template

DIY CRUD Request Handler Routing Database Access TemplatingWhy ring? Defacto standardRingBidiYesqlErinite/template

DIY CRUD Request Handler Routing Database Access TemplatingRingBidiYesqlErinite/templateWhy bidi? Routes are data Reversible (URL - route, route - URL) Clojure and Clojurescript

DIY CRUD Request Handler Routing Database Access TemplatingWhy Yesql? Works with JDBC Write SQL as SQLRingBidiYesqlErinite/template

DIY CRUD Request Handler Routing Database Access TemplatingRingBidiYesqlErinite/templateWhy Erinite/template? Because I wrote it ;-) USP: template manipulation through data Will go into more detail later in this talk

DIY CRUDsetup a project lein new crud-talk; cd crud-talk; vim project.clj:dependencies [[org.clojure/clojure “1.7.0”][ring “1.4.0-RC1”][bidi “1.20.0”][yesql "0.4.0“ :exclusions [instaparse]][instaparse “1.4.1”] ; For clj 1.7 support[org.xerial/sqlite-jdbc "3.7.2"][hiccup "1.0.5"][erinite/template “0.2.0”]]:plugins [[lein-ring "0.9.6"]]:ring {:handler crud-talk.core/handler}

DIY CRUDcreate handler vim src/crud-talk/core.clj(ns crud-talk.core(:require[bidi.ring :as bidi-ring][yesql.core :refer [defqueries]][erinite.template.core :as t][hiccup.core :refer [html]]));; Other code goes here.(def handler (bidi-ring/make-handler routes))

DIY CRUDcreate our routes(def routes["/" {"items"{{:request-method :get} read-items{:request-method :post} create-item}["item/" t-method{:request-method:get} read-item:put} update-item:post} delete-item:delete} delete-item}}])

DIY CRUDcreate our routes(def routes["/" {"items"{{:request-method :get} read-items{:request-method :post} create-item}So wecan testin browser without using od{:request-method{:request-method:get} read-item:put} update-item:post} delete-item:delete} delete-item}}])

DIY CRUDread a list of items-- name: get-all-items-- Read a summary list of all items in database.SELECT id, name, quantityFROM items

DIY CRUDcreate database queries(def db-spec {:classname "org.sqlite.JDBC":subprotocol "sqlite":subname "db.sqlite"})(defqueries “queries.sql”)

DIY CRUDcreate database queriesMake sure you have a database setup: sqlite db.sqlitesqlite create table items (id int, name text, description text, qunantityint);sqlite insert into items values (1, ‘Test item’, ‘A test item. The first one’,5);sqlite insert into items values (2, ‘Another item’, ‘A test item. Thesecond one’, 26);sqlite insert into items values (3, ‘Item’, ‘The third item’, 3);

DIY CRUDcreate template(def item-list-template[:div[:h1 "Item List"][:table.items[:tr[:td.id] [:td.name] [:td.quantity][:td [:form {:method “post”} [:button {:type "submit"} "delete"]]]]][:hr] [:div "New item"][:form#new {:action "/items“ :method "post"}[:input.name {:type "text"}][:input.description {:type "text"}][:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])

DIY CRUDtransform template(def item-list-transformations{[:.items][:clone-for :items][:.items :.id][:content :id][:.items :.name][:content :name][:.items :.quantity] [:content :quantity][:.items :form][:set-attr :action :url]})

DIY CRUDcompile template(def -list-transformation))

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDthe data({:id 1:name “Test item”:quantity 5}{:id 2:name “Another item”:quantity 26}{:id 3:name “Item”:quantity 3})

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDthe data({:id 1:name “Test item”:url “/item/1”:quantity 5}{:id 2:name “Another item”:url “/item/2”:quantity 26}{:id 3:name “Item”:url “/item/3”:quantity 3})

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDthe data{:items ({:id 1:name “Test item”:url “/item/1”:quantity 5}{:id 2:name “Another item”:url “/item/2”:quantity 26}{:id 3:name “Item”:url “/item/3”:quantity 3})

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDthe data[:div {}[:h1 {} "Item List"][:table {:class “items”}[:tr {}[:td {:class “id”} 1][:td {:class “name”} “Test Item”][:td {:class “quantity”} 5][:td {}[:form {:action “/item/1” :method “post”}[:button {:type "submit“} "delete"]]]]].

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDthe data div h1 Item List /h1 table class “items” tr td class “id” 1 /td td class “name” Test Item /td td class “quantity” 5 /td td form action “/item/1” method “post” button type "submit“ delete /button /form /td /tr .

DIY CRUDitem list handler(defn read-items [request](let [items (map#(assoc % :url (str "/item/" (:id %)))(get-all-items db-spec))]{:status 200:body (html (item-list {:items items}))}))

DIY CRUDtest run! lein ring serverNow open localhost:3001/items

DIY CRUDErinite/template crash course(def item-list-template[:div[:h1 "Item List"][:table.items[:tr[:td.id] [:td.name] [:td.quantity][:td [:form {:method “post”} [:button {:type "submit“} "delete"]]]]][:hr] [:div "New item"][:form#new {:action "/items“ :method "post"}[:input.name {:type "text"}][:input.description {:type "text"}][:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])

DIY CRUDErinite/template crash course(def item-list-template[:div[:h1 "Item List"][:table.items[:tr[:td.id] [:td.name] [:td.quantity][:td [:form {:method “post”} [:button {:type "submit“} "delete"]]]]][:hr] [:div "New item"][:form#new {:action "/items“ :method "post"}[:input.name {:type "text"}][:input.description {:type "text"}][:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])[:.items][:clone-for :items]

DIY CRUDErinite/template crash course(def item-list-template[:div[:h1 "Item List"][:table.items[:tr[:td.id] [:td.name] [:td.quantity][:td [:form {:method “post”} [:button {:type "submit“} "delete"]]]]][:hr] [:div "New item"][:form#new {:action "/items“ :method "post"}[:input.name {:type "text"}][:input.description {:type "text"}][:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])[:.items][:clone-for :items]

DIY CRUDErinite/template crash course{:items ({:id 1:name “Test item”(def item-list-template:url “/item/1”[:div:quantity 5}[:h1 "Item List"][:table.items{:id 2[:tr:name “Another item”[:td.id] [:td.name] [:td.quantity]:url “/item/2”[:td [:form {:method “post”} [:button {:type "submit"}"delete"]]]]]:quantity26}[:hr] [:div "New item"]{:id 3[:form#new {:action "/items“ :method "post"}:name “Item”[:input.name {:type "text"}]:url “/item/3”[:input.description {:type "text"}]:quantity 3})[:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])[:.items][:clone-for :items]

DIY CRUDErinite/template crash course(def item-list-template[:div[:h1 "Item List"][:table.items[:tr[:td.id] [:td.name] [:td.quantity][:td [:form {:method “post”} [:button {:type "submit“} "delete"]]]]][:hr] [:div "New item"][:form#new {:action "/items“ :method "post"}[:input.name {:type "text"}][:input.description {:type "text"}][:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])[:.items :.id][:content :id]

DIY CRUDErinite/template crash course{:id 1:name “Test item”:url “/item/1”:quantity 5}(def item-list-template[:div[:h1 "Item List"][:table.items[:tr[:td.id] [:td.name] [:td.quantity][:td [:form {:method “post”} [:button {:type "submit“} "delete"]]]]][:hr] [:div "New item"][:form#new {:action "/items“ :method "post"}[:input.name {:type "text"}][:input.description {:type "text"}][:input.quantity {:type "text"}][:button {:type "submit"} "add"]]])[:.items :.id][:content :id]

DIY CRUDdetour: erinite/template-stylesheetsDetour Time!Erinite/template comes with a companion library: erinite/template-stylesheets

DIY CRUDdetour: erinite/template-stylesheetsDetour Time!Erinite/template comes with a companion library: erinite/template-stylesheets{[:.items][:.items :.id][:.items :.name][:.items :.quantity][:.items :form][:clone-for :items][:content :id][:content :name][:content :quantity][:set-attr :action :url]}

DIY CRUDdetour: erinite/template-stylesheetsDetour Time!Erinite/template comes with a companion library: erinite/template-stylesheets{[:.items][:.items :.id][:.items :.name][:.items :.quantity][:.items :form][:clone-for :items][:content :id][:content :name][:content :quantity][:set-attr :action :url]}

DIY CRUDdetour: erinite/template-stylesheets.items {clone-for: items;}.items .id {content: id;}.items .name {content: name;}.items .quantity {content: quantity;}.items form {set-attr: action url;}

DIY CRUDdetour: erinite/template-stylesheets.items {clone-for: items;}.items .id {content: id;}Now {your designers can update your.items .namecontent:name;templatetransformation selectors!}.items .quantity {content: quantity;}.items form {set-attr: action url;}

DIY CRUDdeleting items-- name: delete-item!-- Delete a specific item from the database.DELETE FROM itemsWHERE id :id

DIY CRUDdeleting items(defn delete-item [request](let [item-id (get-in request[:route-params :item-id])](delete-item! db-spec item-id){:status 302:headers {"Location" "/items"}}))

DIY CRUDdeleting items(defn delete-item [request](let [item-id (get-in request[:route-params :item-id])](delete-item! db-spec item-id){:status 302:headers {"Location" "/items"}}))

DIY CRUDcreate our routes(def routes["/" {"items"{{:request-method :get} read-items{:request-method :post} create-item}["item/" t-method{:request-method:get} read-item:put} update-item:post} delete-item:delete} delete-item}}])

DIY CRUDdeleting items(defn delete-item [request](let [item-id (get-in request[:route-params :item-id])](delete-item! db-spec item-id){:status 302:headers {"Location" "/items"}}))

DIY CRUDdeleting items(defn delete-item [request](let [item-id (get-in request[:route-params :item-id])](delete-item! db-spec item-id){:status 302:headers {"Location" "/items"}}))

DIY CRUD.and so on for create and update.

Where to next?LiberatorDefine resources as conditions and actions in a state machine.Very flexibleMakes it easy to be RFC compliantBut is lower level than I’d like

Where to next?YadaJUXT’s new Liberator competitorSimpler and higher level than LiberatorBuilt-in swagger support

Where to next?Erinite/crudThere is no good out-of-the-box CRUD library than I know of. So I planon making one.https://github.com/Erinite/crud

Where to next?Erinite/crudDoesn’t exist yet, so I’m taking feature requests

Where to next?Erinite/crudThe plan so far: Support any database (by implementing a protocol) Data-driven handlers (you define your resources as data, library doesthe rest) Just a handler, so you can mount it at any route you want

Questions?

DIY CRUD create database queries Make sure you have a database setup: sqlite db.sqlite sqlite create table items (id int, name text, description text, qunantity

Related Documents:

Introductory class in Clojure Clojure setup Future steps About UMM CS Clojure at UMM Clojure in CS at UMM Timeline (cont.): Fall 2012: a directed study (Joe Einertson, UMM CS’13) on developing a Clojure programming environment for novice students. Spring 2013: a directed study on creating sample exercises and improving the environment.

Typed Clojure is an optional type system for the Clojure programming language that aims to type check idiomatic Clojure code. This dissertation presents the design of Typed Clojure, formalizes Typed Clojure’s underlying theory, studies its effectiveness in real-world code bases, and proposes several extensions to help address its shortcomings.

Clojure Recipes is not an “introduction to Clojure” book. There are some really brilliant books and online resources that cover this topic area. If you want an in-depth explana-tion of Clojure, then read one of those. This is a “learn by doing” type of book. What This Book Is About Clojure Recipes is about “the weekend project.” It .

Clojure/Java interoperation is found in almost every signi cant Clojure code base. Typed Clojure therefore builds in an understanding of the Java type system and handles interoperation appropriately. Notably, null is a distinct type in Typed Clojure, designed to automatically rule out null-pointer exceptions.

Clojure’s somewhat odd syntax is not the shady outcome of a conspiracy of parentheses manufacturers. Nor is it a completely arbitrary esthetic choice. Clojure’s syntax is an integral part of how the language works. So in this chapter we’re going to look at the two critical functions at the heart of Clojure,

Clojure offers significant advantages for programmers coming to it from other Lisps: Clojure generalizes Lisp’s physical list into an abstraction called a sequence. This preserves the power of lists, while extending that power to a variety of other data structures. Clojure’s reliance on the JVM provides a standard library and a deploy-

Clojure to a useful state. I announced and released the first version of Clojure, as an open source project, in the fall of 2007. I did 100% of the implementation work during this time, and more than 90% through the 1.2 release in 2010. Subsequent to release Clojure benefited greatly from the feedback, suggestions and effort of its community.

like 'normal' Clojure functions. 'run' queries a logic function for results. May return none, one or many results. Mini-Kanren (in Clojure) (run q; some logic functions) 'q' is a logic variable. Mini-Kanren (in Clojure) Binding: Logic variables are not like normal, mutable variables.