From 5b72d063c5b494b874037aeb988610b447f576b9 Mon Sep 17 00:00:00 2001 From: Oliver Marks Date: Fri, 29 Jan 2021 12:32:09 +0000 Subject: [PATCH 1/5] Refactor --- .drone.yml | 8 +- .../resources/public/rename-me-index.html | 2 +- reagent-reitit-demo/deps.edn | 6 +- reagent-reitit-demo/dev.cljs.edn | 2 +- .../resources/public/rename-me-index.html | 2 +- reagent-reitit-demo/src/demo/core.cljs | 224 ++++++++++++++++++ reagent-reitit-demo/src/org.cljs | 162 ------------- .../resources/public/rename-me-index.html | 2 +- 8 files changed, 238 insertions(+), 170 deletions(-) create mode 100644 reagent-reitit-demo/src/demo/core.cljs delete mode 100644 reagent-reitit-demo/src/org.cljs diff --git a/.drone.yml b/.drone.yml index 5571165..6cca8f4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,7 +4,9 @@ steps: - name: Build image: clojure:tools-deps commands: - - clj -Adev -m figwheel.main -bo prod + - cd reagent-reitit-demo + - cp resources/public/rename-me-index.html resources/public/index.html + - clj -A:prod - name: deploy-site pull: True @@ -17,6 +19,8 @@ steps: from_secret: ssh_key port: 22 duration: 4m + ## strip components removes parts of path at destination + strip_components: 1 target: /var/www/clojure-demos/ source: - - resources/public/* + - reagent-reitit-demo/resources/public/* diff --git a/datalog-demo/resources/public/rename-me-index.html b/datalog-demo/resources/public/rename-me-index.html index ef36acc..b427263 100644 --- a/datalog-demo/resources/public/rename-me-index.html +++ b/datalog-demo/resources/public/rename-me-index.html @@ -5,7 +5,7 @@ - Atom Juice Merchant Website + Clojure demos diff --git a/reagent-reitit-demo/deps.edn b/reagent-reitit-demo/deps.edn index 02b9b9f..497a6cd 100644 --- a/reagent-reitit-demo/deps.edn +++ b/reagent-reitit-demo/deps.edn @@ -1,13 +1,15 @@ {:deps {org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/clojurescript {:mvn/version "1.10.764"} cljs-ajax {:mvn/version "0.8.1"} + ;; react reagent {:mvn/version "0.9.1"} reagent-utils {:mvn/version "0.3.3"} olymk2/cl-org {:git/url "https://gitlab.com/olymk2/cl-org.git" :sha "c366560dd59e16759ca24209b253f55e46ecbbe3"} - + ;;rouoting metosin/reitit {:mvn/version "0.5.10"} metosin/reitit-spec {:mvn/version "0.5.10"} metosin/reitit-frontend {:mvn/version "0.5.10"} com.bhauman/figwheel-main {:mvn/version "0.2.11"}} - :paths ["src" "resources"]} + :paths ["src" "resources"] + :aliases {:prod {:main-opts ["-m" "figwheel.main" "-bo" "dev"]}}} diff --git a/reagent-reitit-demo/dev.cljs.edn b/reagent-reitit-demo/dev.cljs.edn index 6798d2b..e4f26c2 100644 --- a/reagent-reitit-demo/dev.cljs.edn +++ b/reagent-reitit-demo/dev.cljs.edn @@ -5,4 +5,4 @@ :source-map true :source-map-timestamp true :devcards true - :main core.demo} + :main demo.core} diff --git a/reagent-reitit-demo/resources/public/rename-me-index.html b/reagent-reitit-demo/resources/public/rename-me-index.html index ef36acc..b427263 100644 --- a/reagent-reitit-demo/resources/public/rename-me-index.html +++ b/reagent-reitit-demo/resources/public/rename-me-index.html @@ -5,7 +5,7 @@ - Atom Juice Merchant Website + Clojure demos diff --git a/reagent-reitit-demo/src/demo/core.cljs b/reagent-reitit-demo/src/demo/core.cljs new file mode 100644 index 0000000..2394301 --- /dev/null +++ b/reagent-reitit-demo/src/demo/core.cljs @@ -0,0 +1,224 @@ +(ns ^:figwheel-hooks demo.core + (:require [reagent.core :as reagent] + [ajax.core :refer [GET raw-response-format]] + ;[demo.org :refer [parse->to-hiccup parse-flat]] + [cl-eorg.parser :as o :refer [parse parse-flat]] + [cl-eorg.html :refer [org->replacements]] + [reitit.frontend :as rf] + [reitit.frontend.easy :as rfe] + [reitit.coercion.spec :as rss] + [spec-tools.data-spec :as ds])) + +;; put constant data here +(def site-data + {:demos {:dsl-demo + {:file "dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} + :datalog-demo + {:file "datalog-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} + :reagent-demo + {:file "reagent-reitit.org" :git-link "https://github.com/atomjuice/dsl-demo"}} + :homepage {:intro "Clojure tutorials examples and exploration"} + :lorem "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}) + +;; Store site state +(defonce site-state (reagent/atom {})) + +;; for one component to render an article +(defn article [{:keys [title description tagline]}] + [:article {:data-name "article-full-bleed-background"} + [:div.cf {:style {:background "url(http://placekitten.com/g/600/300)" + :no-repeat "center center fixed" :background-size "cover"}} + [:div.fl.pa3.pa4-ns.bg-white.black-70.measure-narrow.f3.times + [:header.b--black-70.pv4 {:class (when tagline "bb")} + [:h3.f2.fw7.ttu.tracked.lh-title.mt0.mb3.avenir title] + (when tagline [:h4.f3.fw4.i.lh-title.mt0 tagline])] + [:section.pt5.pb4 [:p.times.lh-copy.measure.f5.mt0 description]]]]]) + +;; form one component to render article tiles +(defn articles [{:keys [title body articles]}] + [:section.mw7.center.avenir {:key title} + [:h2.baskerville.fw1.ph3.ph0-l title] + (when body [:p body]) + (map (fn [{:keys [title author link description img-src img-alt]}] + [:article.bt.bb.b--black-10 + [:a.db.pv4.ph3.ph0-l.no-underline.black.dim {:href link} + [:div.flex.flex-column.flex-row-ns + (when img-src + [:div.pr3-ns.mb4.mb0-ns.w-100.w-40-ns + [:img.db {:src img-src :alt img-alt}]]) + [:div.w-100.w-60-ns.pl3-ns + [:h1.f3.fw1.baskerville.mt0.lh-title title] + [:p.f6.f5-l.lh-copy description] + [:p.f6.lh-copy.mv0 author]]]]]) + articles)]) + +;; form one component to render a product +(defn product-card + [{:keys [title amount description link]}] + [:article.br2.ba.dark-gray.b--black-10.ma2.w-100.w-50-m.w-25-l.mw5 + [:img.db.w-100.br2.br--top {:src link}] + [:div.pa2.ph3-ns.pb3-ns + [:div.dt.w-100.mt1 + [:div.dtc [:h1.f5.f4-ns.mv0 title]] + [:div.dtc.tr [:h2.f5.mv0 amount]]] + [:p.f6.lh-copy.measure.mt2.mid-gray description]]]) + +(defn circle [{:keys [img alt]}] + [:div.pa4.tc [:img.br-100.ba.h3.w3.dib {:src img :alt alt}]]) + +;; form one component to render a nav link +(defn navbar-link [{:keys [href title text] :or {text nil title nil}}] + [:a.link.dim.white.dib.mr3 {:key href :href href :title title} text]) + +;; form one component to render a navbar +(defn navbar [links] + [:header.bg-black-90.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l + [:nav.f6.fw6.ttu.tracked + (map navbar-link links)]]) + +;; form2 component notice the render function takes the same param as the component function +;; this is important, you can hit issues if you forget this in form 2 components. +(defn my-component [title value] + (let [local-state (reagent/atom value)] + (fn [title] + [:h1 {:class (when @local-state "hide") + :on-click (fn [] (swap! local-state inc))} (str title " " @local-state)]))) + + +;; form one homepage component + + +(defn home-page [] + [:<> + [articles + {:title "Clojure Demos" + :body (-> site-data :homepage :intro) + :articles [{:title "DSL Demo" + :link (rfe/href ::demo {:page "dsl-demo"}) + :img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"} + {:title "Datalog Demo" + :link (rfe/href ::demo {:page "datalog-demo"}) + :img-src "https://raw.githubusercontent.com/tonsky/datascript/master/extras/logo.svg"} + {:title "Reagent Demo" + :link (rfe/href ::demo {:page "reagent-demo"}) + :img-src "https://raw.githubusercontent.com/reagent-project/reagent/master/logo/logo-text.png"}]}]]) + +;; form two component render demo +(defn demo-page [route] + (let [demo-key (keyword (-> route :parameters :path :page)) + content (reagent/atom {})] + (GET (-> site-data :demos demo-key :file) + {:response-format (raw-response-format) + :handler (fn [response] + (->> response + parse + org->replacements + (reset! content)) + #_(reset! content (org->replacements (parse response))))}) + (fn [route] + [:main.mt4 + [:div.mw7.center.avenir @content]]))) + +;; form one render about page component +(defn about-page [] + [:main.mt4 + [:section.mw7.center.avenir + [:h1 "Clojure library examples to aid learning"] + [:p "Selection of clojure demos, rendered in reagent which itself is an example of using reagent."] + [my-component "Clickable component" 1] + [my-component "Clickable component" 2] + [:p "Image circle componenet"] + [circle {:img "http://placekitten.com/g/300/300" :alt "test"}] + [:p "Product card component"] + [:div.flex.flex-column.flex-row-ns + [product-card + {:title "Cat 01" + :amount "£54.59" + :description "Cat description here" + :link "http://placekitten.com/g/600/300"}] + [product-card + {:title "Cat 02" + :amount "£10.59" + :description "Cat description here" + :link "http://placekitten.com/g/600/300"}]] + [:p "Article component"] + [article {:title "Example Article component" + :description (-> site-data :lorem) + :tagline "https://tachyons.io/components/"}]]]) + +;; form 3 component wrap rendering to catch errors and render them +;; or just render the rest of the page if all is good +;; this uses =create-class= and a hash map of life cycle functions + + +(defn err-boundary + [& children] + (let [err-state (reagent/atom nil)] + (reagent/create-class + {:display-name "ErrBoundary" + :component-did-catch (fn [err info] + (reset! err-state [err info])) + :reagent-render (fn [& children] + (if (nil? @err-state) + (into [:<>] children) + (let [[_ info] @err-state] + [:pre [:code (pr-str info)]])))}))) + +;; define our routes, just nested vectors of the route definition hash maps +(def routes + [["/" + {:name ::frontpage + :my-data "hi" + :view home-page}] + + ["/about" + {:name ::about + :view about-page}] + + ["/demo/:page" + {:name ::demo + :view demo-page + :parameters {:path {:page string?} + :query {(ds/opt :foo) keyword?}}}]]) + +;; top level component contains nav and adds in the select page into a containing element +;; we are adding in a style sheet but this will often be done in index.html +(defn current-page [] + [:<> [:link {:rel "stylesheet" :href "https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"}] + [navbar [{:href (rfe/href ::frontpage) :title "title here" :text "home"} + {:href (rfe/href ::about) :text "About"} + {:href (rfe/href ::i-do-not-exist) :text "missing"}]] + [:main.mt4 + (when-let [view (-> @site-state :current-route :data :view)] [view (-> @site-state :current-route)])]]) + + +;; This simply calls reagent render and puts the result in a div with the id of app +;; you can create your own index.html or figwheel provides one with the app id which will replace the default data +;; ^:after-load is meta data its not needed but informs figwheel to run this code after a page load + + +(defn mount-root-page [] + ;; this select the main node from the html file and injects your page content + (reagent/render + (fn [] [err-boundary [current-page]]) + (.getElementById js/document "app"))) + +(defn ^:after-load render-site [] + ;; this select the main node from the html file and injects your page content + (mount-root-page)) + +(defn startup! [] + (rfe/start! + (rf/router routes {:data {:coercion rss/coercion}}) + (fn [m] (swap! site-state assoc :current-route m)) + ;; set to false to enable HistoryAPI + {:use-fragment true}) + (render-site)) + +;; we defonce the startup so that hot reloading does not reinitialize the state of the site +(def launch (do (startup!) true)) + +(comment + @site-state + + (GET "/test.org" {:handler (fn [response] (swap! site-state assoc :content response))})) diff --git a/reagent-reitit-demo/src/org.cljs b/reagent-reitit-demo/src/org.cljs deleted file mode 100644 index 951fcd7..0000000 --- a/reagent-reitit-demo/src/org.cljs +++ /dev/null @@ -1,162 +0,0 @@ -(ns demo.org - (:require [clojure.string :as str])) - - -(def ESCAPE "\n") - -(def BLANK 0) -(def META 1) -(def META_OTHER 2) -(def HEADER 5) -(def BOLD 10) -(def ITALIC 11) -(def UNDERLINED 12) -(def VERBATIM 13) -(def LIST 20) -(def TEXT 21) -(def IMAGE 22) -(def LINK 23) -(def CAPTION 24) -(def BULLET 25) -(def SOURCE 50) -(def EXAMPLE 51) -(def RESULTS 52) -(def COMMENT 53) -(def TABLE 54) - -(def METADATA ["TITLE" - "AUTHOR" - "EMAIL" - "DESCRIPTION" - "KEYWORDS" - "FILETAGS" - "DATE" - "HTML_DOCTYPE" - "SETUPFILE"]) - -(def t_META #"^[#]\+(?:TITLE|AUTHOR|EMAIL|DESCRIPTION|KEYWORDS|FILETAGS|DATE|HTML_DOCTYPE|SETUPFILE)\:") -(def t_BLANK_LINE #"^\s*$") -(def t_COMMENT_BEGIN #"^\#\+BEGIN_COMMENT") -(def t_COMMENT_END #"^\#\+END_COMMENT") -(def t_EXAMPLE_BEGIN #"^\#\+BEGIN_EXAMPLE") -(def t_EXAMPLE_END #"^\#\+END_EXAMPLE") -(def t_SRC_BEGIN #"^\#\+BEGIN_SRC\s+") -(def t_SRC_END #"^\#\+END_SRC") -(def t_TABLE_START #"^\s*\|") -(def t_TABLE_END #"^(?!\s*\|).*$") -(def t_RESULTS_START #"^\#\+RESULTS\:") -(def t_CAPTIONS #"^\#\+CAPTION:") -(def t_NAME #"^\#\+NAME:") -; t_IMG #"^\[\[(\w|\.|-|_|/)+\]\]$" -(def t_IMG #"^\[\[") -(def t_IMG_END #"\]\]") -(def t_RESULTS_END #"^\s*$") -(def t_END_LABELS #"^(?!\[|\#).*") -(def t_BULLET_START #"^\s*[\+|\-|0-9\.]") -(def t_BULLET_END #"^(?!\s*[\+|\-|0-9\.]).*$") - -(def t_HEADER #"^\*+") -(def t_META_OTHER #"^[#]\+[A-Z\_]+\:") - -(def TYPE_SINGLE 0) -(def TYPE_BLOCK 1) -(def TYPE_ATTRIBUTE 2) - -(defn TokenStruct [values] - (merge {:start "" :end false :type TYPE_SINGLE :start_pos 2 :end_pos nil :count 0 :key ""} values)) - -(def token-map {:token nil :value nil :attrs nil}) - -(defn token - ([token-type value] (token token-type value nil)) - ([token-type value attrs] - {:token token-type :value (or value "") :attrs attrs})) - -(def TOKENS [[:META (TokenStruct {:start t_META, :end_pos -1 :key-fn #(clojure.string/join (drop 2 (butlast %)))})], - [:COMMENT (TokenStruct {:start t_COMMENT_BEGIN, :end t_COMMENT_END, :type TYPE_BLOCK, :end_pos -1})], - [:EXAMPLE (TokenStruct {:start t_EXAMPLE_BEGIN, :end t_EXAMPLE_END, :type TYPE_BLOCK, :end_pos -})], - [:IMAGE (TokenStruct {:start t_IMG, :end_pos -2})], - [:CAPTION (TokenStruct {:start t_CAPTIONS, :type TYPE_ATTRIBUTE, :key "caption"})], - [:br (TokenStruct {:start t_BLANK_LINE, :end_pos -1})], - [:SOURCE (TokenStruct {:start t_SRC_BEGIN, :end t_SRC_END})], - [:TABLE (TokenStruct {:start t_TABLE_START, :end t_TABLE_END, :start_pos 0})], - [:BULLET (TokenStruct {:start t_BULLET_START, :end t_BULLET_END, :start_pos 0})], - [:RESULTS (TokenStruct {:start t_RESULTS_START, :end t_RESULTS_END})], - [:HEADER (TokenStruct {:start t_HEADER, :start_pos 1, :count 0})], - [:META_OTHER (TokenStruct {:start t_META_OTHER, :start_pos 2, :end_pos -1})]]) - - -(def rm-ns (comp keyword name)) - -(defn mk-key [k t pos] - (case (name k) - "HEADER" (keyword (str (name k) pos)) - (rm-ns k))) - -(clojure.string/join (drop 2 (butlast "#+title:"))) -(re-find t_META "#+TITLE: abc12345def") -(re-find t_HEADER "** test") -(str/split "#+TITLE: abc12345def" t_META) -(def t - "#+TITLE: abc12345def -#+DESCRIPTION: test descriptioon - -* title -paragraph -text -** header test -hi there -") - -(defn re-split [rx line] - (let [matches (re-find rx line) - pos (if (string? matches) - (count matches) - (count (or (str (first matches)) matches))) - remainder (subs line pos)] - [matches pos remainder])) - -(defn match-token [line] - (->> TOKENS - (reduce (fn [m [k v]] - (let [s (re-find (:start v) line) - pos (if (string? s) (count s) (count (or (str (first s)) s))) - v (subs line pos)] - (when (not (nil? s)) - (reduced (remove nil? [(mk-key k v pos) (if (= "" v) nil v)]))))) []) - (#(or % [:p line])))) - - -(defn parse-line [text] - (loop [line (str/split-lines text) - r []] - (if (empty? line) - r - (recur (next line) - (if (= :HEADER1 (first (last r))) - (conj (last r) (match-token (first line))) - (conj r (match-token (first line)))))))) - -(defn parse-flat [text] - (->> (str/split-lines text) - (map (fn [a] (match-token a))))) - -(parse-line t ) -(parse-flat t ) - -(defn to->html [dsl] - (map #(apply conj [(case (first %) - :TITLE :h1 - :HEADER1 :h1 - :HEADER2 :h2 - :br :br - :SOURCE :pre - (first %))] (rest %)) dsl)) - -(defn parse->to-hiccup [org-document] - (to->html (parse-flat org-document))) - -(parse->to-hiccup t) -(parse->to-hiccup "") - -(map #(clojure.set/rename-keys (parse-line t) {:HEADER :h1}) (parse-line t )) diff --git a/spec-demo/resources/public/rename-me-index.html b/spec-demo/resources/public/rename-me-index.html index ef36acc..b427263 100644 --- a/spec-demo/resources/public/rename-me-index.html +++ b/spec-demo/resources/public/rename-me-index.html @@ -5,7 +5,7 @@ - Atom Juice Merchant Website + Clojure demos From 6e228128a4f9b357cd525c47954409d0a5b1b970 Mon Sep 17 00:00:00 2001 From: Oliver Marks Date: Fri, 29 Jan 2021 15:22:20 +0000 Subject: [PATCH 2/5] Add klipse. --- .../resources/public/clojure-basics.org | 260 ++++++++++++++++++ .../resources/public/rename-me-index.html | 8 + reagent-reitit-demo/src/demo/core.cljs | 29 +- 3 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 reagent-reitit-demo/resources/public/clojure-basics.org diff --git a/reagent-reitit-demo/resources/public/clojure-basics.org b/reagent-reitit-demo/resources/public/clojure-basics.org new file mode 100644 index 0000000..4d55a8d --- /dev/null +++ b/reagent-reitit-demo/resources/public/clojure-basics.org @@ -0,0 +1,260 @@ +#+TITLE: Getting started with Clojure some of the basics. + +* Clojure datatype's +Clojure is a very dynamic and interactive language it is able to give very in depth feed back of your running program, all code below can evaluated live inside an IDE when connected. + +Basic data +#+BEGIN_SRC clojure :tangle src/core.cljc +;; comments are semi colon, linters will treat alignment different bassed on the number of semi colons +;;Integers +1234 +;;Doubles +1.234 +;;BigDecimal +2.33M +;;Ratios are allowed +34/3 +;;Strings are double quoted +"Hello World" +;;characters are backslash escaped +\a \b \c \e \t \c +;;Symbols (named things like function & variable) are just text +my-var-name my-fn-name my-second-var-etc my-second-fn-etc +;;hash map or dictonary keys are prefixed with a colon, no need to quote +:key1 :key2 :key3 +;;booleans +true false +;; Null or None is just nil +nil +;; Regex patterns are strings prefixed with a hash symbol +#"^Begins with this text" +#+END_SRC + +Collections of data +#+BEGIN_SRC clojure :tangle src/core.cljc +;; List are wrapped in brackets, space or comma act as seerators +(1 2 3 4 5 6 7 8 9) +;; Lists are special in that they need to be escaped to be treated as data +;; using the list function or the quote symbol, which is short hand and explained later. +(list 1 2 3 4 5 6 7 8 9) +'(1 2 3 4 5 6 7 8 9) + +;;Vectors or indexed lists are created with square brackets +;;or using the long form vector function +[1 2 3 4 5 6 7 8 9] +(vector 1 2 3 4 5 6 7 8 9) + +;; hash-maps are denoted with curly braces or calling hash-map function +;; they must always contain key value pairs. +{:key-one 1 :key-two 2} +(hash-map :key-one 1 :key-two 2) + +;; sets also use curly braces but start with a # or you can call hash-set function +;; sets have to be unique so duplicates will throw an error +#{1 2 3} +(hash-set 1 2 3) +;;#{1 2 1 3} how ever would through an error, using hash-set would remove the duplicate with out error. +#+END_SRC + + +* Language syntax +The clojure syntax is super simple it is always a function call followed by arguments, this applies for things like standard conditionals and, or, not etc + +All functions return a value, this is always the last statement called inside the function. + +When ever you see an opening bracket the value after is always a function unless the bracket is preceded by a character in which case this is a macro a common macro is ='(1 2)= which is the same as typing (list 1 2) so a function is still the first parameter. + +** Basic data structures +In clojure you will find you spend most of your time dealing with lists, vectors, maps and sets each has its own bracket notation in the language you can also call =vector=, =list=, =hash-map= and =hash-set= functions to the same effect, how ever it is far more convenient to use the shorthand. + +#+BEGIN_SRC clojure :tangle src/core.cljc +(ns demo.core) +;; lists are denoted with brackets, but remember to use '( +;; or (list function) else the first value is a function call. +'(1 2 3) + +;; vectors are donoted with square brackets +[1 2 3 4] + +;; hash-maps are denoted with curly braces +;; they must always contain key value pairs. +{:key-one 1 :key-two 2} + +;; sets also use curly braces but start with a # +;; sets have to be unique so duplicates will throw an error +#{1 2 3} + +;;#{1 2 1 3} how ever would through an error. +#+END_SRC + +** Maths +All maths operators are also functions, meaning you start with the operation then the numbers to apply the operator against, opposed to many other languages where you would separate the numbers by operators. + +This goes back to function first then arguments. + +#+BEGIN_SRC clojure :tangle src/core.cljc +;; This is equivalend to 1 + 2 + 3 in other languages +(+ 1 2 3) + +;; When using multiple operators just nest them. +(* 2 (+ 1 2 3)) +#+END_SRC + + +** Variables +Variables in clojure are defined with =def= function, how ever unlike other languages you can not change these unless using some construct which allows this like an atom. + +#+BEGIN_SRC clojure :tangle src/core.cljc +;; These define variable Which can not change +(def my-string "A string once set does not change") +(def my-number 3.14) + +;; If you want to change your variables you need to define your variable as an atom +;; Where possible you want to avoid this, the language is immutable by design to help avoid bugs +;; any variable that can be arbitarily changed like atom's means any piece of code can change the value +;; and potentially cause issues when used else where in functions that did not expect the new value +(def my-atom-string (atom "A string once set does not change")) +(def my-atom-number (atom 3.14)) +(def my-atom-hashmap (atom {:one 1 :two 2})) + +;;One important thig to remember is that to access the values of an atom you need to prepend an @ +;;when using functions that change an atom you do not need the @ +;;@ is shorthand for the deref function +@my-atom-string +@my-atom-number +@my-atom-hashmap + +;; Atoms are changed by calling functions which change the value in a thread safe fashion +;; reset! is to replace the value, swap! is used to update a value and takes a function to apply the update +(reset! my-atom-string "My new string") +;; Append to the existing string +(swap! my-atom-string str " appended text") + +;; replace with empty hash map +(reset! my-atom-hashmap {}) +;; add new key and value using assoc function +(swap! my-atom-hashmap assoc :key-one "value") + +;; There is also defonce which is handy for hot reloading +;; when your code is reloaded all variables will be reset to the inital states +;; with defonce the values are maintained over reloads which helps when testing user flows +;; by maintining the state the user does not need to start again +(defonce my-atom-hashmap (atom {:ex-one "hi"})) +(swap! my-atom-hashmap assoc :key-one "value") + +#+END_SRC + +** Conditionals +Similar rules apply to if conditions =if=, =and=, =or= and =not= are also functions, it's also worth noting that all these function return values in other languages like python you would update a variable and the if would not directly return a value, try evaluating these to get a feel for this. + +When needing multiple conditions look into, =cond= =condp= or =case= + +#+BEGIN_SRC clojure :tangle src/core.cljc +;; OR AND NOT can be used by there own as function calls +(or nil 1) +(and nil 1) +(not true) + +;; These can then be mixed with the if condition function like so +;; its important to note that the first parameter is the condition to check +;; then the code to call followed by the else function, you can only call a single function +(if (true? (not true)) + 1 + 2) + +;; If you don't need the else your better of using when +(when (= 1 1) + (prn "True so print me!") + (prn "multiple statements aloowed in a when, only last returns a value")) + +;; If you need to call multiple function when only one is allowed use the do function. +(if (true? (not true)) + (do + (prn "first thing todo") + (prn "second thing todo, last statement will be the return") + 1) + 2) +#+END_SRC + +** Looping +For the most part you will use =map= =filter= and =reduce= which apply a function for each item in a sequence of values, for the most part this is enough. + +You can also use loop which takes your initial values as params inside the [] block and you call recur supplying the updated values in each iteration, not calling recur will end the loop. + +#+BEGIN_SRC clojure :tangle src/core.cljc +;; set n to 0 initially, increment in each loop while n is less than 5 return n value when this is no longer the case +(loop [n 0] + (if (< n 5) + (recur (inc n)) n)) +#+END_SRC + +For loops are also available in a similar fashion to =loop= +#+BEGIN_SRC clojure :tangle src/core.cljc +;; loop over the sequence of numbers, assigning each to X until the end +;; run any functions on the value in the rest of the body +(for [x [1 2 3 4 5 6]] + (* x x)) +#+END_SRC + + +** Hashmap's keyword's & De-structuring +One of the fundamentals to working with hashmap's in clojure is that keywords are function when ever you see =:my-key= you are actually calling a function this is very different to most other languages. + +This has some really nice side effects, one being it is very easy to navigate your hasahmap's the example below shows how to pull out the value 2. + +*** Basic value fetching +#+BEGIN_SRC clojure :tangle src/core.cljc +(def my-hashmap {:top {:first-key 1 :second-key 2}}) + +;; you can get the value in a number of ways. +;; using neted get return the result of one get to the next +(get (get my-hashmap :top) :second-key) +;; much nicer is to use get-in and specify the path +(get-in my-hashmap [:top :second-key]) +;; You can also call the keywords as a function +(:second-key (:top my-hashmap)) +;; or using something called a threading macro +;; push the map through the :top function then the result +;; into the :second-key function +(-> my-hashmap :top :second-key) +#+END_SRC + + +You can use de-structuring in clojure to explode out keys when passed to a function. +Using =:or= we can set default values if the key is missing, the :as keyword can be used to access the unstructured map. +#+BEGIN_SRC clojure :tangle src/core.cljc +(defn my-fn [{:keys [id name value missing] :or {missing "not set"} :as my-hashmap}] + [id name value missing my-hashmap]) + +(my-fn {:id 1 :name "bob" :value 456}) +#+END_SRC + + +* Wierd symbol's +There is a good reference on the symbols in clojure below when you encounter one your not sure about. +https://clojure.org/guides/weird_characters + +#+BEGIN_SRC clojure :tangle src/core.cljc +;; : Colon indicates a keyword's + +;; :: Double colon makes a namespaced keyword, what ever is defined at the top of your file under (ns) +;; will be pre pended to the keyword + +;; , comma is just white space and is used only for readability to the user + +;; #( is an annoymous function (fn [param1] (prn param1)) is equivalent to #(prn %) % being param1 +;; you can also use %1 %2 %3 etc to refernce other params, longer form is prefered because the params are named +;; but for very short small functions this variant can be handy + +;; -> is the threading macro, basically the result of each statement is passed to the next +;; as the first parameter, this works nicely with hashmaps because keywords are functions + +;; ->> same as above but the result is the last parameter +;; when working with sequences you tend to use this one most functions Take +;; a sequence as the last parameter + +;; '( this is the same as writting (list 1 2 3) the ' denotes we are using the list function + +;; #_ this is the comment block it +#+END_SRC + diff --git a/reagent-reitit-demo/resources/public/rename-me-index.html b/reagent-reitit-demo/resources/public/rename-me-index.html index b427263..1996064 100644 --- a/reagent-reitit-demo/resources/public/rename-me-index.html +++ b/reagent-reitit-demo/resources/public/rename-me-index.html @@ -10,6 +10,8 @@ + + + + + + +
+ loading here +
+ + + + + diff --git a/reagent-reitit-demo/resources/public/rename-me-index.html b/reagent-reitit-demo/resources/public/rename-me-index.html index a46f977..7182c97 100644 --- a/reagent-reitit-demo/resources/public/rename-me-index.html +++ b/reagent-reitit-demo/resources/public/rename-me-index.html @@ -9,8 +9,8 @@ - +