On Github babysnakes / ayler-presentation
Notes from my (almost) first clojure project.
~
~
what are my take aways from the project?
Don't forget to mention:
~
This was where I started to look for such a solution. I borrowed the layout concept from this.~
Searching for a better implementation of the namespace browser I found clj-ns-btowser. I had to run it in separate repl because of the many version clashes with my project.~
~
First project using angularjs and JavaScript. Liked them both.~
e.g. what happens when deleting or renaming a function. It still part of the namespace until I restart the JVM.(defmacro var-route [route] (if (development?) `(var ~route) route))
~
Since I'm constantly changing function names I found myself running lein test constantly.The only global variable in the application. Should hold all the data in the application.
(defn system "Returns a new instance of the application." [] {:settings {:port 5000}})
(defn init "Construct development env." [] (alter-var-root #'system (constantly (-> (app/system) (assoc :remote [6001 "localhost"])))))system can accept parameters.
(defn start "Start all components of the application. Returns the updates system." [system] (when-let [level (:log-level system)] (timbre/set-level! level)) (when-let [remote (:remote system)] (apply client/set-remote remote)) (let [server (run-server app (:settings system))] (timbre/info (str "Ayler started on port " (get-in system [:settings :port]))) (assoc system :stop-server-fn server)))
(defn stop "Stops all components of the application. Returns the updated system." [system] (if-let [stop-server-fn (:stop-server-fn system)] (stop-server-fn)) (assoc system :remote (client/extract-remote)))
:profiles {:dev {:source-paths ["src/dev"] :dependencies [[ring-mock "0.1.5"] [org.clojure/tools.namespace "0.2.4"] [org.clojure/java.classpath "0.2.1"]]}
(ns user (:require [ayler.app :as app] ... [clojure.tools.namespace.find :refer (find-namespaces-in-dir)])) (def system nil) (defn init ...) (defn start ...) (defn stop ...) (defn go "Initialize and run" [] (init) (start)) (defn reset [] (stop) (refresh :after 'user/go)) (defn run-all-tests ...)If this file has an error the repl fails to start. Can workaround by just calling a different namespace which contains all these methods.
~
~
Hardly ever use exceptions
Indicate errors with data structures
(defn eval-on-remote-nrepl "Evaluates the op and code on the remote nrepl. ..." [op code] (if (empty? @_remote) {:status :not-connected} (try #_(...) (with-open [conn (apply repl/connect @_remote)] #_(...)) (catch java.net.ConnectException e (do #_(...) {:status :disconnected})))))
~
State is usually in the database or session
Every layer is in charge of it's own state
Nearly all of the functions are pure
In every layer there's a few messy functions that collect all the required state.
~
"functional core imperative shell"(defonce ^:private _remote (atom [])) ;; ... getter and setter
(defn- var-doc "Returns the docstring of a fully qualified var name" [namespace var] (-> (construct-varname namespace var) (queries/query-docstring)))
(defroutes routes #_(...) (GET "/api/doc/:namespace/:var" [namespace var] (response (var-doc namespace var))) #_(...))Although this is not much of an example, think of the construct-varname method as verifying input and fetching user from database and only then it is passed to the function that does something with it (purely).
Stuart Sierra - Clojure in the Large
Announced today - Component - Framework for managing lifecycle of stateful objects.
He returns to the big data structure pattern. Also discuss about the possibility of holding transaction in data structures (barybernhardt), etc...Input anybody?
Test only the pure methods
Rely on the fact that the non-pure methods are really simple
About 0.5 to 1 test/code ratio
Compared to 1 to 1 javascript test/code ratio
Integration tests
~
Talk about the difference from ruby testing religion. compared to 5/1 test/code ratio in my first rails app.Ring-json (There are more fully featured solutions - liberator)
(defroutes routes #_(...) (POST "/api/disconnect/" _ (response (client/disconnect)))) (def app (-> routes wrap-json-response wrap-json-params))
Protection against CSRF with customized ring-anti-forgery (submitted PR)
~
On POST the params are shown as regular params.{ "status": "done", "response": "([x])\n Returns a number one greater than num. Does not auto-promote\n longs, will throw on overflow. See also: inc'" }
{ "status": "error", "response": "IllegalArgumentException No such namespace: clojure.coree clojure.lang.Var.find (Var.java:153)\n" }
~
The error is the direct response from nrepl.apiClient.handleResponse = function(response, handler) { switch(response.status) { case "disconnected": $rootScope.$broadcast("connect", {disconnected: true}); case "not-connected": $rootScope.$broadcast("connect"); case "done": handler(response.response); break; case "error": apiClient.handleError(response.response); break; default: alert("Unknown response: " + response); break; }; };Parsing responses.
Questions?