update example todomvc app
This commit is contained in:
parent
6542fab155
commit
2e429bb5c0
41
examples/todomvc/README.md
Normal file
41
examples/todomvc/README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Reagent Data Views Example - Todo MVC
|
||||
|
||||
This is a modification of the Todo MVC app for Reagent [demonstrated here][1].
|
||||
This version of the app has been modified to use a PostgreSQL database
|
||||
to store the Todos and to provide realtime synchronization of changes
|
||||
to that data to any number of users currently viewing the app.
|
||||
|
||||
[1]: http://reagent-project.github.io/
|
||||
|
||||
## Running
|
||||
|
||||
Since Reagent Data Views and the Views library it depends on are all
|
||||
currently in somewhat of an experimental / pre-beta state right now,
|
||||
you will need to first clone the following repositories and manually
|
||||
install the libraries via `lein install`:
|
||||
|
||||
* [views](https://github.com/gered/views)
|
||||
* [views-sql](https://github.com/gered/views-sql)
|
||||
* [reagent-data-views](https://github.com/gered/reagent-data-views)
|
||||
|
||||
As well, you can install [views-honeysql](https://github.com/gered/views-honeysql)
|
||||
if you want to try out using HoneySQL instead of SQL with views. But
|
||||
this example app does not use it so it's not required.
|
||||
|
||||
Once these libraries are install, you can simply build the ClojureScript:
|
||||
|
||||
$ lein cljsbuild once
|
||||
|
||||
And then start up a REPL and run:
|
||||
|
||||
(-main)
|
||||
|
||||
And a new browser window should open to the app.
|
||||
|
||||
Alternatively, to build and run in one go:
|
||||
|
||||
$ lein rundemo
|
||||
|
||||
Open up a second browser and make changes by adding or deleting a Todo,
|
||||
or marking them as completed, etc. and see that the changes are
|
||||
instantly propagated to all clients.
|
|
@ -1,50 +1,56 @@
|
|||
(defproject todomvc "0.1.0-SNAPSHOT"
|
||||
:dependencies [[org.clojure/clojure "1.6.0"]
|
||||
[org.clojure/clojurescript "0.0-2371"]
|
||||
[compojure "1.2.1"]
|
||||
[ring "1.3.1"]
|
||||
[ring/ring-defaults "0.1.3" :exclusions [javax.servlet/servlet-api]]
|
||||
[net.thegeez/clj-browserchannel-jetty-adapter "0.0.6"]
|
||||
[clj-browserchannel-messaging "0.0.4"]
|
||||
[clj-pebble "0.2.0"]
|
||||
[cljs-ajax "0.3.3"]
|
||||
[reagent "0.5.0-alpha"]
|
||||
[reagent-data-views "0.1.0-SNAPSHOT"]
|
||||
[views "0.5.0"]
|
||||
[org.clojure/java.jdbc "0.3.5"]
|
||||
[org.postgresql/postgresql "9.2-1003-jdbc4"]
|
||||
[honeysql "0.4.3"]
|
||||
[environ "1.0.0"]]
|
||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/clojurescript "1.8.51"]
|
||||
[ring "1.4.0"]
|
||||
[ring/ring-defaults "0.2.0" :exclusions [javax.servlet/servlet-api]]
|
||||
[compojure "1.4.0"]
|
||||
[org.immutant/web "2.1.4"]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.0.3"]]
|
||||
[org.clojure/java.jdbc "0.6.1"]
|
||||
[org.postgresql/postgresql "9.4.1208.jre7"]
|
||||
[gered/clj-browserchannel "0.3.1"]
|
||||
[gered/clj-browserchannel-immutant-adapter "0.0.3"]
|
||||
[gered/views "1.5-SNAPSHOT"]
|
||||
[gered/views-sql "0.1.0-SNAPSHOT"]
|
||||
[reagent-data-views "0.2.0-SNAPSHOT"]
|
||||
[reagent-data-views-browserchannel "0.1.0-SNAPSHOT"]
|
||||
|
||||
:main todomvc.server
|
||||
[clj-pebble "0.2.0"]
|
||||
[reagent "0.6.0-alpha2"]
|
||||
[cljs-ajax "0.5.4"]
|
||||
; only being used to get a <meta> tag value with the CSRF token in it
|
||||
[prismatic/dommy "1.1.0"]
|
||||
|
||||
:cljsbuild {:builds
|
||||
{:main
|
||||
{:source-paths ["src"]
|
||||
:compiler
|
||||
{:preamble ["reagent/react.js"]
|
||||
:output-to "resources/public/cljs/client.js"
|
||||
:source-map "resources/public/cljs/client.js.map"
|
||||
:output-dir "resources/public/cljs/client"
|
||||
:optimizations :none
|
||||
:pretty-print true}}}}
|
||||
[environ "1.0.3"]]
|
||||
|
||||
:profiles {:dev {:env {:dev true}}
|
||||
:plugins [[lein-cljsbuild "1.1.3"]
|
||||
[lein-environ "1.0.3"]]
|
||||
|
||||
:uberjar {:env {:dev false}
|
||||
:hooks [leiningen.cljsbuild]
|
||||
:cljsbuild
|
||||
{:jar true
|
||||
:builds
|
||||
{:main
|
||||
{:compiler
|
||||
^:replace
|
||||
{:output-to "resources/public/cljs/client.js"
|
||||
:preamble ["reagent/react.min.js"]
|
||||
:optimizations :advanced
|
||||
:pretty-print false}}}}}}
|
||||
:main todomvc.server
|
||||
|
||||
:aliases {"uberjar" ["do" "clean" ["cljsbuild clean"] "uberjar"]
|
||||
"cljsdev" ["do" ["cljsbuild" "clean"] ["cljsbuild" "once"] ["cljsbuild" "auto"]]})
|
||||
:clean-targets ^{:protect false} [:target-path
|
||||
[:cljsbuild :builds :main :compiler :output-dir]
|
||||
[:cljsbuild :builds :main :compiler :output-to]]
|
||||
:cljsbuild {:builds {:main
|
||||
{:source-paths ["src"]
|
||||
:compiler {:output-to "resources/public/cljs/app.js"
|
||||
:output-dir "resources/public/cljs/target"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true}}}}
|
||||
|
||||
:profiles {:dev {:env {:dev "true"}}
|
||||
|
||||
:uberjar {:env {}
|
||||
:aot :all
|
||||
:hooks [leiningen.cljsbuild]
|
||||
:cljsbuild {:jar true
|
||||
:builds {:main
|
||||
{:compiler ^:replace {:output-to "resources/public/cljs/app.js"
|
||||
:optimizations :advanced
|
||||
:pretty-print false}}}}}}
|
||||
|
||||
:aliases {"rundemo" ["do" ["clean"] ["cljsbuild" "once"] ["run"]]
|
||||
"uberjar" ["do" ["clean"] ["uberjar"]]}
|
||||
|
||||
)
|
||||
|
|
|
@ -5,12 +5,19 @@
|
|||
<title>todomvc with reagent</title>
|
||||
<link rel="stylesheet" href="todos.css">
|
||||
<link rel="stylesheet" href="todosanim.css">
|
||||
|
||||
<!-- include CSRF token that ring's anti-forgery middleware is expecting.
|
||||
clj-browserchannel's client-side init will pick this meta tag up
|
||||
automatically and include the token in all of browserchannel's
|
||||
requests to the server. -->
|
||||
<meta name="anti-forgery-token" content="{{ csrfToken }}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>This will become todomvc when the ClojureScript is compiled</h1>
|
||||
{% if dev %}<script type="text/javascript" src="cljs/client/goog/base.js"></script>{% endif %}
|
||||
{% if dev %}<script src="http://fb.me/react-0.12.1.js"></script>{% endif %}
|
||||
<script type="text/javascript" src="cljs/client.js"></script>
|
||||
<div id="app">
|
||||
<h1>This will become todomvc when the ClojureScript is compiled</h1>
|
||||
</div>
|
||||
{% if dev %}<script type="text/javascript" src="cljs/target/goog/base.js"></script>{% endif %}
|
||||
<script type="text/javascript" src="cljs/app.js"></script>
|
||||
{% if dev %}<script type="text/javascript">goog.require('todomvc.client');</script>{% endif %}
|
||||
<script type="text/javascript">
|
||||
todomvc.client.run();
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
(ns todomvc.client
|
||||
(:require
|
||||
[reagent.core :as reagent :refer [atom]]
|
||||
[clj-browserchannel-messaging.client :as browserchannel]
|
||||
[reagent-data-views.client.core :as rviews]
|
||||
[reagent.core :as r]
|
||||
[ajax.core :refer [POST default-interceptors to-interceptor]]
|
||||
[dommy.core :refer-macros [sel1]]
|
||||
[net.thegeez.browserchannel.client :as browserchannel]
|
||||
[reagent-data-views.client.component :refer [view-cursor] :refer-macros [defvc]]
|
||||
[ajax.core :refer [POST]]))
|
||||
[reagent-data-views.browserchannel.client :as rdv-browserchannel]))
|
||||
|
||||
;; Todo MVC - Reagent Implementation
|
||||
;;
|
||||
;; This is taken from the example code shown on http://reagent-project.github.io/
|
||||
;; It has been modified so that instead of using todo data stored client-side in
|
||||
;; an atom, the data is retrieved from the server.
|
||||
;;
|
||||
;; AJAX requests are used to add/edit/delete the todos. The list is refreshed
|
||||
;; whenever a change is made (by any client currently viewing the app) by a
|
||||
;; view subscription. See the 'todo-app' component near the bottom-middle of this
|
||||
;; file for more details about this.
|
||||
|
||||
|
||||
|
||||
;; AJAX operations
|
||||
|
||||
(defn add-todo [text] (POST "/todos/add" {:format :url :params {:title text}}))
|
||||
(defn toggle [id] (POST "/todos/toggle" {:format :url :params {:id id}}))
|
||||
|
@ -14,8 +30,12 @@
|
|||
(defn complete-all [v] (POST "/todos/mark-all" {:format :url :params {:done? v}}))
|
||||
(defn clear-done [] (POST "/todos/delete-all-done"))
|
||||
|
||||
|
||||
|
||||
;; UI Components
|
||||
|
||||
(defn todo-input [{:keys [title on-save on-stop]}]
|
||||
(let [val (atom title)
|
||||
(let [val (r/atom title)
|
||||
stop #(do (reset! val "")
|
||||
(if on-stop (on-stop)))
|
||||
save #(let [v (-> @val str clojure.string/trim)]
|
||||
|
@ -31,7 +51,7 @@
|
|||
nil)})])))
|
||||
|
||||
(def todo-edit (with-meta todo-input
|
||||
{:component-did-mount #(.focus (reagent/dom-node %))}))
|
||||
{:component-did-mount #(.focus (r/dom-node %))}))
|
||||
|
||||
(defn todo-stats [{:keys [filt active done]}]
|
||||
(let [props-for (fn [name]
|
||||
|
@ -49,7 +69,7 @@
|
|||
"Clear completed " done])]))
|
||||
|
||||
(defn todo-item []
|
||||
(let [editing (atom false)]
|
||||
(let [editing (r/atom false)]
|
||||
(fn [{:keys [id done title]}]
|
||||
[:li {:class (str (if done "completed ")
|
||||
(if @editing "editing"))}
|
||||
|
@ -63,23 +83,48 @@
|
|||
:on-save #(save id %)
|
||||
:on-stop #(reset! editing false)}])])))
|
||||
|
||||
|
||||
|
||||
;; Main TODO app component
|
||||
;;
|
||||
;; Note that this component is defined using 'defvc' instead of 'defn'. This is a
|
||||
;; macro provided by reagent-data-views which is required to be used by any Reagent
|
||||
;; component that will directly subscribe/unsubscribe to views. It handles all the
|
||||
;; housekeeping operations that working with views on the client entails.
|
||||
;;
|
||||
;; The call to 'view-cursor' is where the rest of the magic happens. This function
|
||||
;; will:
|
||||
;;
|
||||
;; - Send a subscription request to the server for the specified view and parameters
|
||||
;; if a subscription for the view (and the exact provided parameters) does not
|
||||
;; already exist.
|
||||
;; - Returns the most recent data for this view in a Reagent cursor. When the data
|
||||
;; is changed and the server sends a view refresh, components dereferencing this
|
||||
;; cursor will be rerendered, just like any other Reagent atom/cursor.
|
||||
;; - If the values of the (optional) parameters passed to view-cursor change, a
|
||||
;; view resubscription (with the new parameters) will be triggered automatically
|
||||
;; and the server will send us new view data.
|
||||
;;
|
||||
;; NOTE:
|
||||
;; view-cursor cannot be used in a Reagent component that was created using defn.
|
||||
|
||||
(defvc todo-app [props]
|
||||
(let [filt (atom :all)]
|
||||
(let [filt (r/atom :all)]
|
||||
(fn []
|
||||
(let [items (view-cursor [:todos])
|
||||
done (->> @items (filter :done) count)
|
||||
(let [items (view-cursor :todos)
|
||||
done (->> @items (filter :done) count)
|
||||
active (- (count @items) done)]
|
||||
[:div
|
||||
[:section#todoapp
|
||||
[:header#header
|
||||
[:h1 "todos"]
|
||||
[todo-input {:id "new-todo"
|
||||
[todo-input {:id "new-todo"
|
||||
:placeholder "What needs to be done?"
|
||||
:on-save add-todo}]]
|
||||
:on-save add-todo}]]
|
||||
(when (-> @items count pos?)
|
||||
[:div
|
||||
[:section#main
|
||||
[:input#toggle-all {:type "checkbox" :checked (zero? active)
|
||||
[:input#toggle-all {:type "checkbox" :checked (zero? active)
|
||||
:on-change #(complete-all (pos? active))}]
|
||||
[:label {:for "toggle-all"} "Mark all as complete"]
|
||||
[:ul#todo-list
|
||||
|
@ -96,9 +141,36 @@
|
|||
[:footer#info
|
||||
[:p "Double-click to edit a todo"]]]))))
|
||||
|
||||
|
||||
|
||||
;; Some unfortunately necessary set up to ensure we send the CSRF token back with
|
||||
;; AJAX requests (clj-browserchannel handles this automatically for it's own HTTP
|
||||
;; requests, so the set up we do is only for our own application code).
|
||||
|
||||
(defn get-anti-forgery-token []
|
||||
(if-let [tag (sel1 "meta[name='anti-forgery-token']")]
|
||||
(.-content tag)))
|
||||
|
||||
(def csrf-interceptor
|
||||
(to-interceptor {:name "CSRF Interceptor"
|
||||
:request #(assoc-in % [:headers "X-CSRF-Token"] (get-anti-forgery-token))}))
|
||||
|
||||
(swap! default-interceptors (partial cons csrf-interceptor))
|
||||
|
||||
|
||||
|
||||
;; Page load
|
||||
|
||||
(defn ^:export run []
|
||||
(browserchannel/init!
|
||||
:on-connect
|
||||
(fn []
|
||||
(rviews/init!)
|
||||
(reagent/render-component [todo-app] (.-body js/document)))))
|
||||
; Configure reagent-data-views and then BrowserChannel.
|
||||
(rdv-browserchannel/configure!)
|
||||
|
||||
; NOTE: We are passing in an empty map for the BrowserChannel event handlers only
|
||||
; because this todo app is not using BrowserChannel for any purpose other
|
||||
; then to provide client/server messaging for reagent-data-views. If we
|
||||
; wanted to use it for client/server messaging in our application as well,
|
||||
; we could pass in any event handlers we want here and it would not intefere
|
||||
; with reagent-data-views.
|
||||
(browserchannel/connect! {} {:middleware [rdv-browserchannel/middleware]})
|
||||
|
||||
(r/render-component [todo-app] (.getElementById js/document "app")))
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
(:require
|
||||
[compojure.core :refer [routes GET POST]]
|
||||
[compojure.route :as route]
|
||||
[net.thegeez.jetty-async-adapter :refer [run-jetty-async]]
|
||||
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]
|
||||
[ring.middleware.anti-forgery :refer [*anti-forgery-token*]]
|
||||
[ring.util.response :refer [response]]
|
||||
[net.thegeez.browserchannel.server :refer [wrap-browserchannel]]
|
||||
[net.thegeez.browserchannel.immutant-async-adapter :refer [wrap-immutant-async-adapter]]
|
||||
[immutant.web :as immutant]
|
||||
[clj-pebble.core :as pebble]
|
||||
[clj-browserchannel-messaging.server :as browserchannel]
|
||||
[environ.core :refer [env]]
|
||||
[honeysql.helpers :refer :all]
|
||||
[clojure.java.jdbc :as sql]
|
||||
[reagent-data-views.server.core :as rviews :refer [views-config]]
|
||||
[views.db.core :refer [vexec! with-view-transaction]]))
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[views.sql.core :refer [vexec! with-view-transaction]]
|
||||
[views.sql.view :refer [view]]
|
||||
[reagent-data-views.browserchannel.server :as rdv-browserchannel]))
|
||||
|
||||
|
||||
(def db {:classname "org.postgresql.Driver"
|
||||
:subprotocol "postgresql"
|
||||
|
@ -21,74 +24,82 @@
|
|||
:password "s3cr3t"})
|
||||
|
||||
|
||||
;; View templates (functions which return HoneySQL SELECT query maps)
|
||||
|
||||
(defn ^:refresh-only todos-view []
|
||||
(-> (select :id :title :done)
|
||||
(from :todos)))
|
||||
;; View functions.
|
||||
;;
|
||||
;; These are functions which accept any number of parameters provided when the view
|
||||
;; is subscribed to and run whenever a subscriber needs refreshed data for it.
|
||||
;;
|
||||
;; A view function's return value requirement depends on what views IView
|
||||
;; implementation is being used.
|
||||
;;
|
||||
;; This example app is using views-sql, so view templates should return a SQL SELECT
|
||||
;; query in a clojure.java.jdbc "sqlvec" which is a vector where the first string is
|
||||
;; the actual SQL query and is followed by any number of parameters to be used in
|
||||
;; the query.
|
||||
|
||||
(defn get-todos []
|
||||
["SELECT id, title, done FROM todos ORDER BY title"])
|
||||
|
||||
|
||||
|
||||
;; Views list.
|
||||
;;
|
||||
;; A definition/declaration of the views in the system. Each view is given an id and
|
||||
;; points to a function that returns the query that will be used to retrieve the view's
|
||||
;; data. Also other properties can be provided to each view, such as the database connection.
|
||||
;;
|
||||
;; The view id and parameters to the view function get used later on to form a
|
||||
;; "view signature" or "view-sig" when the client subscribes to a view.
|
||||
|
||||
; the keys in this map are the names of the views, which we can refer to in our cljs code
|
||||
; (the key + args to the view fn combined make up a "view signature" or "view-sig"
|
||||
; e.g. [:items] in this case as it has zero arguments.
|
||||
(def views
|
||||
{:todos {:fn #'todos-view}})
|
||||
[(view :todos db #'get-todos)])
|
||||
|
||||
|
||||
;; SQL operations (affecting the views defined above, so we use vexec! instead of jdbc calls)
|
||||
|
||||
;; SQL operations triggered by AJAX requests.
|
||||
;;
|
||||
;; These functions are just your ordinary AJAX request handlers that do the various
|
||||
;; CRUD operations on the example app's data. The only difference is that instead
|
||||
;; of using clojure.java.jdbc/execute!, we instead use vexec!.
|
||||
;;
|
||||
;; vexec! performs the exact same operation as execute!, except that it also
|
||||
;; analyzes the SQL query being run and dispatches "hints" to the view system which
|
||||
;; trigger view refrehses for all subscribers of the views that the hints match.
|
||||
|
||||
(defn add-todo! [title]
|
||||
(vexec!
|
||||
@views-config
|
||||
(-> (insert-into :todos)
|
||||
(values [{:title title}])))
|
||||
(response "added todo"))
|
||||
(vexec! db ["INSERT INTO todos (title) VALUES (?)" title])
|
||||
(response "ok"))
|
||||
|
||||
(defn delete-todo! [id]
|
||||
(vexec!
|
||||
@views-config
|
||||
(-> (delete-from :todos)
|
||||
(where [:= :id id])))
|
||||
(response "deleted todo"))
|
||||
(vexec! db ["DELETE FROM todos WHERE id = ?" id])
|
||||
(response "ok"))
|
||||
|
||||
(defn update-todo! [id title]
|
||||
(vexec!
|
||||
@views-config
|
||||
(-> (update :todos)
|
||||
(sset {:title title})
|
||||
(where [:= :id id])))
|
||||
(response "updated todo"))
|
||||
(vexec! db ["UPDATE todos SET title = ? WHERE id = ?" title id])
|
||||
(response "ok"))
|
||||
|
||||
(defn toggle-todo! [id]
|
||||
; note that we could have written this operation using a single UPDATE query,
|
||||
; but writing it this way also serves to demonstrate:
|
||||
; - using transactions with vexec!
|
||||
; - that the db connection is available under :db in the views config map and can
|
||||
; be used to run any ordinary query directly with jdbc (of course)
|
||||
(with-view-transaction [vt @views-config]
|
||||
(let [done? (:done (first (sql/query (:db vt) ["SELECT done FROM todos WHERE id = ?" id])))]
|
||||
(vexec!
|
||||
vt
|
||||
(-> (update :todos)
|
||||
(sset {:done (not done?)})
|
||||
(where [:= :id id]))))
|
||||
(response "toggled todo")))
|
||||
; note that a transaction is obviously not necessary here as we could have used
|
||||
; just a single UPDATE query. however, it is being done this way to demonstrate
|
||||
; using transactions with vexec!.
|
||||
(with-view-transaction
|
||||
[dt db]
|
||||
(let [done? (:done (first (jdbc/query dt ["SELECT done FROM todos WHERE id = ?" id])))]
|
||||
(vexec! dt ["UPDATE todos SET done = ? WHERE id = ?" (not done?) id]))
|
||||
(response "ok")))
|
||||
|
||||
(defn mark-all! [done?]
|
||||
(vexec!
|
||||
@views-config
|
||||
(-> (update :todos)
|
||||
(sset {:done done?})))
|
||||
(response "completed all todos"))
|
||||
(vexec! db ["UPDATE todos SET done = ?" done?])
|
||||
(response "ok"))
|
||||
|
||||
(defn delete-all-done! []
|
||||
(vexec!
|
||||
@views-config
|
||||
(-> (delete-from :todos)
|
||||
(where [:= :done true])))
|
||||
(response "deleted all done todos"))
|
||||
(vexec! db ["DELETE FROM todos WHERE done = true"])
|
||||
(response "ok"))
|
||||
|
||||
|
||||
;; Compojure routes / Jetty server & main
|
||||
|
||||
;; Compojure routes and Ring handler
|
||||
|
||||
(def app-routes
|
||||
(routes
|
||||
|
@ -100,23 +111,46 @@
|
|||
(POST "/todos/mark-all" [done?] (mark-all! (Boolean/parseBoolean done?)))
|
||||
(POST "/todos/delete-all-done" [] (delete-all-done!))
|
||||
|
||||
(GET "/" [] (pebble/render-resource "html/app.html" {:dev (env :dev)}))
|
||||
; main page
|
||||
(GET "/" [] (pebble/render-resource
|
||||
"html/app.html"
|
||||
{:dev (boolean (env :dev))
|
||||
:csrfToken *anti-forgery-token*}))
|
||||
|
||||
(route/resources "/")
|
||||
(route/not-found "not found")))
|
||||
|
||||
(defn run-server []
|
||||
(browserchannel/init!
|
||||
:middleware [rviews/views-middleware])
|
||||
(rviews/init! db views)
|
||||
|
||||
(def handler
|
||||
(-> app-routes
|
||||
(browserchannel/wrap-browserchannel)
|
||||
(wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))
|
||||
(run-jetty-async
|
||||
{:port 8080
|
||||
:auto-reload? true
|
||||
:join? false}))
|
||||
(println "Web app is running at http://localhost:8080/"))
|
||||
(wrap-defaults site-defaults)
|
||||
; NOTE: We are passing in an empty map for the BrowserChannel event handlers only
|
||||
; because this todo app is not using BrowserChannel for any purpose other
|
||||
; then to provide client/server messaging for reagent-data-views. If we
|
||||
; wanted to use it for client/server messaging in our application as well,
|
||||
; we could pass in any event handlers we want here and it would not intefere
|
||||
; with reagent-data-views.
|
||||
(wrap-browserchannel {} {:middleware [rdv-browserchannel/middleware]})
|
||||
(wrap-immutant-async-adapter)))
|
||||
|
||||
|
||||
|
||||
;; Web server startup & main
|
||||
|
||||
(defn run-server []
|
||||
(pebble/set-options! :cache (env :dev))
|
||||
|
||||
; init-views takes care of initialization views and reagent-data-views at the same
|
||||
; time. As a result, we do not need to also call views.core/init! anywhere. The
|
||||
; same options you are able to pass to views.core/init! can also be passed in here
|
||||
; and they will be forwarded along.
|
||||
;
|
||||
; if you need to shutdown the views system (e.g. if you're using something like
|
||||
; Component or Mount), you can just call views.core/shutdown!.
|
||||
(rdv-browserchannel/init-views! views)
|
||||
|
||||
(if (env :dev)
|
||||
(immutant/run-dmc #'handler {:port 8080})
|
||||
(immutant/run #'handler {:port 8080})))
|
||||
|
||||
(defn -main [& args]
|
||||
(run-server))
|
||||
|
|
Loading…
Reference in a new issue