├── .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 --------------------------------------------------------------------------------