KLIPSE: how we built it?
source link: https://blog.klipse.tech/clojure/2016/03/21/klipse-tutorial.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
In a precedent post you can read the explanation about what is KLIPSE and what was the motivation to build it. In this post, you will find a basic tutorial to explain step by step how KLIPSE is build.
Final render of the tutorial:
Of course, the design of the app not the subject of the tutorial.
Tutorial Summary
1- Init Project
Create a new project directory, switch into it, add a project.clj
configuration file to it.
mkdir cljs-compiler
cd cljs-compiler
touch project.clj
(defproject cljs-compiler "0.1.0-SNAPSHOT"
:description "Eval clojurescript to javascript!"
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.170"]]
:plugins [[lein-cljsbuild "1.1.2" :exclusions [[org.clojure/clojure]]]]
:cljsbuild {:builds
[{:id "dev"
:source-paths ["src"]
:compiler {
:main cljs-compiler.core
:asset-path "js"
:output-to "resources/public/js/main.js"
:output-dir "resources/public/js"}}]})
Create a resources/public/index.html
file with the following contents:
mkdir -p resources/public
touch resources/public/index.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Eval clojurescript to javascript!</title>
</head>
<body>
<script src="js/main.js"></script>
</body>
</html>
Create a file src/cljs_compiler/core.cljs
with the following contents:
mkdir -p src/cljs_compiler
touch src/cljs_compiler/core.cljs
(ns cljs_compiler.core)
(enable-console-print!)
(println "Hello world!")
lein cljsbuild once dev
Now open the index.html
file in a chrome browser, open the console, great! you can see our “Hello world!” log.
2- figwheel
It is a good idea to use figwheel to make your devlopment more funny. Add figwheel plugin to your project.clj
and :figwheel true
configuration to your dev compilation configuration:
(defproject cljs-compiler "0.1.0-SNAPSHOT"
:description "Eval clojurescript to javascript!"
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.170"]]
:plugins [[lein-figwheel "0.5.0-6"]
[lein-cljsbuild "1.1.2" :exclusions [[org.clojure/clojure]]]]
:cljsbuild {:builds
[{:id "dev"
:source-paths ["src"]
:figwheel true
:compiler {
:main cljs-compiler.core
:asset-path "js"
:output-to "resources/public/js/main.js"
:output-dir "resources/public/js"}}]})
Launch figwheel from the terminal:
lein figwheel
rlwrap lein figwheel
Open http://localhost:3449/
in chrome, open console, there is your “Hello world!” log. Now, update core.cljs
:
(ns cljs_compiler.core)
(enable-console-print!)
(defn hello [] (println "Hello world!"))
And in the terminal, in the figwheel session:
(in-ns 'cljs_compiler.core)
(hello)
;; repl => nil
;; chrome console => "Hello world!"
3- compiler functions
Add to dependencies the cljs.js
namespace.
Learn more about cljs.js
here.
(ns cljs_compiler.core
(:require
[cljs.js :as cljs]))
You’ll need a callback function as util to handle compilation errors.
(defn callback [{:keys [value error]}]
(let [status (if error :error :ok)
res (if error
(.. error -cause -message)
value)]
[status res]))
The function _compilation
will receive a clojurescript source as a string.
Return [:error “reason”] in case of error and [:ok “js-code”] in case of success.
(defn _compilation [s]
(cljs/compile-str
(cljs/empty-state)
s
callback))
The function _eval
will receive a clojurescript source as a string and evaluate it.
Return [:error “reason”] in case of error and [:ok “js-code”] in case of success.
(defn _eval [s]
(cljs/eval-str
(cljs/empty-state)
s
'test
{:eval cljs/js-eval}
callback))
The function _evaluation-js
return the jsonify _eval
result.
(defn _evaluation-js [s]
(let [[status res] (_eval s)]
[status (.stringify js/JSON res nil 4)]))
The function _evaluation-clj
return the stringify _eval
result.
(defn _evaluation-clj [s]
(let [[status res] (_eval s)]
[status (str res)]))
In this step you can test the compilation functions using the repl.
(in-ns 'cljs_compiler.core)
(_compilation "(+ 1 2"))
;; => [:ok "(2 + 2);\n"]
4- om.next
Om.next is a great client framework based on React. It wasn’t necessary to use it for this small app but it was a cute opportunity to discover it.
Add om.next dependencies to project.clj
{
;; ...
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.170"]
[org.omcljs/om "1.0.0-alpha22"]]
;; ...
}
and to core.cljs
(ns cljs_compiler.core
(:require
[cljs.js :as cljs]
[goog.dom :as gdom]
[om.next :as om :refer-macros [defui]]
[om.dom :as dom]))
You can use an atom to store your local data
(defonce app-state (atom
{:input ""
:compilation ""
:evaluation-js ""
:evaluation-clj ""}))
a read function to read your local data
(defn read [{:keys [state]} key params]
{:value (get @state key "")})
a mutation multimethod to mutate your local data
(defmulti mutate om/dispatch)
(defmethod mutate 'input/save [{:keys [state]} _ {:keys [value]}]
{:action (fn []
(swap! state assoc :input value))})
(defmethod mutate 'cljs/compile [{:keys [state]} _ {:keys [value]}]
{:action (fn []
(swap! state update :compilation
(partial _compilation value)))})
(defmethod mutate 'js/eval [{:keys [state]} _ {:keys [value]}]
{:action (fn []
(swap! state update :evaluation-js
(partial _evaluation-js value)))})
(defmethod mutate 'clj/eval [{:keys [state]} _ {:keys [value]}]
{:action (fn []
(swap! state update :evaluation-clj
(partial _evaluation-clj value)))})
a parser to say om.next what is your read/mutate functions
(def parser (om/parser {:read read
:mutate mutate}))
a reconciler to merge the result of your mutations to your local data.
(def reconciler
(om/reconciler
{:state app-state
:parser parser}))
an om transact function, adding for convenience
(defn process-input [compiler s]
(om/transact! compiler
[(list 'input/save {:value s})
(list 'cljs/compile {:value s})
(list 'js/eval {:value s})
(list 'clj/eval {:value s})]))
and of course test all that using the repl:
(in-ns 'cljs_compiler.core)
(process-input reconciler "(+ 2 2)")
(parser {:state app-state} '[:input
:compilation
:evaluation-js
:evaluation-clj])
;; => {:input "(+ 2 2)",
;; :compilation [:ok "(2 + 2);\n"],
;; :evaluation-js [:ok "4"],
;; :evaluation-clj [:ok "4"]}
OK! now you need UI components: Build an om component that contains 4 textarea, one for input and one for each compile/eval results.
(defui CompilerUI
static om/IQuery
(query [this]
'[:compilation :evaluation-js :evaluation-clj])
Object
(render [this]
(as->
(om/props this) $
(dom/div nil
(input-ui this)
(compile-cljs-ui $)
(evaluate-clj-ui $)
(evaluate-js-ui $)))))
and the 4 textareas
(defn input-ui [reconciler]
(dom/section nil
(dom/textarea #js {:autoFocus true
:onChange #(process-input
reconciler
(.. % -target -value))})))
(defn compile-cljs-ui [{:keys [compilation]}]
(let [[status result] compilation]
(dom/section nil
(dom/textarea #js {:value result
:readOnly true}))))
(defn evaluate-clj-ui [{:keys [evaluation-clj]}]
(let [[status result] evaluation-clj]
(dom/section nil
(dom/textarea #js {:value result
:readOnly true}))))
(defn evaluate-js-ui [{:keys [evaluation-js]}]
(let [[status result] evaluation-js]
(dom/section nil
(dom/textarea #js {:value result
:readOnly true}))))
Go to http://localhost:3449/, you have an awesome clojurescript compiler !!! Now let’s go! fork it!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK