Some fixes for the klipse to sci migration.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
443fd967ab
commit
78cd228610
|
@ -9,11 +9,12 @@ steps:
|
|||
#- npx shadow-cljs build app
|
||||
|
||||
- name: Build
|
||||
image: clojure:tools-deps
|
||||
image: cimg/clojure:1.11.1-node #clojure:tools-deps
|
||||
user: root
|
||||
commands:
|
||||
- cd reagent-reitit-demo
|
||||
- cp resources/public/rename-me-index.html resources/public/index.html
|
||||
- clj -Mbuild-dev release app
|
||||
- clojure -Mbuild-dev release app
|
||||
#- clj -A:prod
|
||||
|
||||
- name: deploy-site
|
||||
|
|
30
deps.edn
30
deps.edn
|
@ -1,8 +1,30 @@
|
|||
{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
|
||||
;; version updates seem to break the code eval.
|
||||
org.clojure/clojurescript {:mvn/version "1.10.740"}
|
||||
com.github.seancorfield/honeysql {:mvn/version "2.2.891"}
|
||||
org.clojure/clojurescript {:mvn/version "1.11.60"}
|
||||
|
||||
;;ajax requests
|
||||
cljs-ajax/cljs-ajax {:mvn/version "0.8.1"}
|
||||
|
||||
;; react
|
||||
reagent/reagent {:mvn/version "1.1.1"}
|
||||
reagent-utils/reagent-utils {:mvn/version "0.3.3"}
|
||||
olymk2/cl-org {:git/url "https://gitlab.com/olymk2/cl-org.git"
|
||||
:sha "9f29b9a3989bbd0f843e2f025d91cc7b92e818a4"}
|
||||
com.bhauman/figwheel-main {:mvn/version "0.2.11"}}
|
||||
:sha "528e8125afcac5d7664aac80f7fbba12dd0f5d98"}
|
||||
;;routing
|
||||
metosin/reitit {:mvn/version "0.5.10"}
|
||||
metosin/reitit-spec {:mvn/version "0.5.10"}
|
||||
metosin/reitit-frontend {:mvn/version "0.5.10"}
|
||||
|
||||
;; interactive code snippets
|
||||
;;viebel/klipse {:mvn/version "7.10.4"}
|
||||
com.github.seancorfield/honeysql {:mvn/version "2.4.1026"}
|
||||
|
||||
hiccup/hiccup {:mvn/version "1.0.5"}
|
||||
viebel/klipse {:mvn/version "7.11.4"}
|
||||
org.babashka/sci {:mvn/version "0.7.39"}
|
||||
org.babashka/sci.configs {:git/url "https://github.com/babashka/sci.configs"
|
||||
:sha "33bd51e53700b224b4cb5bda59eb21b62f962745"}
|
||||
datascript/datascript {:mvn/version "1.3.15"}
|
||||
|
||||
com.bhauman/figwheel-main {:mvn/version "0.2.18"}}
|
||||
:paths ["src" "resources"]}
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
;;viebel/klipse {:mvn/version "7.10.4"}
|
||||
com.github.seancorfield/honeysql {:mvn/version "2.4.1026"}
|
||||
|
||||
hiccup/hiccup {:mvn/version "1.0.5"}
|
||||
viebel/klipse {:mvn/version "7.11.4"}
|
||||
org.babashka/sci {:mvn/version "0.7.39"}
|
||||
org.babashka/sci.configs {:git/url "https://github.com/babashka/sci.configs"
|
||||
:sha "33bd51e53700b224b4cb5bda59eb21b62f962745"}
|
||||
datascript/datascript {:mvn/version "1.3.15"}
|
||||
datascript/datascript {:mvn/version "1.4.2"}
|
||||
|
||||
|
||||
com.bhauman/figwheel-main {:mvn/version "0.2.18"}}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.2.4",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
"@codemirror/legacy-modes": "^6.3.2",
|
||||
"@codemirror/matchbrackets": "^0.19.4",
|
||||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
#+TITLE: Clojure(script) CI
|
||||
|
||||
* CI integration
|
||||
|
||||
Building a polylith based clojure project, these steps are similar to most clojure projects with a few additions for the poly tool.
|
||||
|
||||
* Github
|
||||
|
||||
** Deploy rules
|
||||
Name our action and run it only when code is pushed into master
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
name: Staging Deploy
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Checkout
|
||||
Checkout repository, fetch depth is required if you needs tags available
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Checkout the code
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
#+END_SRC
|
||||
|
||||
** Cache
|
||||
Cache the downloaded libraries so we don't do each each time.
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Cache project dependencies
|
||||
- name: Cache deps
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.polylith
|
||||
~/.m2
|
||||
~/.gitlibs
|
||||
~/.clojure
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('deps.edn') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
${{ runner.os }}-
|
||||
#+END_SRC
|
||||
|
||||
** Set env / project checks
|
||||
Create a var called CHANGED_PROJECTS we can use in conditionals in later build steps, we also check our project comply's to the polylith rules before moving onto linting, testing, building and deploying.
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Check the project and set enc for later steps
|
||||
- name: Poly Check / Set Env
|
||||
run: |-
|
||||
cd workspace
|
||||
echo "CHANGED_PROJECTS=$(clojure -M:poly ws get:changes:changed-or-affected-projects since:previous-release skip:dev)" >> $GITHUB_ENV
|
||||
clojure -M:poly info since:release
|
||||
clojure -M:poly check
|
||||
#+END_SRC
|
||||
|
||||
** Linting
|
||||
Lint code with clj-kondo this example lints src and workspace folders.
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Lint the code with kondo
|
||||
- name: Code Linting
|
||||
uses: DeLaGuardo/clojure-lint-action@master
|
||||
with:
|
||||
clj-kondo-args: --lint src workspace
|
||||
check-name: Linting
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
#+END_SRC
|
||||
|
||||
** Setup Java
|
||||
Install a version on java using the java action.
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Install java
|
||||
- name: Set up JDK & publish to maven
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 13
|
||||
#+END_SRC
|
||||
|
||||
** Setup clojure
|
||||
Install clojure from the clojure action.
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Install clojure
|
||||
- name: Install clojure tools
|
||||
uses: DeLaGuardo/setup-clojure@3.5
|
||||
with:
|
||||
cli: 1.10.3.933
|
||||
#+END_SRC
|
||||
|
||||
** Testing changes since last release
|
||||
Run test but only test since the last tagged release
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Run the tests
|
||||
- name: Poly Test
|
||||
run: |-
|
||||
cd workspace
|
||||
clojure -M:poly test since:previous-release
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Building
|
||||
Finally create your uberjar, this example uses a condition so it only builds if the projects or one of its components has changed.
|
||||
We need to set the env in a previous step to make it available to future steps.
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Build the uberjar
|
||||
- name: Build my example api
|
||||
if: contains('${{ env.CHANGED_PROJECTS }}', 'my-example-api')
|
||||
run: |-
|
||||
cd workspace/projects/my-example-api
|
||||
clojure -X:uberjar
|
||||
#+END_SRC
|
|
@ -1,9 +1,42 @@
|
|||
#+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.
|
||||
* Clojure basics
|
||||
|
||||
Clojure is a very dynamic and interactive language that can give you very in-depth feedback on your running programs. For example, you can use Clojure to:
|
||||
|
||||
- Debug your code more easily by seeing the values of variables, the call stack, and the execution time of each function.
|
||||
- Experiment with different ideas more quickly by evaluating code live inside an IDE.
|
||||
- Use third party graphical tools to aid debugging and testing your code like portal, portfolio & clerk
|
||||
|
||||
The code below can be evaluated live inside the page you can select parts of the code to evaluate partially you can also modify the code the new version will be evaluated live.
|
||||
|
||||
** Clojure comments
|
||||
|
||||
There a few ways to comment code in this language.
|
||||
|
||||
- Semi colon's can be used to comment out a whole line handy for adding in detailed comments, for commenting out code you can also use semi colons but more useful is the #_ syntax.
|
||||
- #_ prefixed before a bracket will comments out everything between the opening and closing bracket, this is most hopefull to comment out entire blocks in one go.
|
||||
- Another method is to use rich comments which is a function which stops the evaluation of everything inside the block, this is extremely useful as you can run the code from your IDE but it will not be executed in production.
|
||||
|
||||
#+BEGIN_SRC clojure :tangle src/core.cljc
|
||||
; comments are semi colon, linters will treat alignment different bassed on the number of semi colons
|
||||
|
||||
; comment out the block of code from starting bracket to matching closing bracket
|
||||
; in this example the whole if condition is commented out which spans 3 lines
|
||||
#_(if true
|
||||
(prn "true")
|
||||
(prn "false"))
|
||||
|
||||
; Ignore this code, but we can still eval it inside an IDE
|
||||
(comment
|
||||
(+ 1 2 3))
|
||||
|
||||
#+END_SRC
|
||||
|
||||
** Basic datatype's
|
||||
|
||||
Clojure has a rich set of data types, including numbers, strings, characters, booleans, and nil.
|
||||
|
||||
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
|
||||
|
@ -32,7 +65,10 @@ nil
|
|||
#"^Begins with this text"
|
||||
#+END_SRC
|
||||
|
||||
Collections of data
|
||||
** Collections of data
|
||||
|
||||
Clojure also has a number of compound data types, such as lists, vectors, maps, and sets.
|
||||
|
||||
#+BEGIN_SRC clojure :tangle src/core.cljc
|
||||
;; List are wrapped in brackets, space or comma act as seerators
|
||||
;; Lists are special in that they need to be escaped to be treated as data
|
||||
|
@ -79,7 +115,7 @@ When ever you see an opening bracket the value after is always a function unless
|
|||
** 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 as the first item in a list then arguments.
|
||||
This goes back to function as the first item in a list then the arguments.
|
||||
|
||||
#+BEGIN_SRC clojure :tangle src/core.cljc
|
||||
;; This is equivalend to 1 + 2 + 3 in other languages
|
||||
|
@ -91,7 +127,7 @@ This goes back to function as the first item in a list then arguments.
|
|||
|
||||
|
||||
** 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.
|
||||
Variables in clojure are defined with =def= function, how ever unlike other languages you can not change these unless using some construct which allows the value to be changed like an atom.
|
||||
|
||||
#+BEGIN_SRC clojure :tangle src/core.cljc
|
||||
;; These define variable Which can not change
|
||||
|
@ -240,7 +276,7 @@ Below you will find some simple examples, it is worth noting that some of the wi
|
|||
;; 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
|
||||
;; when working with sequences you tend to use this one most sequence 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
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#+TITLE: Containerizing your application
|
||||
|
||||
This is a multistage Dockerfile it has a build and run stage to help reduce the final image size.
|
||||
|
||||
This example is built for use with polylith it allows you to build multiple containers from a single docker file.
|
||||
|
||||
|
||||
|
||||
|
||||
#+BEGIN_SRC yaml :tangle src/core.cljc
|
||||
# Beware the alpine images, seems the googleads library does not like the alphine glib alternative ie muscl
|
||||
FROM clojure:temurin-17-tools-deps AS builder
|
||||
ENV CLOJURE_VERSION=1.11.1.1182
|
||||
ARG PROJECT
|
||||
|
||||
RUN mkdir -p /build
|
||||
WORKDIR /build
|
||||
|
||||
# Fetch the deps first, by copying just the deps we can cache the download
|
||||
# except when the deps file has changed.
|
||||
COPY ./deps.edn /build
|
||||
RUN clojure -P -X:dev
|
||||
|
||||
COPY ./ /build
|
||||
RUN clojure -T:build uberjar :project $PROJECT
|
||||
|
||||
FROM eclipse-temurin:17
|
||||
ARG PROJECT
|
||||
COPY --from=builder /build/target/ /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD ./projects/car-cli/resources/google/ /app/
|
||||
ADD ./projects/car-cli/resources/google/ /root/
|
||||
RUN mkdir -p /app/environment/
|
||||
|
||||
# Build ARG are not available at runtime but we can insert them into env as part of the build
|
||||
ENV PROJECT_RUN=/app/${PROJECT}.jar
|
||||
|
||||
ENTRYPOINT ["java", "-jar"]
|
||||
|
||||
# this is expanded by a shell so can't use in a params list, can't find another way
|
||||
CMD java -jar ${PROJECT_RUN}.jar
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC sh :tangle src/core.cljc
|
||||
docker build -f Dockerfile-Multistage . --build-arg PROJECT=example-api
|
||||
#+END_SRC
|
|
@ -3,7 +3,7 @@
|
|||
* Getting started
|
||||
To work with the code interactively jack into the project in your IDE of choice select "figwheel-main" as the build tool and dev as the build.
|
||||
|
||||
Alternatively start a repl from the command line with
|
||||
Alternatively start a repl from the command line with.
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
clojure -m figwheel.main --build dev --repl
|
||||
|
@ -12,7 +12,7 @@ clojure -m figwheel.main --build dev --repl
|
|||
* Intro to datalog
|
||||
First import datascript which is a client side version of datalog, see datomic crux datalevin and datahike for other datalog database.
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
|
||||
(ns core.demo
|
||||
#_(ns core.demo
|
||||
(:require [datascript.core :as d]))
|
||||
#+END_SRC
|
||||
|
||||
|
@ -32,12 +32,13 @@ Datalog databases can be schema less but a lot of the power comes from creating
|
|||
|
||||
You can create a new database using create-conn as below then empty hash map is simply a blank schema,
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
|
||||
(def demo-conn (d/create-conn {}))
|
||||
(def demo-conn (d/create-conn {}))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
Using the connection we can just start inserting data, using standard hash maps and lists structures, we always specify the attribute and the value when transacting.
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
|
||||
(def demo-conn (d/create-conn {}))
|
||||
(d/transact! demo-conn [{:user/name "Brooke" :user/img "me.jpg"} {:user/name "Kalvin" :user/img "you.jpg"}])
|
||||
#+END_SRC
|
||||
|
||||
|
|
|
@ -6,21 +6,22 @@ https://github.com/weavejester/hiccup
|
|||
hiccup is a html DSL, used extensively in the clojure's eco-system, there are others as well but hiccup is the most widely used.
|
||||
|
||||
In hiccup everything is a list, this means you can easily compose html using standard language constructs.
|
||||
To render the hiccup to html elements we call the (html) function from the hiccup library for server side with reagent or client side code this will likely be a different function like (render).
|
||||
|
||||
** Simple examples
|
||||
If using reagent you don't need to pass into h/html but this is server side reagent also has some helpers to work with react nicer.
|
||||
#+BEGIN_SRC clojure
|
||||
(h/html [:span "bar"])
|
||||
[:span "bar"]
|
||||
#+END_SRC
|
||||
|
||||
Attributes are added as a map of values styles are also a map
|
||||
#+BEGIN_SRC clojure
|
||||
(h/html [:span "bar" {:class "class1 class2" :title "my title" :style {:color "red"}}])
|
||||
[:span {:class "class1 class2" :title "my title" :style {:color "red"}} "bar"]
|
||||
#+END_SRC
|
||||
|
||||
You can use shorthand to add id's and classes
|
||||
#+BEGIN_SRC clojure
|
||||
(h/html [:span#id.class1.class2 "bar"])
|
||||
[:span#id.class1.class2 "bar"]
|
||||
#+END_SRC
|
||||
|
||||
** Compossible components
|
||||
|
@ -35,210 +36,36 @@ In this example its only a hash map, the data is separated out from the html, in
|
|||
|
||||
(defn navbar [links]
|
||||
[:header.bg-black-90.fixed.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l
|
||||
[:nav.f6.fw6.ttu.tracked
|
||||
(map navbar-link links)]])
|
||||
(into [:nav.f6.fw6.ttu.tracked]
|
||||
(map navbar-link links))])
|
||||
|
||||
(h/html (navbar [{:href "link1" :title "title here"}
|
||||
{:href "link2" :title nil}
|
||||
{:href "link3" :text "link text"}
|
||||
{:href "link4"}]))
|
||||
(navbar [{:href "link1" :title "title here"}
|
||||
{:href "link2" :title nil}
|
||||
{:href "link3" :text "link text"}
|
||||
{:href "link4"}])
|
||||
#+END_SRC
|
||||
|
||||
You can use clojure core language to manipulate these lists.
|
||||
|
||||
place parts inside another containing element
|
||||
#+BEGIN_SRC clojure
|
||||
(h/html (into [:div.container] [[:span "span 1"] [:span "span 2"]]))
|
||||
(into [:div.container]
|
||||
[[:span "span 1"]
|
||||
[:span "span 2"]])
|
||||
#+END_SRC
|
||||
|
||||
You could also use merge
|
||||
#+BEGIN_SRC clojure
|
||||
(h/html (merge [:span "span 1"] [:span "span 2"] [:span "span 3"]))
|
||||
(merge [:span "span 1"] [:span "span 2"] [:span "span 3"])
|
||||
#+END_SRC
|
||||
|
||||
We can take advantage of lazyness if we like
|
||||
#+BEGIN_SRC clojure
|
||||
(h/html (take 2 (map navbar-link [{:href "link1" :title "title here"}
|
||||
{:href "link2" :title nil}
|
||||
{:href "link3" :text "link text"}
|
||||
{:href "link4"}])))
|
||||
(take 2 (map navbar-link
|
||||
[{:key "link1" :href "link1" :title "title here"}
|
||||
{:key "link2" :href "link2" :title nil}
|
||||
{:key "link3" :href "link3" :text "link text"}
|
||||
{:key "link4" :href "link4"}]))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
* HoneySQL
|
||||
https://github.com/seancorfield/honeysql
|
||||
HoneySQL is a DSL specifically for building SQL queries, much like hiccup you build up a data structure that can be composed, honey has a lot of helper functions available so you don't need to build these maps manually.
|
||||
|
||||
Honey does not care about connecting to your database it only builds your queries.
|
||||
|
||||
A nice way to write these is using =->= which is a threading macro, put simply the result one call is passed to the next.
|
||||
** Basic queries
|
||||
first tip is sql/format will convert to an sql query, use it to examine your query's or run else where.
|
||||
In this example we create a select but with no from format will still give you a query but it will be incomplete.
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format (sqlh/select :first_name :last_name :email))
|
||||
#+END_SRC
|
||||
|
||||
You use multiple functions to combine the parts, below we add the missing from.
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format (sqlh/from
|
||||
(sqlh/select :first_name :last_name :email)
|
||||
:users))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
How ever it's much nicer for readability to use the threading macro
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)))
|
||||
#+END_SRC
|
||||
|
||||
The functions understand ordering so your order does not matter.
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format
|
||||
(-> (sqlh/from :users)
|
||||
(sqlh/select :first_name :last_name :email)))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
We can do the usual things like limiting & ordering etc
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sqlh/order-by :first_name)
|
||||
(sqlh/limit 10)))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Filtering
|
||||
|
||||
Filtering is just as simple and support the usual < > not in type expressions.
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sqlh/where [:= :first_name "spot"]
|
||||
[:= :last_name "dog"])))
|
||||
#+END_SRC
|
||||
|
||||
Often we want to conditionally filter, this is nice and simple with the knowledge that where will short circuit give nil.
|
||||
|
||||
So below no where will not be appended because true is not false so the when return nil which removes the where in the final query.
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sqlh/where (when (true? false) [:= :first_name "spot"]))))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Extending / joins
|
||||
For all the standard fn's like select and where there are equivalent merge fn's the merge versions append in place of replacing.
|
||||
|
||||
A good strategy is to build basic queries extending them when needed.
|
||||
|
||||
we can use =if= =when= =when-let= =cond->= to help build these.
|
||||
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-sql
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)))
|
||||
|
||||
(defn user-search [{:keys [first-name last-name] :or {first-name nil last-name nil}}]
|
||||
(sql/format
|
||||
(-> base-sql
|
||||
(sqlh/select :first_name :last_name)
|
||||
(sqlh/merge-where (when first-name [:= :first_name first-name]))
|
||||
(sqlh/merge-where (when last-name [:= :last_name last-name])))))
|
||||
|
||||
(sql/format (user-search {:first-name "spot"}))
|
||||
#+END_SRC
|
||||
|
||||
Now is a good time to explain aliasing, basically keywords become vectors so :first_name would become [:first_name :fn] to alias the column first_name to fn we can aliases columns tables sub select the lot like in standard sql.
|
||||
#+BEGIN_SRC clojure
|
||||
(sql/format
|
||||
(-> base-sql
|
||||
(select [:first_name :fn] [:last_name :ln] [:email :e])))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
We can also do joins to other table's
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-sql
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)))
|
||||
|
||||
(def base-join-sql
|
||||
(-> base-sql
|
||||
(sqlh/join [:address] [:= :users.address_id :address.id])))
|
||||
|
||||
(sql/format base-join-sql)
|
||||
#+END_SRC
|
||||
|
||||
or group by's and sql functions like =count= =max= =min= these can be used by appending :%name to the selected column.
|
||||
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-group-sql
|
||||
(-> base-sql
|
||||
(sqlh/select :first_name [:%count.first_name :count_name])
|
||||
(sqlh/group-by :first_name)))
|
||||
|
||||
(sql/format base-group-sql)
|
||||
#+END_SRC
|
||||
|
||||
** Larger query
|
||||
This is how I like to compose queries, and shows a larger query being generated.
|
||||
#+BEGIN_SRC clojure
|
||||
(def big-base-sql
|
||||
(-> (sqlh/select :users.* :address.* :products.*)
|
||||
(sqlh/from :users)
|
||||
(sqlh/join :address [:= :users.address_id :address.id])
|
||||
(sqlh/join :products [:= :users.address_id :address.id])
|
||||
(sqlh/limit 100)))
|
||||
|
||||
(defn big-base-filters [filters]
|
||||
(-> big-base-sql
|
||||
(sqlh/where
|
||||
(when (:first_name filters)
|
||||
[:= :first_name (:first_name filters)]))
|
||||
(sqlh/where
|
||||
(when (:last_name filters)
|
||||
[:= :last_name (:last_name filters)]))
|
||||
(sqlh/where
|
||||
(when (:product_name filters)
|
||||
[:= :product.name (:product_name filters)]))
|
||||
(sqlh/where
|
||||
(when (:active filters)
|
||||
[:= :active (:active filters)]))))
|
||||
|
||||
(sql/format
|
||||
(big-base-filters
|
||||
{:first_name "spot"
|
||||
:last_name "dog"
|
||||
:product_name "lead"
|
||||
:active true}))
|
||||
#+END_SRC
|
||||
|
||||
Don't forget its just data, if you don't use sql/format it just returns a data structure which you can build manually, or manipulate with the standard library.
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
{:select (:first_name :last_name :email), :from (:users)}
|
||||
#+END_EXAMPLE
|
||||
|
||||
** Extending / raw sql
|
||||
|
||||
When all else fails you have a few options, check to see if there is a honeysql db specific library or break out =sql/raw= or extending honey sql.
|
||||
|
||||
Say we want to get people added in the last 14 days this is a bit more tricky
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-last-14-days-sql
|
||||
(-> base-sql
|
||||
(sqlh/where [:>
|
||||
[:raw "created"]
|
||||
[:raw "CURRENT_DATE - INTERVAL '14' DAY"]])))
|
||||
|
||||
|
||||
(sql/format base-last-14-days-sql)
|
||||
#+END_SRC
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#+TITLE: HoneySQL tips & tricks
|
||||
|
||||
|
||||
* Basic statements
|
||||
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
#_(ns honeysql.core
|
||||
(:require
|
||||
[honey.sql.core :as sql]
|
||||
[honey.sql.helpers :refer :all :as helpers]))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Simple table select, with filtering and sorting applied
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
(-> (sqlh/select :*)
|
||||
(sqlh/from [:company])
|
||||
(sqlh/where [:= :id 1])
|
||||
(sqlh/format :pretty true))
|
||||
#+END_SRC
|
|
@ -0,0 +1,272 @@
|
|||
#+TITLE: HoneySQL
|
||||
|
||||
* HoneySQL
|
||||
https://github.com/seancorfield/honeysql
|
||||
|
||||
HoneySQL is a Domain Specific Language (DSL) for building SQL queries. HoneySQL provides a number of helper functions to make it easier to build queries, so you don't need to build the data structure manually.
|
||||
|
||||
HoneySQL does not care about connecting to your database. It only builds the queries. All examples send the result to =format= and =first= so that the resulting query is displayed inside the browser.
|
||||
|
||||
One nice way to write HoneySQL queries is to use the =->= threading macro. The =->= threading macro takes two arguments: the first argument is the result of the previous call, and the second argument is the function to call with that result. This allows you to chain together multiple calls to build complex queries.
|
||||
|
||||
Here is an example of how to use the =->= macro to build a simple SQL query:
|
||||
|
||||
#+BEGIN_SRC clojure :eval string
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
The equivalent code with out a threading macros looks like this.
|
||||
|
||||
#+BEGIN_SRC clojure :eval string
|
||||
(first
|
||||
(sql/format
|
||||
(sqlh/from
|
||||
(sqlh/select :first_name :last_name :email)
|
||||
:users)
|
||||
{:pretty true}))
|
||||
#+END_SRC
|
||||
|
||||
** Basic SQL select queries
|
||||
Using HoneySQL you can incrementally build up our queries, they do not need to be complete to get an answer this allow us to define partial SQL and compose it together.
|
||||
|
||||
The example below will produce a select for a couple of fields passing the resulting data into format to create an SQL string we can send to the database, we call first because the result is a vector containing and SQL and any values need none in this instance.
|
||||
|
||||
#+BEGIN_SRC clojure
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
We can extend the example above to include the =FROM= part of the SQL statement to give us something more complete.
|
||||
How ever it's much nicer for readability to use the threading macro
|
||||
#+BEGIN_SRC clojure
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
The functions understand SQL statement ordering so the order you call the functions does not matter.
|
||||
#+BEGIN_SRC clojure
|
||||
(-> (sqlh/from :users)
|
||||
(sqlh/select :first_name :last_name :email)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
We can extend the query to add in limiting & ordering.
|
||||
#+BEGIN_SRC clojure
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sqlh/order-by :first_name)
|
||||
(sqlh/limit 10)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Basic SQL filtering
|
||||
|
||||
Filtering is just as simple and support the usual operators like = < > we pass them in as keywords so =:== =:<>= =:<= =:.= would be the equivalents.
|
||||
#+BEGIN_SRC clojure
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sqlh/where [:= :first_name "spot"]
|
||||
[:= :last_name "dog"])
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
Often we want to conditionally filter, this is nice and simple with the knowledge that where will short circuit given a nil value this means we can use =when= and =if= functions inside our sql generations.
|
||||
|
||||
So below no where will not be appended because true is not false so the when return nil which removes the where in the final query.
|
||||
#+BEGIN_SRC clojure
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)
|
||||
(sqlh/where (when (true? false) [:= :first_name "spot"]))
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Composing SQL queries and performing joins
|
||||
For all the standard fn's like select and where there are equivalent merge fn's the merge versions append in place of replacing.
|
||||
|
||||
A good strategy is to build basic queries extending them when needed, so create a base select then create a function which build on the query adding in the conditions, this allow you to run the base queries in the REPL or the extended version making it easier to find query related issues by testing parts in isolation.
|
||||
|
||||
we can use =if= =when= =when-let= =cond->= to help build these, in the example below you can see that we switch if we search for first last or both name based on the provided map.
|
||||
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-sql
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)))
|
||||
|
||||
(defn user-search [{:keys [first-name last-name] :or {first-name nil last-name nil}}]
|
||||
(-> base-sql
|
||||
(sqlh/select :first_name :last_name)
|
||||
(sqlh/where (when first-name [:= :first_name first-name]))
|
||||
(sqlh/where (when last-name [:= :last_name last-name]))))
|
||||
|
||||
(clojure.string/join
|
||||
"\n"
|
||||
[;; Search for furst name only
|
||||
(-> {:first-name "spot" }
|
||||
(user-search)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
;; Search for last name only
|
||||
(-> {:last-name "dog"}
|
||||
(user-search)
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
;; Search for both first and last name
|
||||
(-> {:first-name "spot" :last-name "dog"}
|
||||
(user-search)
|
||||
(sql/format {:pretty true})
|
||||
(first))])
|
||||
#+END_SRC
|
||||
|
||||
Now is a good time to explain aliasing, basically keywords become vectors so :first_name would become [:first_name :fn] to alias the column first_name to fn we can aliases columns tables sub select the lot like in standard sql.
|
||||
#+BEGIN_SRC clojure
|
||||
(-> base-sql
|
||||
(sqlh/select [:first_name :fn] [:last_name :ln] [:email :e])
|
||||
(sql/format {:pretty true})
|
||||
(first))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
We can also do joins to other table's
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-sql
|
||||
(-> (sqlh/select :first_name :last_name :email)
|
||||
(sqlh/from :users)))
|
||||
|
||||
(def base-join-sql
|
||||
(-> base-sql
|
||||
(sqlh/join [:address] [:= :users.address_id :address.id])))
|
||||
|
||||
(first (sql/format base-join-sql))
|
||||
#+END_SRC
|
||||
|
||||
or group by's and sql functions like =count= =max= =min= these can be used by appending :%name to the selected column.
|
||||
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-group-sql
|
||||
(-> base-sql
|
||||
(sqlh/select :first_name [:%count.first_name :count_name])
|
||||
(sqlh/group-by :first_name)))
|
||||
|
||||
(first (sql/format base-group-sql))
|
||||
#+END_SRC
|
||||
|
||||
** Larger query
|
||||
This is how I like to compose queries, and shows a larger query being generated.
|
||||
#+BEGIN_SRC clojure
|
||||
(def big-base-sql
|
||||
(-> (sqlh/select :users.* :address.* :products.*)
|
||||
(sqlh/from :users)
|
||||
(sqlh/join :address [:= :users.address_id :address.id])
|
||||
(sqlh/join :products [:= :users.address_id :address.id])
|
||||
(sqlh/limit 100)))
|
||||
|
||||
(defn big-base-filters [filters]
|
||||
(-> big-base-sql
|
||||
(sqlh/where
|
||||
(when (:first_name filters)
|
||||
[:= :first_name (:first_name filters)]))
|
||||
(sqlh/where
|
||||
(when (:last_name filters)
|
||||
[:= :last_name (:last_name filters)]))
|
||||
(sqlh/where
|
||||
(when (:product_name filters)
|
||||
[:= :product.name (:product_name filters)]))
|
||||
(sqlh/where
|
||||
(when (:active filters)
|
||||
[:= :active (:active filters)]))))
|
||||
|
||||
(first (sql/format
|
||||
(big-base-filters
|
||||
{:first_name "spot"
|
||||
:last_name "dog"
|
||||
:product_name "lead"
|
||||
:active true})))
|
||||
#+END_SRC
|
||||
|
||||
Don't forget its just data, if you don't use sql/format it just returns a data structure which you can build manually, or manipulate with the standard library.
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
; {:select (:first_name :last_name :email), :from (:users)}
|
||||
#+END_EXAMPLE
|
||||
|
||||
** Extending / raw sql
|
||||
|
||||
When all else fails you have a few options, check to see if there is a honeysql db specific library or break out =sql/raw= or extending honey sql.
|
||||
|
||||
Say we want to get people added in the last 14 days this is a bit more tricky
|
||||
#+BEGIN_SRC clojure
|
||||
(def base-last-14-days-sql
|
||||
(-> base-sql
|
||||
(sqlh/where [:>
|
||||
[:raw "created"]
|
||||
[:raw "CURRENT_DATE - INTERVAL '14' DAY"]])))
|
||||
|
||||
|
||||
(first (sql/format base-last-14-days-sql))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
* Basic statements
|
||||
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
#_(ns core.demo
|
||||
(:require
|
||||
[honey.sql :as sql]
|
||||
[honey.sql.helpers :as sqlh]))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
(clojure.core/into [:a] [1 2 3 4])
|
||||
#+END_SRC
|
||||
|
||||
** Simple table select, with filtering and sorting applied
|
||||
#+BEGIN_SRC clojurescript
|
||||
(-> (sqlh/select :*)
|
||||
(sqlh/from :company)
|
||||
(sqlh/where [:= :id 1])
|
||||
str)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
(str (sqlh/select :*))
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Dynamic where clauses
|
||||
|
||||
*** where value exists
|
||||
=sqlh/where= will only be applied to the query if it has a value so nil on its own will not applied
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
(defn conditional-where [id]
|
||||
(-> (sqlh/select :*)
|
||||
(sqlh/from [:company])
|
||||
(sqlh/where (when id [:= :id id]))))
|
||||
|
||||
(first (sql/format (conditional-where 1) {:pretty true}))
|
||||
#+END_SRC
|
||||
|
||||
*** Switch between singular or multiple values in condition
|
||||
|
||||
#+BEGIN_SRC clojurescript :tangle ./src/core.cljs
|
||||
(defn conditional-where [id]
|
||||
(-> (sqlh/select :*)
|
||||
(sqlh/from [:company])
|
||||
(sqlh/where (if (sequential? id) [:in :id id] [:= :id id]))))
|
||||
|
||||
(first (sql/format (conditional-where [1 2 3]) {:pretty true}))
|
||||
;(clojure.string/join "" (sql/format (conditional-where [1 2 3]) {:pretty true}))
|
||||
#+END_SRC
|
||||
|
|
@ -37,9 +37,30 @@ https://purelyfunctional.tv/guide/reagent/
|
|||
Form one components are for simply rendering some html with values that are not going to change.
|
||||
|
||||
In the example below the function just returns some hiccup with the parameters inserted, you need to specify =:key= when dynamically repeating the elements and these should be reproducible unique id's where possible not randomly generated or indexed numbers if the data is unordered.
|
||||
#+BEGIN_SRC clojurescript
|
||||
#+BEGIN_SRC clojure
|
||||
(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])
|
||||
[:a.link.dim.dib.mr3 {:key href :href href :title title} text])
|
||||
|
||||
(navbar-link {:href "https://clojure.org" :title "linke title" :text "link here"})
|
||||
#+END_SRC
|
||||
|
||||
|
||||
#+BEGIN_SRC clojure
|
||||
(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]]])
|
||||
|
||||
(product-card
|
||||
{:title "Cat 01"
|
||||
:amount "£54.59"
|
||||
:description "Cat description here"
|
||||
:link "http://placekitten.com/g/600/300"})
|
||||
#+END_SRC
|
||||
|
||||
|
||||
|
@ -47,8 +68,15 @@ In the example below the function just returns some hiccup with the parameters i
|
|||
In form two we can track local state inside a component a click counter being a basic example.
|
||||
|
||||
|
||||
#+BEGIN_SRC clojurescript
|
||||
#+BEGIN_SRC clojure
|
||||
(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)])))
|
||||
|
||||
[my-component "Clickable component" 1]
|
||||
#+END_SRC
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
:deps {:aliases [:dev]}
|
||||
:dev-http {8080 "resources/public/"}
|
||||
:dev-http {8080 ["resources/public/" "classpath:public"]}
|
||||
:source ["src" "../../components"]
|
||||
:builds {:app {:output-dir "resources/public/cljs-out/"
|
||||
:asset-path "."
|
||||
:asset-path "/cljs-out"
|
||||
:target :browser
|
||||
:compiler-options {:infer-externs :auto
|
||||
:externs ["datascript/externs.js"]
|
||||
:output-feature-set :es6}
|
||||
:modules {:main_bundle {:init-fn clojure-demo.core/startup!}}
|
||||
:devtools {:after-load app.main/reload!}}}}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
(ns ^:figwheel-hooks clojure-demo.core
|
||||
(:require ["react-dom/client" :refer [createRoot]]
|
||||
["@codemirror/state" :as cm-state :refer [EditorState Transaction] ]
|
||||
["@codemirror/language" :refer [LanguageSupport StreamLanguage]]
|
||||
["@codemirror/view" :as cm-view :refer [EditorView ViewUpdate]]
|
||||
["@codemirror/commands" :as cm-commands :refer [defaultKeymap]]
|
||||
["@codemirror/theme-one-dark" :refer [oneDark]]
|
||||
["@nextjournal/lang-clojure" :refer [clojure]]
|
||||
["@codemirror/legacy-modes/mode/yaml" :refer [yaml]]
|
||||
|
||||
[reagent.core :as reagent]
|
||||
[reagent.dom :refer [render]]
|
||||
[ajax.core :refer [GET raw-response-format]]
|
||||
;[demo.org :refer [parse->to-hiccup parse-flat]]
|
||||
;[cljs.js :as cljs]
|
||||
|
@ -25,12 +26,22 @@
|
|||
[sci.configs.tonsky.datascript :as sci-datascript]
|
||||
;;[klipse.run.plugin.plugin]
|
||||
;;[klipse.plugin :as klipse-plugin]
|
||||
;[hiccup.core :as h]
|
||||
[honey.sql :as sql]
|
||||
[honey.sql.helpers :as sqlh]
|
||||
[datascript.core :as d]
|
||||
;[datascript.core :as d]
|
||||
[spec-tools.data-spec :as ds]))
|
||||
|
||||
|
||||
(def languages {"clojure" {:mode "clojure"}
|
||||
"clojurescript" {:mode "clojure"}
|
||||
"yaml" {:mode "yaml"}})
|
||||
|
||||
(def yaml-mode (LanguageSupport. (.define StreamLanguage yaml) ))
|
||||
;const yaml = new LanguageSupport(StreamLanguage.define(yamlMode.yaml));
|
||||
|
||||
;; https://github.com/babashka/sci.configs
|
||||
;(def hc-ns (sci/create-ns 'hiccup.core nil))
|
||||
(def rf-ns (sci/create-ns 'reitit.frontend nil))
|
||||
(def rfe-ns (sci/create-ns 'reitit.frontend.easy nil))
|
||||
(def rss-ns (sci/create-ns 'reitit.coercion.spec nil))
|
||||
|
@ -40,7 +51,7 @@
|
|||
(sci/init
|
||||
{:namespaces
|
||||
{'reagent sci-reagent/reagent-namespace
|
||||
;'h
|
||||
;'h {'html (sci/copy-var h/html hc-ns)}
|
||||
'd sci-datascript/core-ns
|
||||
'sql {'format (sci/copy-var sql/format sql-ns)
|
||||
#_#_'raw (sci/copy-var sql/raw sql-ns)}
|
||||
|
@ -59,12 +70,14 @@
|
|||
;(def sci-ctx (sci/empty-environment))
|
||||
(sci/alter-var-root sci/print-fn (constantly *print-fn*))
|
||||
|
||||
(defn fetch-selected-text [v transactions]
|
||||
(defn fetch-selected-text
|
||||
"Get the users selected text"
|
||||
[updated-view transactions]
|
||||
(reduce (fn [text t]
|
||||
(if (= "select.pointer" (str (.annotation t (.-userEvent Transaction) )))
|
||||
(conj text
|
||||
(conj text
|
||||
(.sliceDoc
|
||||
^EditorState (.-state v)
|
||||
^EditorState (.-state updated-view)
|
||||
(.-from (.-main (.-selection t)))
|
||||
(.-to (.-main (.-selection t)))))
|
||||
text))
|
||||
|
@ -72,50 +85,62 @@
|
|||
transactions))
|
||||
|
||||
(defn code-editor
|
||||
[content]
|
||||
(let [editor (atom nil)
|
||||
[{:keys [class eval-mode]} content]
|
||||
(let [language class
|
||||
editor (atom nil)
|
||||
evaled-result (reagent/atom nil)
|
||||
update-timeout (reagent/atom nil)
|
||||
update-fn (.of (.-updateListener EditorView)
|
||||
(fn [^ViewUpdate view-update]
|
||||
(js/clearTimeout @update-timeout)
|
||||
(reset! update-timeout
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(let [selected-text (fetch-selected-text view-update (.-transactions view-update))
|
||||
eval-code (if (seq selected-text)
|
||||
(first selected-text)
|
||||
(.-doc (.-state view-update)))]
|
||||
(reset! evaled-result (sci/eval-string* sci-ctx
|
||||
(.toString eval-code) ))
|
||||
(prn "updated delayed"))) 1000))))
|
||||
(when (some #(= language %) ["clojure" "clojurescript"])
|
||||
(js/clearTimeout @update-timeout)
|
||||
(reset! update-timeout
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(let [selected-text (fetch-selected-text view-update (.-transactions view-update))
|
||||
eval-code (if (seq selected-text)
|
||||
(first selected-text)
|
||||
(.-doc (.-state view-update)))]
|
||||
(reset! evaled-result (sci/eval-string* sci-ctx
|
||||
(.toString eval-code) ))
|
||||
(prn "updated delayed"))) 1000)))))
|
||||
start-state
|
||||
(.create EditorState
|
||||
(clj->js {:doc content
|
||||
:mode "text/x-clojure"
|
||||
:extensions [(clojure) update-fn oneDark #_cm-keymap/of #_cm-commands/default-keymap]}))
|
||||
;:mode "text/yaml"
|
||||
:extensions [(clojure) yaml-mode update-fn oneDark #_cm-keymap/of #_cm-commands/default-keymap]}))
|
||||
view (atom nil)]
|
||||
(reagent/create-class
|
||||
{:component-did-mount
|
||||
(fn [_]
|
||||
(reset! view (EditorView.
|
||||
(clj->js {:state start-state
|
||||
:mode "clojure"
|
||||
:updateListener prn
|
||||
:parent @editor}))))
|
||||
(clj->js (merge {} #_(get languages language)
|
||||
{:state start-state
|
||||
;:mode "yaml"
|
||||
:mode "clojure"
|
||||
:updateListener prn
|
||||
:parent @editor})))))
|
||||
:component-will-unmount (fn [_] (.destroy @view))
|
||||
:reagent-render (fn []
|
||||
[:div
|
||||
[:div.editor {:ref #(reset! editor %)}]
|
||||
[:pre (str @evaled-result)]])})))
|
||||
[:div.ba.ma0.f5.b--black-05.pa2.overflow-auto.editor {:ref #(reset! editor %)}]
|
||||
[:pre.ba.ma0.f6.code.b--black-05.pa2.pl4.overflow-auto
|
||||
{:style {:white-space "pre-line"}}
|
||||
(case eval-mode
|
||||
"string" (str @evaled-result)
|
||||
"hiccup" @evaled-result
|
||||
@evaled-result)
|
||||
]])})))
|
||||
|
||||
|
||||
(defn code-snippet [settings content]
|
||||
#_(defn code-snippet [settings content]
|
||||
(let [snippet (reagent/atom nil)
|
||||
;;evaled (sci/eval-string* sci-ctx content )
|
||||
;;evaled (sci/eval-form sci-ctx content )
|
||||
]
|
||||
[code-editor (str content)]))
|
||||
[:div (str settings)
|
||||
[code-editor (:class settings) (str content)]]))
|
||||
|
||||
(defn link-handler [v _]
|
||||
(prn v)
|
||||
|
@ -130,20 +155,23 @@
|
|||
(defn link-handler4 [v _]
|
||||
(if (-> v second :img)
|
||||
(conj [:img] (dissoc (rename-keys (second v) {:href :src}) :img))
|
||||
(conj [:a] (second v) #_(or (when-not (clojure.string/blank? (last v)) (last v))
|
||||
(conj [:a] (second v) #_(or (when-not (clojure.string/blank? (last v)) (last v))
|
||||
{:href (:href (second v))}
|
||||
(:title (second v))))))
|
||||
|
||||
(def theme (merge tachyon-theme
|
||||
{:SRC code-snippet
|
||||
:LINK :a #_link-handler}))
|
||||
(def theme
|
||||
(merge tachyon-theme
|
||||
{:SRC code-editor
|
||||
:LINK :a #_link-handler}))
|
||||
|
||||
|
||||
;; :FILETAGS (fn [v] [:meta {:name "keywords" :content (str v)}])
|
||||
|
||||
;; put constant data here
|
||||
(def site-data
|
||||
{:demos {:sci-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."
|
||||
:demos
|
||||
{:sci-demo
|
||||
{:file "documents/sci-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
:dsl-demo
|
||||
{:file "documents/dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
|
@ -156,9 +184,9 @@
|
|||
:clojure-basics
|
||||
{:file "documents/clojure-basics.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
:ci-demo
|
||||
{:file "documents/ci-demo.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."})
|
||||
{:file "documents/ci-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
:containers
|
||||
{:file "documents/containers.org" :git-link "https://github.com/atomjuice/containers"}}})
|
||||
|
||||
;; Store site state
|
||||
(defonce site-state (reagent/atom {}))
|
||||
|
@ -213,8 +241,7 @@
|
|||
;; 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)]])
|
||||
(into [: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.
|
||||
|
@ -236,24 +263,35 @@
|
|||
:link (rfe/href ::demo {:page "sci-demo"})
|
||||
:img-src "https://clojure.org/images/clojure-logo-120b.png"}
|
||||
{:title "Clojure Basics"
|
||||
:description "Getting started with clojure syntax datatype's sequences conditions"
|
||||
:link (rfe/href ::demo {:page "clojure-basics"})
|
||||
:img-src "https://clojure.org/images/clojure-logo-120b.png"}
|
||||
{:title "DSL Demo"
|
||||
:link (rfe/href ::demo {:page "dsl-demo"})
|
||||
:img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"}
|
||||
{:title "Honey SQL Demo"
|
||||
:link (rfe/href ::demo {:page "honey-sql-demo"})
|
||||
:img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"}
|
||||
{:title "CI Demo"
|
||||
:description "Clojure in a CI pipeline"
|
||||
:link (rfe/href ::demo {:page "ci-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"
|
||||
:description "React application using reagent"
|
||||
:link (rfe/href ::demo {:page "reagent-demo"})
|
||||
:img-src "https://raw.githubusercontent.com/reagent-project/reagent/master/logo/logo-text.png"}]}]])
|
||||
|
||||
(defn dsl-page []
|
||||
[:<>
|
||||
[articles
|
||||
{:title "Clojure Demos"
|
||||
:body (-> site-data :homepage :intro)
|
||||
:articles
|
||||
[
|
||||
{:title "Hiccup HTML Demo"
|
||||
:link (rfe/href ::demo {:page "dsl-demo"})
|
||||
:img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"}
|
||||
{:title "Honey SQL Demo"
|
||||
:link (rfe/href ::demo {:page "honey-sql-demo"})
|
||||
:img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"}]}]])
|
||||
|
||||
;; form two component render demo
|
||||
(defn demo-page [route]
|
||||
(let [demo-key (keyword (-> route :parameters :path :page))
|
||||
|
@ -325,6 +363,10 @@
|
|||
:my-data "hi"
|
||||
:view home-page}]
|
||||
|
||||
["/dsl"
|
||||
{:name ::dsl
|
||||
:view dsl-page}]
|
||||
|
||||
["/about"
|
||||
{:name ::about
|
||||
:view about-page}]
|
||||
|
@ -342,6 +384,7 @@
|
|||
;;[:link {:rel "stylesheet" :href "https://raw.githubusercontent.com/FarhadG/code-mirror-themes/master/themes/rdark.css"}]
|
||||
;;[: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 ::dsl) :text "DSL's"}
|
||||
{:href (rfe/href ::about) :text "About"}
|
||||
{:href (rfe/href ::i-do-not-exist) :text "missing"}]]
|
||||
[:main.mt4
|
||||
|
|
Loading…
Reference in New Issue