Devcards aims to provide a visual REPL experience for ClojureScript. They offer support to Reagent and OM. How can I make devCards work with re-frame?
This is a recurrent issue with re-frame and devcards. The main problem is the globals in re-frame (the main issue is the db, but handlers and subscriptions can be an issue too) that don't play well with the idea of rendering multiple devcards on the same page.
One potential solution is to render each devcard inside of an iframe. Each devcard would be isolated from each other, even though they are contained and visually rendered in a single page. It's probably not the most efficient solution, but it works: I implemented it in my devcards fork, under the iframe branch. It has a couple example devcards using re-frame
Even though it's published in clojars as [org.clojars.nberger/devcards "0.2.3-0-iframe"], it needs some work to provide a more friendly way to create iframe devcards and maybe a devcard macro specific for re-frame. Also there might be some UI rough edges to polish. But feel free to use it. Of course contributions and feedback are welcome.
I'll put an example here to show how to use it:
(defcard-rg re-frame-component-initialize-db
"This is the same re-frame component, but now using
data-atom to initialize the db, rendered in an iframe:"
(fn [data-atom _]
(setup-example-1)
(re-frame/dispatch [:initialize-db #data-atom])
[re-frame-component-example])
{:guest-name "John"}
{:iframe true})
(the example is based on re-frame 0.7.x but everything should work the same with newer versions because the iframe mechanism is indifferent to using re-frame or anything)
Update:
This how I did it with figwheel main:
Add [devcards "0.2.6" ] to your dependencies.
Create a namespace for your cards, say src/cljs/cards/core.cljs.
Add new extra-main-files section and turn devcards on in dev.cljs.edn
^{:watch-dirs ["src/cljs" "test"]
:css-dirs ["resources/public/css"]
:auto-testing true
:extra-main-files {:testing {:main menu-planner.test-runner}
:devcards {:main cards.core}} ;; <- this
:open-url false}
{:main menu-planner.core
:infer-externs true
:devcards true ;; <- and this
}
Add cards with defcard-rg to src/cljs/cards/core.cljs
(ns cards.core
(:require
[devcards.core]
[re-frame.core :as re-frame])
(:require-macros
[devcards.core :refer [defcard-rg]]))
(devcards.core/start-devcard-ui!)
(defcard-rg no-state
[:div {:style {:border "10px solid blue" :padding "20px"}}
[:h1 "Composing Reagent Hiccup on the fly"]
[:p "adding arbitrary hiccup"]])
(defcard-rg with-state
(fn [data-atom _]
[:div "test"])
{:initial-data "Ta-ta!"})
Run figwheel-main with the forementioned profile (dev).
Go to devcards
They say you can't at the first page:
re-frame remains a work in progress and it falls short in a couple of
ways - for example it doesn't work as well as we'd like with devcards
Related
I'm using tools.namespace to provide smart reloading of namespaces on the REPL. However, when calling refresh or refresh-all, it throws an error.
user=> (require '[clojure.tools.namespace.repl :as tn])
user=> (tn/refresh)
:reloading (ep31.common ep31.routes ep31.config ep31.application user ep31.common-test ep31.example-test)
:error-while-loading user
java.lang.Exception: No namespace: ep31.config, compiling:(user.clj:1:1)
And it seems to end up in this weird state where (require ep31.config) works without an error, but afterwards the namespace isn't actually defined.
I kind of figured this out, this seems to be a combination of circumstances
there were AOT compiled classes left in target/classes from doing lein uberjar previously
tools.namespace doesn't function correctly when loaded namespaces are AOT compiled
target/classes is by default on the classpath
So long story short, if you did a jar/uberjar build before, then remove target/ and things should start working again.
The question I haven't been able to solve yet is why target/classes is on the classpath to begin with. I'm suspecting it's being added by Leiningen, but haven't found yet where or why it's happening.
I learned this the hard way, documentation for :target-path says (https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L309-L313):
;; All generated files will be placed in :target-path. In order to avoid
;; cross-profile contamination (for instance, uberjar classes interfering
;; with development), it's recommended to include %s in in your custom
;; :target-path, which will splice in names of the currently active profiles.
:target-path "target/%s/"
I guess there has to be legacy reasons that :target-path "target/%s/" isn't the default.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
I'm struggling to get my head around how to use Stuart Sierra's component library within a Clojure app. From watching his Youtube video, I think I've got an OK grasp of the problems that led to him creating the library; however I'm struggling to work out how to actually use it on a new, reasonably complex project.
I realise this sounds very vague, but it feels like there's some key concept that I'm missing, and once I understand it, I'll have a good grasp on how to use components. To put it another way, Stuart's docs and video go into the WHAT and WHY of components in considerable detail, but I'm missing the HOW.
Is there any sort of detailed tutorial/walkthrough out there that goes into:
why you'd use components at all for a non-trivial Clojure app
a methodology for how you'd break down the functionality in a non-trivial Clojure app, such that components can be implemented in a reasonably optimal fashion. It's reasonably simple when all you've got is e.g. a database, an app server and a web server tier, but I'm struggling to grasp how you'd use it for a system that has many different layers that all need to work together coherently
ways to approach development/testing/failover/etc. in a non-trivial Clojure app that's been built using components
Thanks in advance
In short, Component is a specialized DI framework. It can set up an injected system given two maps: the system map and the dependency map.
Let's look at a made-up web app (disclaimer, I typed this in a form without actually running it):
(ns myapp.system
(:require [com.stuartsierra.component :as component]
;; we'll talk about myapp.components later
[myapp.components :as app-components]))
(defn system-map [config] ;; it's conventional to have a config map, but it's optional
(component/system-map
;; construct all components + static config
{:db (app-components/map->Db (:db config))
:handler (app-components/map->AppHandler (:handler config))
:server (app-components/map->Server (:web-server config))}))
(defn dependency-map
;; list inter-dependencies in either:
;; {:key [:dependency1 :dependency2]} form or
;; {:key {:name-arg1 :dependency1
;; :name-arg2 :dependency2}} form
{:handler [:db]
:server {:app :handler})
;; calling this creates our system
(def create-system [& [config]]
(component/system-using
(system-map (or config {})
(dependency-map)))
This allows us to call (create-system) to create a new instance of our entire application when we need one.
Using (component/start created-system), we can run a system's services it provides. In this case, it's the webserver that's listening on a port and an open db connection.
Finally, we can stop it with (component/stop created-system) to stop the system from running (eg - stop the web server, disconnect from db).
Now let's look at our components.clj for our app:
(ns myapp.components
(:require [com.stuartsierra.component :as component]
;; lots of app requires would go here
;; I'm generalizing app-specific code to
;; this namespace
[myapp.stuff :as app]))
(defrecord Db [host port]
component/Lifecycle
(start [c]
(let [conn (app/db-connect host port)]
(app/db-migrate conn)
(assoc c :connection conn)))
(stop [c]
(when-let [conn (:connection c)]
(app/db-disconnect conn))
(dissoc c :connection)))
(defrecord AppHandler [db cookie-config]
component/Lifecycle
(start [c]
(assoc c :handler (app/create-handler cookie-config db)))
(stop [c] c))
;; you should probably use the jetty-component instead
;; https://github.com/weavejester/ring-jetty-component
(defrecord Server [app host port]
component/Lifecycle
(start [c]
(assoc c :server (app/create-and-start-jetty-server
{:app (:handler app)
:host host
:port port})))
(stop [c]
(when-let [server (:server c)]
(app/stop-jetty-server server)
(dissoc c :server)))
So what did we just do? We got ourselves a reloadable system. I think some clojurescript developers using figwheel start seeing similarities.
This means we can easily restart our system after we reload code. On to the user.clj!
(ns user
(:require [myapp.system :as system]
[com.stuartsierra.component :as component]
[clojure.tools.namespace.repl :refer (refresh refresh-all)]
;; dev-system.clj only contains: (def the-system)
[dev-system :refer [the-system]])
(def system-config
{:web-server {:port 3000
:host "localhost"}
:db {:host 3456
:host "localhost"}
:handler {cookie-config {}}}
(def the-system nil)
(defn init []
(alter-var-root #'the-system
(constantly system/create-system system-config)))
(defn start []
(alter-var-root #'the-system component/start))
(defn stop []
(alter-var-root #'the-system
#(when % (component/stop %))))
(defn go []
(init)
(start))
(defn reset []
(stop)
(refresh :after 'user/go))
To run a system, we can type this in our repl:
(user)> (reset)
Which will reload our code, and restart the entire system. It will shutdown the exiting system that is running if its up.
We get other benefits:
End to end testing is easy, just edit the config or replace a component to point to in-process services (I've used it to point to an in-process kafka server for tests).
You can theoretically spawn your application multiple times for the same JVM (not really as practical as the first point).
You don't need to restart the REPL when you make code changes and have to restart your server
Unlike ring reload, we get a uniform way to restart our application regardless of its purpose: a background worker, microservice, or machine learning system can all be architected in the same way.
It's worth noting that, since everything is in-process, Component does not handle anything related to fail-over, distributed systems, or faulty code ;)
There are plenty of "resources" (aka stateful objects) that Component can help you manage within a server:
Connections to services (queues, dbs, etc.)
Passage of Time (scheduler, cron, etc.)
Logging (app logging, exception logging, metrics, etc.)
File IO (blob store, local file system, etc.)
Incoming client connections (web, sockets, etc.)
OS Resources (devices, thread pools, etc.)
Component can seem like overkill if you only have a web server + db. But few web apps are just that these days.
Side Note: Moving the-system into another namespace reduces the likelihood of refreshing the the-system var when developing (eg - calling refresh instead of reset).
the :injections keyword is really useful. However, I am hoping to dynamically install a couple of functions in core for debugging purposes. How can this be done?
:injections [(require 'spyscope.core)
(use '[cemerick.pomegranate :only (add-dependencies)])
(use '[clojure.tools.namespace.repl :only (refresh)])]
Ideally, I would want refresh to stay around to that I can use it everywhere
You could use intern for this purpose, although I suspect there might be a better way to have debugging functions available all the time. I've used intern with clojure.core the times that I wanted to mess around with existing functions to learn stuff, but injecting functions in other namespaces feels too hackish.
(intern 'clojure.core 'refresh (fn [] (println "refreshed!"))
(ns another-ns)
(refresh)
;=> refreshed!
And in your project.clj you can use the :repl-options key, specifically :init. This, though, depends on the workflow you have in mind, since the function will not be available in all the namespaces that already exist when the REPL fires up, because they all have already refered the public vars in clojure.core.
You could however, call (clojure.tools.namespace.repl/refresh) once, when the REPL starts, to get all namespaces reloaded and then the function should be available from then on. I just tried the following and it seems to work:
:repl-options {:init (do (require 'clojure.tools.namespace.repl)
(intern 'clojure.core 'refresh clojure.tools.namespace.repl/refresh)
(clojure.tools.namespace.repl/refresh))}
I have a fairly standard Quil file that I am editing with Emacs and nrepl.
(defn setup []
(qc/smooth)
(qc/frame-rate 24)
(qc/background 200))
(defn draw []
(draw-world))
(qc/defsketch run
:title "Circles!"
:setup setup
:draw draw
:size [800 600]
:renderer :opengl)
To start with, I use C-c C-l to load the file; this creates a sketch window. I then edit my draw-world function to, say, draw in a different color. My question is:
How do I update the current Quil window with this new function?
*C-x C-e doesn't seem to work.
Try C-M-x (this evals the current top-level form) in the function you want to change or C-c C-k (this evals the current buffer) in the source buffer. Btw, C-x C-e should be working too (it certainly works for me, but I rarely use it). Maybe you're not using nrepl.el's latest version?
I just set up a sample project to handle my workflow for live-coding in Quil. I copied some basics from several places, such as the Quil wiki and forums.
If you look at the basic core.clj file of the project, you'll see it requires separate "draw" and "setup" namespaces:
(ns basic-metronome.core
(:use [basic-metronome.setup :only [HEIGHT WIDTH]])
(:require [basic-metronome.draw :as dynamic-draw]
[basic-metronome.setup :as dynamic-setup]
[quil.core :as qc]))
(defn run-sketch []
(qc/defsketch the-sketch
:title "Hello Metronome"
:setup dynamic-setup/setup
:draw dynamic-draw/draw
:size [WIDTH HEIGHT]))
From:
https://github.com/mudphone/basic_quil_metronome/blob/master/src/basic_metronome/core.clj
In this way, I can re-evaluate C-c C-k the draw.clj file without having to re-evaluate the top-level core namespace (which can cause problems, such as the one you describe where you're seeing a new window).
I used to like to include all of clojure.contrib, and require all the libraries. This makes find-doc useful as a discovery tool.
Nowadays (clojure 1.4) clojure.contrib is split into many sub-libraries. And that rather spoils my scheme, and it also means that I am constantly having to restart the JVM every time I need a new library.
So I'm busy constructing a project.clj file with many lines:
[org.clojure/algo.generic "0.0.6"]
....
[org.clojure/data.xml "0.0.4"]
....
So that I can get leiningen to put every clojure contrib library on the classpath, whether I need them or not.
And I reckon that this is going to be a spectacular pain in the neck, what with the version numbers, and all.
And I wonder if anyone has a better way to do the same thing?
EDIT: Thinking about it, if there's a web page somewhere that has a list of library names and current versions, I can turn that into a project file fairly easily.
You could use pomegranate if you just want to run it in the REPL (which seems like it would be the only appropriate use case, right?). You can have it look up the latest versions using the Maven Central API. I think this is better than maintaining some sort of dependencies project, generated or otherwise.
(require '[cemerick.pomegranate :refer [add-dependencies]])
(add-dependencies
:coordinates '[[clj-http "0.5.8"]]
:repositories {"clojars" "http://clojars.org/repo"})
(require '[clj-http.client :as client])
;; contrib project names from https://github.com/clojure
(def contrib ["tools.nrepl" "tools.trace" "tools.namespace" "tools.macro"
"test.generative" "math.numeric-tower" "core.match" "core.logic"
"data.priority-map" "core.contracts" "tools.cli" "java.jmx"
"java.jdbc" "java.classpath" "data.xml" "data.json" "core.unify"
"core.incubator" "core.cache" "algo.monads" "data.generators"
"core.memoize" "math.combinatorics" "java.data" "tools.logging"
"data.zip" "data.csv" "algo.generic" "data.codec"
"data.finger-tree"])
(defn add-contrib-dependencies
"look up the latest version of every contrib project in maven central,
and add them as dependencies using pomegranate."
[project-names]
(add-dependencies
:coordinates
(map (juxt
(comp symbol (partial format "org.clojure/%s"))
(fn [proj]
(Thread/sleep 100)
(-> "http://search.maven.org/solrsearch/select?q=%s&rows=1&wt=json"
(format proj)
(client/get {:as :json})
:body :response :docs first :latestVersion)))
project-names)))
Now you can just invoke this function on the list of project names:
user=> (add-contrib-dependencies contrib)
{[org.clojure/data.zip "0.1.1"] nil,
[org.clojure/java.classpath "0.2.0"] nil,
[org.clojure/core.cache "0.6.2"] nil, ...}
UPDATE: as suggested earlier, I had made this answer into a library. It can be used either as nREPL middleware or invoked manually from a running REPL session. The code can be found at https://github.com/rplevy/contrib-repl, where usage instructions can also be found.
I feel your pain. It should be helpful http://dev.clojure.org/display/community/Where+Did+Clojure.Contrib+Go