├── .gitignore
├── README.md
├── app
├── chapter-03
│ ├── end
│ │ └── visitera
│ │ │ ├── .gitignore
│ │ │ ├── Capstanfile
│ │ │ ├── Dockerfile
│ │ │ ├── Procfile
│ │ │ ├── README.md
│ │ │ ├── dev-config.edn
│ │ │ ├── env
│ │ │ ├── dev
│ │ │ │ ├── clj
│ │ │ │ │ ├── user.clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ │ ├── env.clj
│ │ │ │ │ │ └── figwheel.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ ├── prod
│ │ │ │ ├── clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── env.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ └── test
│ │ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ │ ├── project.clj
│ │ │ ├── resources
│ │ │ ├── docs
│ │ │ │ └── docs.md
│ │ │ ├── html
│ │ │ │ ├── error.html
│ │ │ │ └── home.html
│ │ │ ├── migrations
│ │ │ │ └── schema.edn
│ │ │ └── public
│ │ │ │ ├── css
│ │ │ │ └── screen.css
│ │ │ │ ├── favicon.ico
│ │ │ │ └── img
│ │ │ │ └── warning_clojure.png
│ │ │ ├── src
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── config.clj
│ │ │ │ │ ├── core.clj
│ │ │ │ │ ├── db
│ │ │ │ │ └── core.clj
│ │ │ │ │ ├── handler.clj
│ │ │ │ │ ├── layout.clj
│ │ │ │ │ ├── middleware.clj
│ │ │ │ │ ├── middleware
│ │ │ │ │ └── formats.clj
│ │ │ │ │ ├── nrepl.clj
│ │ │ │ │ └── routes
│ │ │ │ │ └── home.clj
│ │ │ ├── cljc
│ │ │ │ └── visitera
│ │ │ │ │ └── validation.cljc
│ │ │ └── cljs
│ │ │ │ └── visitera
│ │ │ │ ├── ajax.cljs
│ │ │ │ ├── core.cljs
│ │ │ │ └── events.cljs
│ │ │ ├── test-config.edn
│ │ │ └── test
│ │ │ ├── clj
│ │ │ └── visitera
│ │ │ │ └── test
│ │ │ │ └── handler.clj
│ │ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── core_test.cljs
│ │ │ └── doo_runner.cljs
│ └── start
│ │ └── visitera
│ │ ├── .gitignore
│ │ ├── Capstanfile
│ │ ├── Dockerfile
│ │ ├── Procfile
│ │ ├── README.md
│ │ ├── dev-config.edn
│ │ ├── env
│ │ ├── dev
│ │ │ ├── clj
│ │ │ │ ├── user.clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ ├── env.clj
│ │ │ │ │ └── figwheel.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ ├── prod
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ └── env.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ └── test
│ │ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ │ ├── project.clj
│ │ ├── resources
│ │ ├── docs
│ │ │ └── docs.md
│ │ ├── html
│ │ │ ├── error.html
│ │ │ └── home.html
│ │ ├── migrations
│ │ │ └── schema.edn
│ │ └── public
│ │ │ ├── css
│ │ │ └── screen.css
│ │ │ ├── favicon.ico
│ │ │ └── img
│ │ │ └── warning_clojure.png
│ │ ├── src
│ │ ├── clj
│ │ │ └── visitera
│ │ │ │ ├── config.clj
│ │ │ │ ├── core.clj
│ │ │ │ ├── db
│ │ │ │ └── core.clj
│ │ │ │ ├── handler.clj
│ │ │ │ ├── layout.clj
│ │ │ │ ├── middleware.clj
│ │ │ │ ├── middleware
│ │ │ │ └── formats.clj
│ │ │ │ ├── nrepl.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ ├── cljc
│ │ │ └── visitera
│ │ │ │ └── validation.cljc
│ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── ajax.cljs
│ │ │ ├── core.cljs
│ │ │ └── events.cljs
│ │ ├── test-config.edn
│ │ └── test
│ │ ├── clj
│ │ └── visitera
│ │ │ └── test
│ │ │ └── handler.clj
│ │ └── cljs
│ │ └── visitera
│ │ ├── core_test.cljs
│ │ └── doo_runner.cljs
├── chapter-04
│ ├── end
│ │ └── visitera
│ │ │ ├── .gitignore
│ │ │ ├── Capstanfile
│ │ │ ├── Dockerfile
│ │ │ ├── Procfile
│ │ │ ├── README.md
│ │ │ ├── dev-config.edn
│ │ │ ├── env
│ │ │ ├── dev
│ │ │ │ ├── clj
│ │ │ │ │ ├── user.clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ │ ├── env.clj
│ │ │ │ │ │ └── figwheel.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ ├── prod
│ │ │ │ ├── clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── env.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ └── test
│ │ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ │ ├── project.clj
│ │ │ ├── resources
│ │ │ ├── docs
│ │ │ │ └── docs.md
│ │ │ ├── html
│ │ │ │ ├── error.html
│ │ │ │ └── home.html
│ │ │ ├── migrations
│ │ │ │ └── schema.edn
│ │ │ └── public
│ │ │ │ ├── css
│ │ │ │ └── screen.css
│ │ │ │ ├── favicon.ico
│ │ │ │ └── img
│ │ │ │ └── warning_clojure.png
│ │ │ ├── src
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── config.clj
│ │ │ │ │ ├── core.clj
│ │ │ │ │ ├── db
│ │ │ │ │ └── core.clj
│ │ │ │ │ ├── handler.clj
│ │ │ │ │ ├── layout.clj
│ │ │ │ │ ├── middleware.clj
│ │ │ │ │ ├── middleware
│ │ │ │ │ └── formats.clj
│ │ │ │ │ ├── nrepl.clj
│ │ │ │ │ └── routes
│ │ │ │ │ └── home.clj
│ │ │ ├── cljc
│ │ │ │ └── visitera
│ │ │ │ │ └── validation.cljc
│ │ │ └── cljs
│ │ │ │ └── visitera
│ │ │ │ ├── ajax.cljs
│ │ │ │ ├── core.cljs
│ │ │ │ └── events.cljs
│ │ │ ├── test-config.edn
│ │ │ └── test
│ │ │ ├── clj
│ │ │ └── visitera
│ │ │ │ └── test
│ │ │ │ └── handler.clj
│ │ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── core_test.cljs
│ │ │ └── doo_runner.cljs
│ └── start
│ │ └── visitera
│ │ ├── .gitignore
│ │ ├── Capstanfile
│ │ ├── Dockerfile
│ │ ├── Procfile
│ │ ├── README.md
│ │ ├── dev-config.edn
│ │ ├── env
│ │ ├── dev
│ │ │ ├── clj
│ │ │ │ ├── user.clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ ├── env.clj
│ │ │ │ │ └── figwheel.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ ├── prod
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ └── env.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ └── test
│ │ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ │ ├── project.clj
│ │ ├── resources
│ │ ├── docs
│ │ │ └── docs.md
│ │ ├── html
│ │ │ ├── error.html
│ │ │ └── home.html
│ │ ├── migrations
│ │ │ └── schema.edn
│ │ └── public
│ │ │ ├── css
│ │ │ └── screen.css
│ │ │ ├── favicon.ico
│ │ │ └── img
│ │ │ └── warning_clojure.png
│ │ ├── src
│ │ ├── clj
│ │ │ └── visitera
│ │ │ │ ├── config.clj
│ │ │ │ ├── core.clj
│ │ │ │ ├── db
│ │ │ │ └── core.clj
│ │ │ │ ├── handler.clj
│ │ │ │ ├── layout.clj
│ │ │ │ ├── middleware.clj
│ │ │ │ ├── middleware
│ │ │ │ └── formats.clj
│ │ │ │ ├── nrepl.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ ├── cljc
│ │ │ └── visitera
│ │ │ │ └── validation.cljc
│ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── ajax.cljs
│ │ │ ├── core.cljs
│ │ │ └── events.cljs
│ │ ├── test-config.edn
│ │ └── test
│ │ ├── clj
│ │ └── visitera
│ │ │ └── test
│ │ │ └── handler.clj
│ │ └── cljs
│ │ └── visitera
│ │ ├── core_test.cljs
│ │ └── doo_runner.cljs
├── chapter-05
│ ├── end
│ │ └── visitera
│ │ │ ├── .gitignore
│ │ │ ├── Capstanfile
│ │ │ ├── Dockerfile
│ │ │ ├── Procfile
│ │ │ ├── README.md
│ │ │ ├── dev-config.edn
│ │ │ ├── env
│ │ │ ├── dev
│ │ │ │ ├── clj
│ │ │ │ │ ├── user.clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ │ ├── env.clj
│ │ │ │ │ │ └── figwheel.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ ├── prod
│ │ │ │ ├── clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── env.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ └── test
│ │ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ │ ├── project.clj
│ │ │ ├── resources
│ │ │ ├── docs
│ │ │ │ └── docs.md
│ │ │ ├── html
│ │ │ │ ├── auth.html
│ │ │ │ ├── error.html
│ │ │ │ ├── home.html
│ │ │ │ ├── login.html
│ │ │ │ └── register.html
│ │ │ ├── migrations
│ │ │ │ └── schema.edn
│ │ │ └── public
│ │ │ │ ├── css
│ │ │ │ └── screen.css
│ │ │ │ ├── favicon.ico
│ │ │ │ └── img
│ │ │ │ └── warning_clojure.png
│ │ │ ├── src
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── config.clj
│ │ │ │ │ ├── core.clj
│ │ │ │ │ ├── db
│ │ │ │ │ └── core.clj
│ │ │ │ │ ├── handler.clj
│ │ │ │ │ ├── layout.clj
│ │ │ │ │ ├── middleware.clj
│ │ │ │ │ ├── middleware
│ │ │ │ │ └── formats.clj
│ │ │ │ │ ├── nrepl.clj
│ │ │ │ │ └── routes
│ │ │ │ │ └── home.clj
│ │ │ ├── cljc
│ │ │ │ └── visitera
│ │ │ │ │ └── validation.cljc
│ │ │ └── cljs
│ │ │ │ └── visitera
│ │ │ │ ├── ajax.cljs
│ │ │ │ ├── core.cljs
│ │ │ │ └── events.cljs
│ │ │ ├── test-config.edn
│ │ │ └── test
│ │ │ ├── clj
│ │ │ └── visitera
│ │ │ │ └── test
│ │ │ │ ├── handler.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── core_test.cljs
│ │ │ └── doo_runner.cljs
│ └── start
│ │ └── visitera
│ │ ├── .gitignore
│ │ ├── Capstanfile
│ │ ├── Dockerfile
│ │ ├── Procfile
│ │ ├── README.md
│ │ ├── dev-config.edn
│ │ ├── env
│ │ ├── dev
│ │ │ ├── clj
│ │ │ │ ├── user.clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ ├── env.clj
│ │ │ │ │ └── figwheel.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ ├── prod
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ └── env.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ └── test
│ │ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ │ ├── project.clj
│ │ ├── resources
│ │ ├── docs
│ │ │ └── docs.md
│ │ ├── html
│ │ │ ├── error.html
│ │ │ └── home.html
│ │ ├── migrations
│ │ │ └── schema.edn
│ │ └── public
│ │ │ ├── css
│ │ │ └── screen.css
│ │ │ ├── favicon.ico
│ │ │ └── img
│ │ │ └── warning_clojure.png
│ │ ├── src
│ │ ├── clj
│ │ │ └── visitera
│ │ │ │ ├── config.clj
│ │ │ │ ├── core.clj
│ │ │ │ ├── db
│ │ │ │ └── core.clj
│ │ │ │ ├── handler.clj
│ │ │ │ ├── layout.clj
│ │ │ │ ├── middleware.clj
│ │ │ │ ├── middleware
│ │ │ │ └── formats.clj
│ │ │ │ ├── nrepl.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ ├── cljc
│ │ │ └── visitera
│ │ │ │ └── validation.cljc
│ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── ajax.cljs
│ │ │ ├── core.cljs
│ │ │ └── events.cljs
│ │ ├── test-config.edn
│ │ └── test
│ │ ├── clj
│ │ └── visitera
│ │ │ └── test
│ │ │ └── handler.clj
│ │ └── cljs
│ │ └── visitera
│ │ ├── core_test.cljs
│ │ └── doo_runner.cljs
├── chapter-06
│ ├── end
│ │ └── visitera
│ │ │ ├── .gitignore
│ │ │ ├── Capstanfile
│ │ │ ├── Dockerfile
│ │ │ ├── Procfile
│ │ │ ├── README.md
│ │ │ ├── dev-config.edn
│ │ │ ├── env
│ │ │ ├── dev
│ │ │ │ ├── clj
│ │ │ │ │ ├── user.clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ │ ├── env.clj
│ │ │ │ │ │ └── figwheel.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ ├── prod
│ │ │ │ ├── clj
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── env.clj
│ │ │ │ ├── cljs
│ │ │ │ │ └── visitera
│ │ │ │ │ │ └── app.cljs
│ │ │ │ └── resources
│ │ │ │ │ ├── config.edn
│ │ │ │ │ └── logback.xml
│ │ │ └── test
│ │ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ │ ├── project.clj
│ │ │ ├── resources
│ │ │ ├── docs
│ │ │ │ └── docs.md
│ │ │ ├── html
│ │ │ │ ├── auth.html
│ │ │ │ ├── error.html
│ │ │ │ ├── home.html
│ │ │ │ ├── login.html
│ │ │ │ └── register.html
│ │ │ ├── migrations
│ │ │ │ ├── countries-data.edn
│ │ │ │ ├── schema.edn
│ │ │ │ └── test-data.edn
│ │ │ ├── public
│ │ │ │ ├── css
│ │ │ │ │ └── screen.css
│ │ │ │ ├── favicon.ico
│ │ │ │ └── img
│ │ │ │ │ └── warning_clojure.png
│ │ │ └── raw
│ │ │ │ ├── data.edn
│ │ │ │ ├── parsed-data.edn
│ │ │ │ └── transform-data.clj
│ │ │ ├── src
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── config.clj
│ │ │ │ │ ├── core.clj
│ │ │ │ │ ├── db
│ │ │ │ │ └── core.clj
│ │ │ │ │ ├── handler.clj
│ │ │ │ │ ├── layout.clj
│ │ │ │ │ ├── middleware.clj
│ │ │ │ │ ├── middleware
│ │ │ │ │ └── formats.clj
│ │ │ │ │ ├── nrepl.clj
│ │ │ │ │ └── routes
│ │ │ │ │ └── home.clj
│ │ │ ├── cljc
│ │ │ │ └── visitera
│ │ │ │ │ └── validation.cljc
│ │ │ └── cljs
│ │ │ │ └── visitera
│ │ │ │ ├── ajax.cljs
│ │ │ │ ├── components
│ │ │ │ ├── map.cljs
│ │ │ │ └── navbar.cljs
│ │ │ │ ├── config.cljs
│ │ │ │ ├── core.cljs
│ │ │ │ └── events.cljs
│ │ │ ├── test-config.edn
│ │ │ └── test
│ │ │ ├── clj
│ │ │ └── visitera
│ │ │ │ └── test
│ │ │ │ ├── handler.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── core_test.cljs
│ │ │ └── doo_runner.cljs
│ └── start
│ │ └── visitera
│ │ ├── .gitignore
│ │ ├── Capstanfile
│ │ ├── Dockerfile
│ │ ├── Procfile
│ │ ├── README.md
│ │ ├── dev-config.edn
│ │ ├── env
│ │ ├── dev
│ │ │ ├── clj
│ │ │ │ ├── user.clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ ├── env.clj
│ │ │ │ │ └── figwheel.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ ├── prod
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ └── env.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ └── test
│ │ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ │ ├── project.clj
│ │ ├── resources
│ │ ├── docs
│ │ │ └── docs.md
│ │ ├── html
│ │ │ ├── auth.html
│ │ │ ├── error.html
│ │ │ ├── home.html
│ │ │ ├── login.html
│ │ │ └── register.html
│ │ ├── migrations
│ │ │ └── schema.edn
│ │ └── public
│ │ │ ├── css
│ │ │ └── screen.css
│ │ │ ├── favicon.ico
│ │ │ └── img
│ │ │ └── warning_clojure.png
│ │ ├── src
│ │ ├── clj
│ │ │ └── visitera
│ │ │ │ ├── config.clj
│ │ │ │ ├── core.clj
│ │ │ │ ├── db
│ │ │ │ └── core.clj
│ │ │ │ ├── handler.clj
│ │ │ │ ├── layout.clj
│ │ │ │ ├── middleware.clj
│ │ │ │ ├── middleware
│ │ │ │ └── formats.clj
│ │ │ │ ├── nrepl.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ ├── cljc
│ │ │ └── visitera
│ │ │ │ └── validation.cljc
│ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── ajax.cljs
│ │ │ ├── core.cljs
│ │ │ └── events.cljs
│ │ ├── test-config.edn
│ │ └── test
│ │ ├── clj
│ │ └── visitera
│ │ │ └── test
│ │ │ ├── handler.clj
│ │ │ └── routes
│ │ │ └── home.clj
│ │ └── cljs
│ │ └── visitera
│ │ ├── core_test.cljs
│ │ └── doo_runner.cljs
└── chapter-07
│ ├── end
│ └── visitera
│ │ ├── .gitignore
│ │ ├── Capstanfile
│ │ ├── Dockerfile
│ │ ├── Procfile
│ │ ├── README.md
│ │ ├── datomic
│ │ └── docker-compose.yml
│ │ ├── dev-config.edn
│ │ ├── docker-compose.yml
│ │ ├── env
│ │ ├── dev
│ │ │ ├── clj
│ │ │ │ ├── user.clj
│ │ │ │ └── visitera
│ │ │ │ │ ├── dev_middleware.clj
│ │ │ │ │ ├── env.clj
│ │ │ │ │ └── figwheel.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ ├── prod
│ │ │ ├── clj
│ │ │ │ └── visitera
│ │ │ │ │ └── env.clj
│ │ │ ├── cljs
│ │ │ │ └── visitera
│ │ │ │ │ └── app.cljs
│ │ │ └── resources
│ │ │ │ ├── config.edn
│ │ │ │ └── logback.xml
│ │ └── test
│ │ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ │ ├── project.clj
│ │ ├── resources
│ │ ├── docs
│ │ │ └── docs.md
│ │ ├── html
│ │ │ ├── auth.html
│ │ │ ├── error.html
│ │ │ ├── home.html
│ │ │ ├── login.html
│ │ │ └── register.html
│ │ ├── migrations
│ │ │ ├── countries-data.edn
│ │ │ ├── schema.edn
│ │ │ └── test-data.edn
│ │ ├── public
│ │ │ ├── css
│ │ │ │ └── screen.css
│ │ │ ├── favicon.ico
│ │ │ └── img
│ │ │ │ └── warning_clojure.png
│ │ └── raw
│ │ │ ├── data.edn
│ │ │ ├── parsed-data.edn
│ │ │ └── transform-data.clj
│ │ ├── src
│ │ ├── clj
│ │ │ └── visitera
│ │ │ │ ├── config.clj
│ │ │ │ ├── core.clj
│ │ │ │ ├── db
│ │ │ │ └── core.clj
│ │ │ │ ├── handler.clj
│ │ │ │ ├── layout.clj
│ │ │ │ ├── middleware.clj
│ │ │ │ ├── middleware
│ │ │ │ └── formats.clj
│ │ │ │ ├── nrepl.clj
│ │ │ │ └── routes
│ │ │ │ └── home.clj
│ │ ├── cljc
│ │ │ └── visitera
│ │ │ │ └── validation.cljc
│ │ └── cljs
│ │ │ └── visitera
│ │ │ ├── ajax.cljs
│ │ │ ├── components
│ │ │ ├── map.cljs
│ │ │ └── navbar.cljs
│ │ │ ├── config.cljs
│ │ │ ├── core.cljs
│ │ │ └── events.cljs
│ │ ├── test-config.edn
│ │ └── test
│ │ ├── clj
│ │ └── visitera
│ │ │ └── test
│ │ │ ├── handler.clj
│ │ │ └── routes
│ │ │ └── home.clj
│ │ └── cljs
│ │ └── visitera
│ │ ├── core_test.cljs
│ │ └── doo_runner.cljs
│ └── start
│ └── visitera
│ ├── .gitignore
│ ├── Capstanfile
│ ├── Dockerfile
│ ├── Procfile
│ ├── README.md
│ ├── dev-config.edn
│ ├── env
│ ├── dev
│ │ ├── clj
│ │ │ ├── user.clj
│ │ │ └── visitera
│ │ │ │ ├── dev_middleware.clj
│ │ │ │ ├── env.clj
│ │ │ │ └── figwheel.clj
│ │ ├── cljs
│ │ │ └── visitera
│ │ │ │ └── app.cljs
│ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ ├── prod
│ │ ├── clj
│ │ │ └── visitera
│ │ │ │ └── env.clj
│ │ ├── cljs
│ │ │ └── visitera
│ │ │ │ └── app.cljs
│ │ └── resources
│ │ │ ├── config.edn
│ │ │ └── logback.xml
│ └── test
│ │ └── resources
│ │ ├── config.edn
│ │ └── logback.xml
│ ├── project.clj
│ ├── resources
│ ├── docs
│ │ └── docs.md
│ ├── html
│ │ ├── auth.html
│ │ ├── error.html
│ │ ├── home.html
│ │ ├── login.html
│ │ └── register.html
│ ├── migrations
│ │ ├── countries-data.edn
│ │ ├── schema.edn
│ │ └── test-data.edn
│ ├── public
│ │ ├── css
│ │ │ └── screen.css
│ │ ├── favicon.ico
│ │ └── img
│ │ │ └── warning_clojure.png
│ └── raw
│ │ ├── data.edn
│ │ ├── parsed-data.edn
│ │ └── transform-data.clj
│ ├── src
│ ├── clj
│ │ └── visitera
│ │ │ ├── config.clj
│ │ │ ├── core.clj
│ │ │ ├── db
│ │ │ └── core.clj
│ │ │ ├── handler.clj
│ │ │ ├── layout.clj
│ │ │ ├── middleware.clj
│ │ │ ├── middleware
│ │ │ └── formats.clj
│ │ │ ├── nrepl.clj
│ │ │ └── routes
│ │ │ └── home.clj
│ ├── cljc
│ │ └── visitera
│ │ │ └── validation.cljc
│ └── cljs
│ │ └── visitera
│ │ ├── ajax.cljs
│ │ ├── components
│ │ ├── map.cljs
│ │ └── navbar.cljs
│ │ ├── config.cljs
│ │ ├── core.cljs
│ │ └── events.cljs
│ ├── test-config.edn
│ └── test
│ ├── clj
│ └── visitera
│ │ └── test
│ │ ├── handler.clj
│ │ └── routes
│ │ └── home.clj
│ └── cljs
│ └── visitera
│ ├── core_test.cljs
│ └── doo_runner.cljs
└── tutorial
├── chapter-01
├── 01-The app idea.md
├── login.png
└── map.png
├── chapter-02
└── 02-Choosing the stack.md
├── chapter-03
└── 03-Environment set up.md
├── chapter-04
└── 04-Data modeling.md
├── chapter-05
├── 05-Registration and authentication.md
├── Authentication Flow.svg
└── Registration Flow.svg
├── chapter-06
├── 06-Working on UI.md
├── Re-frame.svg
└── components.svg
└── chapter-07
└── 07-Going live.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .stackedit-*
2 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]))
10 |
11 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
12 |
13 | (add-tap (bound-fn* clojure.pprint/pprint))
14 |
15 | (defn start
16 | "Starts application.
17 | You'll usually want to run this on startup."
18 | []
19 | (mount/start-without #'visitera.core/repl-server))
20 |
21 | (defn stop
22 | "Stops application."
23 | []
24 | (mount/stop-except #'visitera.core/repl-server))
25 |
26 | (defn restart
27 | "Restarts application."
28 | []
29 | (stop)
30 | (start))
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-03/end/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-03/end/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/clj/visitera/routes/home.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.routes.home
2 | (:require
3 | [visitera.layout :as layout]
4 | [clojure.java.io :as io]
5 | [visitera.middleware :as middleware]
6 | [ring.util.http-response :as response]
7 | [visitera.db.core :refer [conn find-user]]
8 | [datomic.api :as d]))
9 |
10 | (defn home-page [request]
11 | (layout/render request "home.html"))
12 |
13 | (defn home-routes []
14 | [""
15 | {:middleware [middleware/wrap-csrf
16 | middleware/wrap-formats]}
17 | ["/" {:get home-page}]
18 | ["/db-test" {:get (fn [_]
19 | (let [db (d/db conn)
20 | user (find-user db "abc")]
21 | (-> (response/ok (:user/name user))
22 | (response/header "Content-Type" "text/plain; charset=utf-8"))))}]
23 | ["/docs" {:get (fn [_]
24 | (-> (response/ok (-> "docs/docs.md" io/resource slurp))
25 | (response/header "Content-Type" "text/plain; charset=utf-8")))}]])
26 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | ; :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 200 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-03/end/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 |
15 | /node_modules
16 | /log
17 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | ; :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]))
10 |
11 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
12 |
13 | (add-tap (bound-fn* clojure.pprint/pprint))
14 |
15 | (defn start
16 | "Starts application.
17 | You'll usually want to run this on startup."
18 | []
19 | (mount/start-without #'visitera.core/repl-server))
20 |
21 | (defn stop
22 | "Stops application."
23 | []
24 | (mount/stop-except #'visitera.core/repl-server))
25 |
26 | (defn restart
27 | "Restarts application."
28 | []
29 | (stop)
30 | (start))
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-03/start/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-03/start/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/clj/visitera/routes/home.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.routes.home
2 | (:require
3 | [visitera.layout :as layout]
4 | [clojure.java.io :as io]
5 | [visitera.middleware :as middleware]
6 | [ring.util.http-response :as response]))
7 |
8 | (defn home-page [request]
9 | (layout/render request "home.html"))
10 |
11 | (defn home-routes []
12 | [""
13 | {:middleware [middleware/wrap-csrf
14 | middleware/wrap-formats]}
15 | ["/" {:get home-page}]
16 | ["/docs" {:get (fn [_]
17 | (-> (response/ok (-> "docs/docs.md" io/resource slurp))
18 | (response/header "Content-Type" "text/plain; charset=utf-8")))}]])
19 |
20 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | ; :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 200 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-03/start/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-04/end/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-04/end/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/clj/visitera/routes/home.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.routes.home
2 | (:require
3 | [visitera.layout :as layout]
4 | [clojure.java.io :as io]
5 | [visitera.middleware :as middleware]
6 | [ring.util.http-response :as response]
7 | [visitera.db.core :refer [conn find-user]]
8 | [datomic.api :as d]))
9 |
10 | (defn home-page [request]
11 | (layout/render request "home.html"))
12 |
13 | (defn home-routes []
14 | [""
15 | {:middleware [middleware/wrap-csrf
16 | middleware/wrap-formats]}
17 | ["/" {:get home-page}]
18 | ["/db-test" {:get (fn [_]
19 | (let [db (d/db conn)
20 | user (find-user db "abc")]
21 | (-> (response/ok (:user/name user))
22 | (response/header "Content-Type" "text/plain; charset=utf-8"))))}]
23 | ["/docs" {:get (fn [_]
24 | (-> (response/ok (-> "docs/docs.md" io/resource slurp))
25 | (response/header "Content-Type" "text/plain; charset=utf-8")))}]])
26 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | ; :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 200 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-04/end/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]))
10 |
11 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
12 |
13 | (add-tap (bound-fn* clojure.pprint/pprint))
14 |
15 | (defn start
16 | "Starts application.
17 | You'll usually want to run this on startup."
18 | []
19 | (mount/start-without #'visitera.core/repl-server))
20 |
21 | (defn stop
22 | "Stops application."
23 | []
24 | (mount/stop-except #'visitera.core/repl-server))
25 |
26 | (defn restart
27 | "Restarts application."
28 | []
29 | (stop)
30 | (start))
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-04/start/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-04/start/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/clj/visitera/routes/home.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.routes.home
2 | (:require
3 | [visitera.layout :as layout]
4 | [clojure.java.io :as io]
5 | [visitera.middleware :as middleware]
6 | [ring.util.http-response :as response]
7 | [visitera.db.core :refer [conn find-user]]
8 | [datomic.api :as d]))
9 |
10 | (defn home-page [request]
11 | (layout/render request "home.html"))
12 |
13 | (defn home-routes []
14 | [""
15 | {:middleware [middleware/wrap-csrf
16 | middleware/wrap-formats]}
17 | ["/" {:get home-page}]
18 | ["/db-test" {:get (fn [_]
19 | (let [db (d/db conn)
20 | user (find-user db "abc")]
21 | (-> (response/ok (:user/name user))
22 | (response/header "Content-Type" "text/plain; charset=utf-8"))))}]
23 | ["/docs" {:get (fn [_]
24 | (-> (response/ok (-> "docs/docs.md" io/resource slurp))
25 | (response/header "Content-Type" "text/plain; charset=utf-8")))}]])
26 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | ; :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 200 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-04/start/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-05/end/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-05/end/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
4 | (def register-schema
5 | [[:email
6 | st/required
7 | st/string
8 | st/email]
9 |
10 | [:password
11 | st/required
12 | st/string
13 | {:message "password must contain at least 8 characters"
14 | :validate #(> (count %) 7)}]])
15 |
16 | (def login-schema
17 | [[:email
18 | st/required
19 | st/string
20 | st/email]
21 |
22 | [:password
23 | st/required
24 | st/string]])
25 |
26 | (defn validate-register [params]
27 | (first (st/validate params register-schema)))
28 |
29 | (defn validate-login [params]
30 | (first (st/validate params login-schema)))
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 302 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-05/end/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | .classpath
11 | /.lein-*
12 | profiles.clj
13 | .project
14 | .settings
15 | /.env
16 | .nrepl-port
17 | /.rebel_*
18 |
19 | /node_modules
20 | /log
21 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-05/start/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-05/start/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/clj/visitera/routes/home.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.routes.home
2 | (:require
3 | [visitera.layout :as layout]
4 | [clojure.java.io :as io]
5 | [visitera.middleware :as middleware]
6 | [ring.util.http-response :as response]
7 | [visitera.db.core :refer [conn find-user]]
8 | [datomic.api :as d]))
9 |
10 | (defn home-page [request]
11 | (layout/render request "home.html"))
12 |
13 | (defn home-routes []
14 | [""
15 | {:middleware [middleware/wrap-csrf
16 | middleware/wrap-formats]}
17 | ["/" {:get home-page}]
18 | ["/db-test" {:get (fn [_]
19 | (let [db (d/db conn)
20 | user (find-user db "abc")]
21 | (-> (response/ok (:user/name user))
22 | (response/header "Content-Type" "text/plain; charset=utf-8"))))}]
23 | ["/docs" {:get (fn [_]
24 | (-> (response/ok (-> "docs/docs.md" io/resource slurp))
25 | (response/header "Content-Type" "text/plain; charset=utf-8")))}]])
26 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | ; :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 200 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-05/start/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | 1. [Leiningen]
10 | 2. [Datomic]
11 |
12 | [leiningen]: https://github.com/technomancy/leiningen
13 | [datomic]: https://my.datomic.com/downloads/free
14 |
15 | ## Running
16 |
17 | 1. Run datomic
18 |
19 | - `cd {datomic-folder}`
20 | - `bin/transactor config/samples/free-transactor-template.properties`
21 | - _Optional gui console_: `bin/console -p 8080 dev datomic:free://localhost:4334`
22 |
23 | 2. Start a web server:
24 |
25 | - `lein repl`
26 | - `(start)`
27 |
28 | 3. Start clientYou will need [Leiningen][1] 2.0 or above installed.
29 |
30 | [1]: https://github.com/technomancy/leiningen
31 |
32 | ## Running
33 |
34 | To start a web server for the application, run:
35 |
36 | - ` lein figwheel`
37 | - Go to `localhost:3001` in your browserrun
38 |
39 | ## License
40 |
41 | Copyright © 2019 Aliaksandr SushkevichFIXME
42 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/resources/html/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Welcome to visitera
7 |
8 |
9 |
10 |
11 |
12 | {% style "/assets/bulma/css/bulma.min.css" %}
13 | {% style "/assets/material-icons/css/material-icons.min.css" %}
14 | {% style "/css/screen.css" %}
15 |
16 |
19 |
20 |
21 |
22 |
23 | {% script "/js/app.js" %}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/resources/migrations/test-data.edn:
--------------------------------------------------------------------------------
1 | {:visitera/data2
2 | {:txes
3 | [[{:user/email
4 | "test@user.com"
5 | :user/password
6 | ; somepass
7 | "bcrypt+sha512$c0d6f8f472f9312d1ac5cb84b39c858e$12$72eb4c3d6d0f6148c66657da865705f67c1914ef1d66fd2a"
8 | :user/countries-to-visit
9 | [{:country/name "Zambia"}
10 | {:country/name "France"}
11 | {:country/name "Albania"}
12 | {:country/name "Andorra"}]
13 | :user/countries-visited
14 | [{:country/alpha-2 "RU"}
15 | {:country/alpha-2 "CZ"}
16 | {:country/alpha-2 "US"}]}
17 |
18 | {:user/email
19 | "test@user-1.com"
20 | :user/password
21 | ; somepass
22 | "bcrypt+sha512$c0d6f8f472f9312d1ac5cb84b39c858e$12$72eb4c3d6d0f6148c66657da865705f67c1914ef1d66fd2a"}]]}}
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-06/end/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-06/end/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/resources/raw/transform-data.clj:
--------------------------------------------------------------------------------
1 | (defn get-raw-data [] (->
2 | (slurp "./resources/raw/data.edn")
3 | (read-string)
4 | (eval)))
5 |
6 | (def keys [:name :country-code :alpha-2 :alpha-3])
7 | (def new-keys {:name :country/name
8 | :country-code :country/code
9 | :alpha-2 :country/alpha-2
10 | :alpha-3 :country/alpha-3})
11 |
12 | (defn transform [country]
13 | (-> country
14 | (select-keys keys)
15 | (clojure.set/rename-keys new-keys)))
16 |
17 | (defn wrap-with-template [data]
18 | (str {:visitera/data1 {:txes [(vec data)]}}))
19 |
20 | (defn save-parsed []
21 | (spit "./resources/raw/parsed-data.edn"
22 | (binding [*print-namespace-maps* false]
23 | (->>
24 | (get-raw-data)
25 | (map transform)
26 | (wrap-with-template)))))
27 |
28 | (save-parsed)
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
4 | (def register-schema
5 | [[:email
6 | st/required
7 | st/string
8 | st/email]
9 |
10 | [:password
11 | st/required
12 | st/string
13 | {:message "password must contain at least 8 characters"
14 | :validate #(> (count %) 7)}]])
15 |
16 | (def login-schema
17 | [[:email
18 | st/required
19 | st/string
20 | st/email]
21 |
22 | [:password
23 | st/required
24 | st/string]])
25 |
26 | (defn validate-register [params]
27 | (first (st/validate params register-schema)))
28 |
29 | (defn validate-login [params]
30 | (first (st/validate params login-schema)))
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/cljs/visitera/components/navbar.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.components.navbar
2 | (:require
3 | [reagent.core :as r]))
4 |
5 | (defn navbar []
6 | (r/with-let [expanded? (r/atom false)]
7 | [:nav.navbar.is-primary>div.container
8 | [:div.navbar-brand
9 | [:a.navbar-item {:href "/" :style {:font-weight :bold}} "visitera"]
10 | [:span.navbar-burger.burger
11 | {:data-target :nav-menu
12 | :on-click #(swap! expanded? not)
13 | :class (when @expanded? :is-active)}
14 | [:span] [:span] [:span]]]
15 | [:div#nav-menu.navbar-menu
16 | {:class (when @expanded? :is-active)}
17 | [:div.navbar-end
18 | [:a.navbar-item {:href "/logout"} "Logout"]]]]))
19 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/src/cljs/visitera/config.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.config)
2 |
3 | (def colors
4 | {:to-visit "#f0dd92"
5 | :visited "#83b582"
6 | :not-visited "#dddddd"})
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 302 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-06/end/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | FIXME
6 |
7 | ## Prerequisites
8 |
9 | You will need [Leiningen][1] 2.0 or above installed.
10 |
11 | [1]: https://github.com/technomancy/leiningen
12 |
13 | ## Running
14 |
15 | To start a web server for the application, run:
16 |
17 | lein run
18 |
19 | ## License
20 |
21 | Copyright © 2019 FIXME
22 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-06/start/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-06/start/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
4 | (def register-schema
5 | [[:email
6 | st/required
7 | st/string
8 | st/email]
9 |
10 | [:password
11 | st/required
12 | st/string
13 | {:message "password must contain at least 8 characters"
14 | :validate #(> (count %) 7)}]])
15 |
16 | (def login-schema
17 | [[:email
18 | st/required
19 | st/string
20 | st/email]
21 |
22 | [:password
23 | st/required
24 | st/string]])
25 |
26 | (defn validate-register [params]
27 | (first (st/validate params register-schema)))
28 |
29 | (defn validate-login [params]
30 | (first (st/validate params login-schema)))
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/src/cljs/visitera/events.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.events
2 | (:require
3 | [re-frame.core :as rf]
4 | [ajax.core :as ajax]))
5 |
6 | ;;dispatchers
7 |
8 | (rf/reg-event-db
9 | :navigate
10 | (fn [db [_ route]]
11 | (assoc db :route route)))
12 |
13 | (rf/reg-event-db
14 | :set-docs
15 | (fn [db [_ docs]]
16 | (assoc db :docs docs)))
17 |
18 | (rf/reg-event-fx
19 | :fetch-docs
20 | (fn [_ _]
21 | {:http-xhrio {:method :get
22 | :uri "/docs"
23 | :response-format (ajax/raw-response-format)
24 | :on-success [:set-docs]}}))
25 |
26 | (rf/reg-event-db
27 | :common/set-error
28 | (fn [db [_ error]]
29 | (assoc db :common/error error)))
30 |
31 | ;;subscriptions
32 |
33 | (rf/reg-sub
34 | :route
35 | (fn [db _]
36 | (-> db :route)))
37 |
38 | (rf/reg-sub
39 | :page
40 | :<- [:route]
41 | (fn [route _]
42 | (-> route :data :name)))
43 |
44 | (rf/reg-sub
45 | :docs
46 | (fn [db _]
47 | (:docs db)))
48 |
49 | (rf/reg-sub
50 | :common/error
51 | (fn [db _]
52 | (:common/error db)))
53 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 302 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-06/start/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 | /data
19 |
20 | /datomic/data
21 | /datomic/log
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM clojure
2 |
3 | RUN mkdir -p /usr/src/app
4 | WORKDIR /usr/src/app
5 |
6 | COPY project.clj /usr/src/app/
7 |
8 | RUN lein deps
9 | COPY . /usr/src/app
10 |
11 | RUN lein uberjar
12 | RUN mv ./target/uberjar/visitera.jar /usr/src/app/visitera.jar
13 |
14 | CMD ["java", "-jar", "/usr/src/app/visitera.jar"]
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | ## Prerequisites
6 |
7 | 1. [Leiningen]
8 | 2. [Datomic]
9 |
10 | [leiningen]: https://github.com/technomancy/leiningen
11 | [datomic]: https://my.datomic.com/downloads/free
12 |
13 | ## Running
14 |
15 | 1. Run datomic
16 |
17 | 1.1 Downloaded version
18 |
19 | - `cd {datomic-folder}`
20 | - `bin/transactor config/samples/free-transactor-template.properties`
21 | - _Optional gui console_: `bin/console -p 8080 dev datomic:free://localhost:4334`
22 |
23 | 1.2 Using docker
24 |
25 | - `cd datomic`
26 | - `docker-compose up`
27 |
28 | 2. Start a web server:
29 |
30 | - `lein repl`
31 | - `(start)`
32 |
33 | 3. Start client:
34 |
35 | - `lein figwheel`
36 | - Go to `localhost:3001` in your browser
37 |
38 | ## License
39 |
40 | Copyright © 2019 Aliaksandr Sushkevich
41 |
42 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/datomic/docker-compose.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: '3'
3 |
4 | services:
5 | db:
6 | image: akiel/datomic-free
7 | ports:
8 | - "4334-4336:4334-4336"
9 | environment:
10 | DATOMIC_PASSWORD: datomic
11 | ADMIN_PASSWORD: admin
12 | volumes:
13 | - ./data:/data
14 | - ./log:/log
15 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev?password=datomic"
12 | ; :database-url "datomic:free://localhost:4334/visitera_dev"
13 |
14 | ; alternatively, you can use the datomic mem db for development:
15 | ; :database-url "datomic:mem://visitera_datomic_dev"
16 | }
17 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/docker-compose.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: '3'
3 |
4 | services:
5 | db:
6 | image: akiel/datomic-free
7 | environment:
8 | DATOMIC_PASSWORD: datomic
9 | ADMIN_PASSWORD: admin
10 | ALT_HOST: db
11 | volumes:
12 | - ./data:/data
13 | - ./log:/log
14 |
15 | app:
16 | build: .
17 | ports:
18 | - "80:3000"
19 | depends_on:
20 | - db
21 | environment:
22 | - DATABASE_URL=datomic:free://db:4334/visitera_prod?password=datomic
23 | volumes:
24 | - ./log:/log
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000
3 | :database-url "datomic:free://localhost:4334/visitera_dev"
4 | }
5 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/resources/html/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Welcome to visitera
7 |
8 |
9 |
10 |
11 |
12 | {% style "/assets/bulma/css/bulma.min.css" %}
13 | {% style "/assets/material-icons/css/material-icons.min.css" %}
14 | {% style "/css/screen.css" %}
15 |
16 |
19 |
20 |
21 |
22 |
23 | {% script "/js/app.js" %}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/resources/migrations/test-data.edn:
--------------------------------------------------------------------------------
1 | {:visitera/data2
2 | {:txes
3 | [[{:user/email
4 | "test@user.com"
5 | :user/password
6 | ; somepass
7 | "bcrypt+sha512$c0d6f8f472f9312d1ac5cb84b39c858e$12$72eb4c3d6d0f6148c66657da865705f67c1914ef1d66fd2a"
8 | :user/countries-to-visit
9 | [{:country/name "Zambia"}
10 | {:country/name "France"}
11 | {:country/name "Albania"}
12 | {:country/name "Andorra"}]
13 | :user/countries-visited
14 | [{:country/alpha-2 "RU"}
15 | {:country/alpha-2 "CZ"}
16 | {:country/alpha-2 "US"}]}
17 |
18 | {:user/email
19 | "test@user-1.com"
20 | :user/password
21 | ; somepass
22 | "bcrypt+sha512$c0d6f8f472f9312d1ac5cb84b39c858e$12$72eb4c3d6d0f6148c66657da865705f67c1914ef1d66fd2a"}]]}}
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-07/end/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-07/end/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/resources/raw/transform-data.clj:
--------------------------------------------------------------------------------
1 | (defn get-raw-data [] (->
2 | (slurp "./resources/raw/data.edn")
3 | (read-string)
4 | (eval)))
5 |
6 | (def keys [:name :country-code :alpha-2 :alpha-3])
7 | (def new-keys {:name :country/name
8 | :country-code :country/code
9 | :alpha-2 :country/alpha-2
10 | :alpha-3 :country/alpha-3})
11 |
12 | (defn transform [country]
13 | (-> country
14 | (select-keys keys)
15 | (clojure.set/rename-keys new-keys)))
16 |
17 | (defn wrap-with-template [data]
18 | (str {:visitera/data1 {:txes [(vec data)]}}))
19 |
20 | (defn save-parsed []
21 | (spit "./resources/raw/parsed-data.edn"
22 | (binding [*print-namespace-maps* false]
23 | (->>
24 | (get-raw-data)
25 | (map transform)
26 | (wrap-with-template)))))
27 |
28 | (save-parsed)
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
4 | (def register-schema
5 | [[:email
6 | st/required
7 | st/string
8 | st/email]
9 |
10 | [:password
11 | st/required
12 | st/string
13 | {:message "password must contain at least 8 characters"
14 | :validate #(> (count %) 7)}]])
15 |
16 | (def login-schema
17 | [[:email
18 | st/required
19 | st/string
20 | st/email]
21 |
22 | [:password
23 | st/required
24 | st/string]])
25 |
26 | (defn validate-register [params]
27 | (first (st/validate params register-schema)))
28 |
29 | (defn validate-login [params]
30 | (first (st/validate params login-schema)))
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/cljs/visitera/components/navbar.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.components.navbar
2 | (:require
3 | [reagent.core :as r]))
4 |
5 | (defn navbar []
6 | (r/with-let [expanded? (r/atom false)]
7 | [:nav.navbar.is-primary>div.container
8 | [:div.navbar-brand
9 | [:a.navbar-item {:href "/" :style {:font-weight :bold}} "visitera"]
10 | [:span.navbar-burger.burger
11 | {:data-target :nav-menu
12 | :on-click #(swap! expanded? not)
13 | :class (when @expanded? :is-active)}
14 | [:span] [:span] [:span]]]
15 | [:div#nav-menu.navbar-menu
16 | {:class (when @expanded? :is-active)}
17 | [:div.navbar-end
18 | [:a.navbar-item {:href "/logout"} "Logout"]]]]))
19 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/src/cljs/visitera/config.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.config)
2 |
3 | (def colors
4 | {:to-visit "#f0dd92"
5 | :visited "#83b582"
6 | :not-visited "#dddddd"})
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 302 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-07/end/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | # dev-config.edn
7 | # test-config.edn
8 | *.jar
9 | *.class
10 | /.lein-*
11 | profiles.clj
12 | /.env
13 | .nrepl-port
14 | /.rebel_*
15 |
16 | /node_modules
17 | /log
18 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/Capstanfile:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # Name of the base image. Capstan will download this automatically from
4 | # Cloudius S3 repository.
5 | #
6 | #base: cloudius/osv
7 | base: cloudius/osv-openjdk8
8 |
9 | #
10 | # The command line passed to OSv to start up the application.
11 | #
12 | cmdline: /java.so -jar /visitera/app.jar
13 |
14 | #
15 | # The command to use to build the application.
16 | # You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
17 | #
18 | # For Leiningen, you can use:
19 | #build: lein uberjar
20 | # For Boot, you can use:
21 | #build: boot build
22 |
23 | #
24 | # List of files that are included in the generated image.
25 | #
26 | files:
27 | /visitera/app.jar: ./target/uberjar/visitera.jar
28 |
29 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | COPY target/uberjar/visitera.jar /visitera/app.jar
4 |
5 | EXPOSE 3000
6 |
7 | CMD ["java", "-jar", "/visitera/app.jar"]
8 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/Procfile:
--------------------------------------------------------------------------------
1 | web: java -cp target/uberjar/visitera.jar clojure.main -m visitera.core
2 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/README.md:
--------------------------------------------------------------------------------
1 | # visitera
2 |
3 | generated using Luminus version "3.42"
4 |
5 | ## Prerequisites
6 |
7 | 1. [Leiningen]
8 | 2. [Datomic]
9 |
10 | [leiningen]: https://github.com/technomancy/leiningen
11 | [datomic]: https://my.datomic.com/downloads/free
12 |
13 | ## Running
14 |
15 | 1. Run datomic
16 |
17 | - `cd {datomic-folder}`
18 | - `bin/transactor config/samples/free-transactor-template.properties`
19 | - _Optional gui console_: `bin/console -p 8080 dev datomic:free://localhost:4334`
20 |
21 | 2. Start a web server:
22 |
23 | - `lein repl`
24 | - `(start)`
25 |
26 | 3. Start client:
27 |
28 | - `lein figwheel`
29 | - Go to `localhost:3001` in your browser
30 |
31 | ## License
32 |
33 | Copyright © 2019 Aliaksandr Sushkevich
34 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/dev-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The dev-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:dev true
6 | :port 3000
7 | ;; when :nrepl-port is set the application starts the nREPL server on load
8 | :nrepl-port 7000
9 |
10 | ; set your dev database connection URL here
11 | :database-url "datomic:free://localhost:4334/visitera_dev"
12 |
13 | ; alternatively, you can use the datomic mem db for development:
14 | ; :database-url "datomic:mem://visitera_datomic_dev"
15 | }
16 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/dev/clj/user.clj:
--------------------------------------------------------------------------------
1 | (ns user
2 | "Userspace functions you can run by default in your local REPL."
3 | (:require
4 | [visitera.config :refer [env]]
5 | [clojure.spec.alpha :as s]
6 | [expound.alpha :as expound]
7 | [mount.core :as mount]
8 | [visitera.figwheel :refer [start-fw stop-fw cljs]]
9 | [visitera.core :refer [start-app]]
10 | [visitera.db.core :refer [conn install-schema delete-database]]))
11 |
12 | (alter-var-root #'s/*explain-out* (constantly expound/printer))
13 |
14 | (add-tap (bound-fn* clojure.pprint/pprint))
15 |
16 | (defn start
17 | "Starts application.
18 | You'll usually want to run this on startup."
19 | []
20 | (mount/start-without #'visitera.core/repl-server)
21 | (install-schema conn))
22 |
23 | (defn stop
24 | "Stops application."
25 | []
26 | (mount/stop-except #'visitera.core/repl-server))
27 |
28 | (defn restart
29 | "Restarts application."
30 | []
31 | (stop)
32 | (start))
33 |
34 | (defn reset-db
35 | "Delete database and restart application"
36 | []
37 | (delete-database)
38 | (restart))
39 |
40 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/dev/clj/visitera/dev_middleware.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.dev-middleware
2 | (:require
3 | [ring.middleware.reload :refer [wrap-reload]]
4 | [selmer.middleware :refer [wrap-error-page]]
5 | [prone.middleware :refer [wrap-exceptions]]))
6 |
7 | (defn wrap-dev [handler]
8 | (-> handler
9 | wrap-reload
10 | wrap-error-page
11 | (wrap-exceptions {:app-namespaces ['visitera]})))
12 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/dev/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require
3 | [selmer.parser :as parser]
4 | [clojure.tools.logging :as log]
5 | [visitera.dev-middleware :refer [wrap-dev]]))
6 |
7 | (def defaults
8 | {:init
9 | (fn []
10 | (parser/cache-off!)
11 | (log/info "\n-=[visitera started successfully using the development profile]=-"))
12 | :stop
13 | (fn []
14 | (log/info "\n-=[visitera has shut down successfully]=-"))
15 | :middleware wrap-dev})
16 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/dev/clj/visitera/figwheel.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.figwheel
2 | (:require [figwheel-sidecar.repl-api :as ra]))
3 |
4 | (defn start-fw []
5 | (ra/start-figwheel!))
6 |
7 | (defn stop-fw []
8 | (ra/stop-figwheel!))
9 |
10 | (defn cljs []
11 | (ra/cljs-repl))
12 |
13 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/dev/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns^:figwheel-no-load visitera.app
2 | (:require
3 | [visitera.core :as core]
4 | [cljs.spec.alpha :as s]
5 | [expound.alpha :as expound]
6 | [devtools.core :as devtools]))
7 |
8 | (extend-protocol IPrintWithWriter
9 | js/Symbol
10 | (-pr-writer [sym writer _]
11 | (-write writer (str "\"" (.toString sym) "\""))))
12 |
13 | (set! s/*explain-out* expound/printer)
14 |
15 | (enable-console-print!)
16 |
17 | (devtools/install!)
18 |
19 | (core/init!)
20 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/dev/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/prod/clj/visitera/env.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.env
2 | (:require [clojure.tools.logging :as log]))
3 |
4 | (def defaults
5 | {:init
6 | (fn []
7 | (log/info "\n-=[visitera started successfully]=-"))
8 | :stop
9 | (fn []
10 | (log/info "\n-=[visitera has shut down successfully]=-"))
11 | :middleware identity})
12 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/prod/cljs/visitera/app.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.app
2 | (:require [visitera.core :as core]))
3 |
4 | ;;ignore println statements in prod
5 | (set! *print-fn* (fn [& _]))
6 |
7 | (core/init!)
8 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/prod/resources/config.edn:
--------------------------------------------------------------------------------
1 | {:prod true
2 | :port 3000}
3 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/prod/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | log/visitera.log
6 |
7 | log/visitera.%d{yyyy-MM-dd}.%i.log
8 |
9 | 100MB
10 |
11 |
12 | 30
13 |
14 |
15 | UTF-8
16 | %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/env/test/resources/config.edn:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/resources/html/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Welcome to visitera
7 |
8 |
9 |
10 |
11 |
12 | {% style "/assets/bulma/css/bulma.min.css" %}
13 | {% style "/assets/material-icons/css/material-icons.min.css" %}
14 | {% style "/css/screen.css" %}
15 |
16 |
19 |
20 |
21 |
22 |
23 | {% script "/js/app.js" %}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/resources/migrations/test-data.edn:
--------------------------------------------------------------------------------
1 | {:visitera/data2
2 | {:txes
3 | [[{:user/email
4 | "test@user.com"
5 | :user/password
6 | ; somepass
7 | "bcrypt+sha512$c0d6f8f472f9312d1ac5cb84b39c858e$12$72eb4c3d6d0f6148c66657da865705f67c1914ef1d66fd2a"
8 | :user/countries-to-visit
9 | [{:country/name "Zambia"}
10 | {:country/name "France"}
11 | {:country/name "Albania"}
12 | {:country/name "Andorra"}]
13 | :user/countries-visited
14 | [{:country/alpha-2 "RU"}
15 | {:country/alpha-2 "CZ"}
16 | {:country/alpha-2 "US"}]}
17 |
18 | {:user/email
19 | "test@user-1.com"
20 | :user/password
21 | ; somepass
22 | "bcrypt+sha512$c0d6f8f472f9312d1ac5cb84b39c858e$12$72eb4c3d6d0f6148c66657da865705f67c1914ef1d66fd2a"}]]}}
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/resources/public/css/screen.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
4 | }
5 | @font-face {
6 | font-family: 'Material Icons';
7 | font-style: normal;
8 | font-weight: 400;
9 | src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
10 | src: local('Material Icons'),
11 | local('MaterialIcons-Regular'),
12 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
13 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'),
14 | url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype');
15 | }
16 | .material-icons {
17 | font-family: 'Material Icons';
18 | font-weight: normal;
19 | font-style: normal;
20 | font-size: 24px; /* Preferred icon size */
21 | display: inline-block;
22 | line-height: 1;
23 | text-transform: none;
24 | letter-spacing: normal;
25 | word-wrap: normal;
26 | white-space: nowrap;
27 | direction: ltr;
28 | /* Support for all WebKit browsers. */
29 | -webkit-font-smoothing: antialiased;
30 | /* Support for Safari and Chrome. */
31 | text-rendering: optimizeLegibility;
32 | /* Support for Firefox. */
33 | -moz-osx-font-smoothing: grayscale;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-07/start/visitera/resources/public/favicon.ico
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/resources/public/img/warning_clojure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/app/chapter-07/start/visitera/resources/public/img/warning_clojure.png
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/resources/raw/transform-data.clj:
--------------------------------------------------------------------------------
1 | (defn get-raw-data [] (->
2 | (slurp "./resources/raw/data.edn")
3 | (read-string)
4 | (eval)))
5 |
6 | (def keys [:name :country-code :alpha-2 :alpha-3])
7 | (def new-keys {:name :country/name
8 | :country-code :country/code
9 | :alpha-2 :country/alpha-2
10 | :alpha-3 :country/alpha-3})
11 |
12 | (defn transform [country]
13 | (-> country
14 | (select-keys keys)
15 | (clojure.set/rename-keys new-keys)))
16 |
17 | (defn wrap-with-template [data]
18 | (str {:visitera/data1 {:txes [(vec data)]}}))
19 |
20 | (defn save-parsed []
21 | (spit "./resources/raw/parsed-data.edn"
22 | (binding [*print-namespace-maps* false]
23 | (->>
24 | (get-raw-data)
25 | (map transform)
26 | (wrap-with-template)))))
27 |
28 | (save-parsed)
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/clj/visitera/config.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.config
2 | (:require
3 | [cprop.core :refer [load-config]]
4 | [cprop.source :as source]
5 | [mount.core :refer [args defstate]]))
6 |
7 | (defstate env
8 | :start
9 | (load-config
10 | :merge
11 | [(args)
12 | (source/from-system-props)
13 | (source/from-env)]))
14 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/clj/visitera/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.handler
2 | (:require
3 | [visitera.middleware :as middleware]
4 | [visitera.layout :refer [error-page]]
5 | [visitera.routes.home :refer [home-routes]]
6 | [reitit.ring :as ring]
7 | [ring.middleware.content-type :refer [wrap-content-type]]
8 | [ring.middleware.webjars :refer [wrap-webjars]]
9 | [visitera.env :refer [defaults]]
10 | [mount.core :as mount]))
11 |
12 | (mount/defstate init-app
13 | :start ((or (:init defaults) (fn [])))
14 | :stop ((or (:stop defaults) (fn []))))
15 |
16 | (mount/defstate app-routes
17 | :start
18 | (ring/ring-handler
19 | (ring/router
20 | [(home-routes)])
21 | (ring/routes
22 | (ring/create-resource-handler
23 | {:path "/"})
24 | (wrap-content-type
25 | (wrap-webjars (constantly nil)))
26 | (ring/create-default-handler
27 | {:not-found
28 | (constantly (error-page {:status 404, :title "404 - Page not found"}))
29 | :method-not-allowed
30 | (constantly (error-page {:status 405, :title "405 - Not allowed"}))
31 | :not-acceptable
32 | (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
33 |
34 | (defn app []
35 | (middleware/wrap-base #'app-routes))
36 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/clj/visitera/middleware/formats.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.middleware.formats
2 | (:require
3 | [cognitect.transit :as transit]
4 | [luminus-transit.time :as time]
5 | [muuntaja.core :as m]))
6 |
7 | (def instance
8 | (m/create
9 | (-> m/default-options
10 | (update-in
11 | [:formats "application/transit+json" :decoder-opts]
12 | (partial merge time/time-deserialization-handlers))
13 | (update-in
14 | [:formats "application/transit+json" :encoder-opts]
15 | (partial merge time/time-serialization-handlers)))))
16 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/clj/visitera/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.nrepl
2 | (:require
3 | [nrepl.server :as nrepl]
4 | [clojure.tools.logging :as log]))
5 |
6 | (defn start
7 | "Start a network repl for debugging on specified port followed by
8 | an optional parameters map. The :bind, :transport-fn, :handler,
9 | :ack-port and :greeting-fn will be forwarded to
10 | clojure.tools.nrepl.server/start-server as they are."
11 | [{:keys [port bind transport-fn handler ack-port greeting-fn]}]
12 | (try
13 | (log/info "starting nREPL server on port" port)
14 | (nrepl/start-server :port port
15 | :bind bind
16 | :transport-fn transport-fn
17 | :handler handler
18 | :ack-port ack-port
19 | :greeting-fn greeting-fn)
20 |
21 | (catch Throwable t
22 | (log/error t "failed to start nREPL")
23 | (throw t))))
24 |
25 | (defn stop [server]
26 | (nrepl/stop-server server)
27 | (log/info "nREPL server stopped"))
28 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/cljc/visitera/validation.cljc:
--------------------------------------------------------------------------------
1 | (ns visitera.validation
2 | (:require [struct.core :as st]))
3 |
4 | (def register-schema
5 | [[:email
6 | st/required
7 | st/string
8 | st/email]
9 |
10 | [:password
11 | st/required
12 | st/string
13 | {:message "password must contain at least 8 characters"
14 | :validate #(> (count %) 7)}]])
15 |
16 | (def login-schema
17 | [[:email
18 | st/required
19 | st/string
20 | st/email]
21 |
22 | [:password
23 | st/required
24 | st/string]])
25 |
26 | (defn validate-register [params]
27 | (first (st/validate params register-schema)))
28 |
29 | (defn validate-login [params]
30 | (first (st/validate params login-schema)))
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/cljs/visitera/ajax.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.ajax
2 | (:require
3 | [ajax.core :as ajax]
4 | [luminus-transit.time :as time]
5 | [cognitect.transit :as transit]
6 | [re-frame.core :as rf]))
7 |
8 | (defn local-uri? [{:keys [uri]}]
9 | (not (re-find #"^\w+?://" uri)))
10 |
11 | (defn default-headers [request]
12 | (if (local-uri? request)
13 | (-> request
14 | (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
15 | request))
16 |
17 | ;; injects transit serialization config into request options
18 | (defn as-transit [opts]
19 | (merge {:raw false
20 | :format :transit
21 | :response-format :transit
22 | :reader (transit/reader :json time/time-deserialization-handlers)
23 | :writer (transit/writer :json time/time-serialization-handlers)}
24 | opts))
25 |
26 | (defn load-interceptors! []
27 | (swap! ajax/default-interceptors
28 | conj
29 | (ajax/to-interceptor {:name "default headers"
30 | :request default-headers})))
31 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/cljs/visitera/components/navbar.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.components.navbar
2 | (:require
3 | [reagent.core :as r]))
4 |
5 | (defn navbar []
6 | (r/with-let [expanded? (r/atom false)]
7 | [:nav.navbar.is-primary>div.container
8 | [:div.navbar-brand
9 | [:a.navbar-item {:href "/" :style {:font-weight :bold}} "visitera"]
10 | [:span.navbar-burger.burger
11 | {:data-target :nav-menu
12 | :on-click #(swap! expanded? not)
13 | :class (when @expanded? :is-active)}
14 | [:span] [:span] [:span]]]
15 | [:div#nav-menu.navbar-menu
16 | {:class (when @expanded? :is-active)}
17 | [:div.navbar-end
18 | [:a.navbar-item {:href "/logout"} "Logout"]]]]))
19 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/src/cljs/visitera/config.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.config)
2 |
3 | (def colors
4 | {:to-visit "#f0dd92"
5 | :visited "#83b582"
6 | :not-visited "#dddddd"})
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/test-config.edn:
--------------------------------------------------------------------------------
1 | ;; WARNING
2 | ;; The test-config.edn file is used for local environment variables, such as database credentials.
3 | ;; This file is listed in .gitignore and will be excluded from version control by Git.
4 |
5 | {:port 3000
6 | ; set your test database connection URL here
7 | :database-url "datomic:free://localhost:4334/visitera_test"
8 | }
9 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/test/clj/visitera/test/handler.clj:
--------------------------------------------------------------------------------
1 | (ns visitera.test.handler
2 | (:require
3 | [clojure.test :refer :all]
4 | [ring.mock.request :refer :all]
5 | [visitera.handler :refer :all]
6 | [visitera.middleware.formats :as formats]
7 | [muuntaja.core :as m]
8 | [mount.core :as mount]))
9 |
10 | (defn parse-json [body]
11 | (m/decode formats/instance "application/json" body))
12 |
13 | (use-fixtures
14 | :once
15 | (fn [f]
16 | (mount/start #'visitera.config/env
17 | #'visitera.handler/app-routes)
18 | (f)))
19 |
20 | (deftest test-app
21 | (testing "main route"
22 | (let [response ((app) (request :get "/"))]
23 | (is (= 302 (:status response)))))
24 |
25 | (testing "not-found route"
26 | (let [response ((app) (request :get "/invalid"))]
27 | (is (= 404 (:status response))))))
28 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/test/cljs/visitera/core_test.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.core-test
2 | (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
3 | [pjstadig.humane-test-output]
4 | [reagent.core :as reagent :refer [atom]]
5 | [visitera.core :as rc]))
6 |
7 | (deftest test-home
8 | (is (= true true)))
9 |
10 |
--------------------------------------------------------------------------------
/app/chapter-07/start/visitera/test/cljs/visitera/doo_runner.cljs:
--------------------------------------------------------------------------------
1 | (ns visitera.doo-runner
2 | (:require [doo.runner :refer-macros [doo-tests]]
3 | [visitera.core-test]))
4 |
5 | (doo-tests 'visitera.core-test)
6 |
7 |
--------------------------------------------------------------------------------
/tutorial/chapter-01/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/tutorial/chapter-01/login.png
--------------------------------------------------------------------------------
/tutorial/chapter-01/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliaksandr-s/prototyping-with-clojure/e1f90bf66c315de1dfa72624895637f1c609c42e/tutorial/chapter-01/map.png
--------------------------------------------------------------------------------