From 20c4ef931599097a7b3fc0a38e5695a17cac3526 Mon Sep 17 00:00:00 2001 From: Carolyn Knight-Serrano Date: Tue, 18 Feb 2020 18:37:43 -0800 Subject: [PATCH] Switched to duct --- .gitignore | 18 +-- Capstanfile | 28 ---- Dockerfile | 7 - Procfile | 1 - README.md | 79 ++++++++-- dev/resources/dev.edn | 3 + dev/src/dev.clj | 31 ++++ dev/src/user.clj | 8 + env/dev/clj/pinkblackrose/dev_middleware.clj | 11 -- env/dev/clj/pinkblackrose/env.clj | 15 -- env/dev/clj/user.clj | 63 -------- env/dev/cljs/pinkblackrose/app.cljs | 19 --- env/dev/resources/logback.xml | 35 ----- env/prod/clj/pinkblackrose/env.clj | 11 -- env/prod/cljs/pinkblackrose/app.cljs | 7 - env/prod/resources/config.edn | 2 - env/prod/resources/logback.xml | 26 --- env/test/resources/config.edn | 1 - env/test/resources/logback.xml | 35 ----- project.clj | 148 +++--------------- resources/docs/docs.md | 145 ----------------- resources/graphql/schema.edn | 25 --- resources/html/error.html | 49 ------ resources/html/graphiql.html | 120 -------------- resources/html/home.html | 41 ----- .../20200218062529-add-users-table.down.sql | 1 - .../20200218062529-add-users-table.up.sql | 9 -- resources/pinkblackrose/config.edn | 18 +++ resources/public/css/screen.css | 35 ----- resources/public/favicon.ico | Bin 1150 -> 0 bytes resources/public/img/warning_clojure.png | Bin 21846 -> 0 bytes resources/scss/screen.scss | 5 - resources/sql/queries.sql | 21 --- src/clj/pinkblackrose/config.clj | 13 -- src/clj/pinkblackrose/core.clj | 74 --------- src/clj/pinkblackrose/db/core.clj | 86 ---------- src/clj/pinkblackrose/handler.clj | 42 ----- src/clj/pinkblackrose/layout.clj | 39 ----- src/clj/pinkblackrose/middleware.clj | 87 ---------- .../pinkblackrose/middleware/exception.clj | 24 --- src/clj/pinkblackrose/middleware/formats.clj | 15 -- src/clj/pinkblackrose/nrepl.clj | 27 ---- src/clj/pinkblackrose/routes/home.clj | 22 --- src/clj/pinkblackrose/routes/services.clj | 93 ----------- .../pinkblackrose/routes/services/graphql.clj | 41 ----- src/cljc/pinkblackrose/validation.cljc | 2 - src/cljs/pinkblackrose/ajax.cljs | 30 ---- src/cljs/pinkblackrose/core.cljs | 79 ---------- src/cljs/pinkblackrose/events.cljs | 78 --------- .../config.edn => src/duct_hierarchy.edn | 0 src/pinkblackrose/client.cljs | 3 + src/pinkblackrose/main.clj | 12 ++ test/clj/pinkblackrose/test/db/core.clj | 38 ----- test/clj/pinkblackrose/test/handler.clj | 52 ------ test/cljs/pinkblackrose/core_test.cljs | 9 -- 55 files changed, 174 insertions(+), 1709 deletions(-) delete mode 100644 Capstanfile delete mode 100644 Dockerfile delete mode 100644 Procfile create mode 100644 dev/resources/dev.edn create mode 100644 dev/src/dev.clj create mode 100644 dev/src/user.clj delete mode 100644 env/dev/clj/pinkblackrose/dev_middleware.clj delete mode 100644 env/dev/clj/pinkblackrose/env.clj delete mode 100644 env/dev/clj/user.clj delete mode 100644 env/dev/cljs/pinkblackrose/app.cljs delete mode 100644 env/dev/resources/logback.xml delete mode 100644 env/prod/clj/pinkblackrose/env.clj delete mode 100644 env/prod/cljs/pinkblackrose/app.cljs delete mode 100644 env/prod/resources/config.edn delete mode 100644 env/prod/resources/logback.xml delete mode 100644 env/test/resources/config.edn delete mode 100644 env/test/resources/logback.xml delete mode 100644 resources/docs/docs.md delete mode 100644 resources/graphql/schema.edn delete mode 100644 resources/html/error.html delete mode 100644 resources/html/graphiql.html delete mode 100644 resources/html/home.html delete mode 100644 resources/migrations/20200218062529-add-users-table.down.sql delete mode 100644 resources/migrations/20200218062529-add-users-table.up.sql create mode 100644 resources/pinkblackrose/config.edn delete mode 100644 resources/public/css/screen.css delete mode 100644 resources/public/favicon.ico delete mode 100644 resources/public/img/warning_clojure.png delete mode 100644 resources/scss/screen.scss delete mode 100644 resources/sql/queries.sql delete mode 100644 src/clj/pinkblackrose/config.clj delete mode 100644 src/clj/pinkblackrose/core.clj delete mode 100644 src/clj/pinkblackrose/db/core.clj delete mode 100644 src/clj/pinkblackrose/handler.clj delete mode 100644 src/clj/pinkblackrose/layout.clj delete mode 100644 src/clj/pinkblackrose/middleware.clj delete mode 100644 src/clj/pinkblackrose/middleware/exception.clj delete mode 100644 src/clj/pinkblackrose/middleware/formats.clj delete mode 100644 src/clj/pinkblackrose/nrepl.clj delete mode 100644 src/clj/pinkblackrose/routes/home.clj delete mode 100644 src/clj/pinkblackrose/routes/services.clj delete mode 100644 src/clj/pinkblackrose/routes/services/graphql.clj delete mode 100644 src/cljc/pinkblackrose/validation.cljc delete mode 100644 src/cljs/pinkblackrose/ajax.cljs delete mode 100644 src/cljs/pinkblackrose/core.cljs delete mode 100644 src/cljs/pinkblackrose/events.cljs rename env/dev/resources/config.edn => src/duct_hierarchy.edn (100%) create mode 100644 src/pinkblackrose/client.cljs create mode 100644 src/pinkblackrose/main.clj delete mode 100644 test/clj/pinkblackrose/test/db/core.clj delete mode 100644 test/clj/pinkblackrose/test/handler.clj delete mode 100644 test/cljs/pinkblackrose/core_test.cljs diff --git a/.gitignore b/.gitignore index 7b7f8df..749eaf8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,14 @@ /target -/lib +/logs /classes /checkouts pom.xml -dev-config.edn -test-config.edn +pom.xml.asc *.jar *.class /.lein-* -profiles.clj -/.env -.nrepl-port -/.shadow-cljs -package.json -shadow-cljs.edn -/node_modules -/log +/.nrepl-port +/.dir-locals.el +/profiles.clj +/dev/resources/local.edn +/dev/src/local.clj diff --git a/Capstanfile b/Capstanfile deleted file mode 100644 index f2a766a..0000000 --- a/Capstanfile +++ /dev/null @@ -1,28 +0,0 @@ - -# -# Name of the base image. Capstan will download this automatically from -# Cloudius S3 repository. -# -#base: cloudius/osv -base: cloudius/osv-openjdk8 - -# -# The command line passed to OSv to start up the application. -# -cmdline: /java.so -jar /pinkblackrose/app.jar - -# -# The command to use to build the application. -# You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine -# -# For Leiningen, you can use: -#build: lein uberjar -# For Boot, you can use: -#build: boot build - -# -# List of files that are included in the generated image. -# -files: - /pinkblackrose/app.jar: ./target/uberjar/pinkblackrose.jar - diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2d4d18f..0000000 --- a/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM openjdk:8-alpine - -COPY target/uberjar/pinkblackrose.jar /pinkblackrose/app.jar - -EXPOSE 3000 - -CMD ["java", "-jar", "/pinkblackrose/app.jar"] diff --git a/Procfile b/Procfile deleted file mode 100644 index aab6c6e..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: java -cp target/uberjar/pinkblackrose.jar clojure.main -m pinkblackrose.core diff --git a/README.md b/README.md index f99562a..3aa8c9d 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,82 @@ # pinkblackrose -generated using Luminus version "3.57" +FIXME: description -FIXME +## Developing -## Prerequisites +### Setup -You will need [Leiningen][1] 2.0 or above installed. +When you first clone this repository, run: -[1]: https://github.com/technomancy/leiningen +```sh +lein duct setup +``` -## Running +This will create files for local configuration, and prep your system +for the project. -To start a web server for the application, run: +### Environment - lein run +To begin developing, start with a REPL. -## License +```sh +lein repl +``` + +Then load the development environment. + +```clojure +user=> (dev) +:loaded +``` + +Run `go` to prep and initiate the system. + +```clojure +dev=> (go) +:duct.server.http.jetty/starting-server {:port 3000} +:initiated +``` + +By default this creates a web server at . + +When you make changes to your source files, use `reset` to reload any +modified files and reset the server. Changes to CSS or ClojureScript +files will be hot-loaded into the browser. + +```clojure +dev=> (reset) +:reloading (...) +:resumed +``` + +If you want to access a ClojureScript REPL, make sure that the site is loaded +in a browser and run: + +```clojure +dev=> (cljs-repl) +Waiting for browser connection... Connected. +To quit, type: :cljs/quit +nil +cljs.user=> +``` + +### Testing + +Testing is fastest through the REPL, as you avoid environment startup +time. + +```clojure +dev=> (test) +... +``` + +But you can also run tests through Leiningen. + +```sh +lein test +``` + +## Legal Copyright © 2020 FIXME diff --git a/dev/resources/dev.edn b/dev/resources/dev.edn new file mode 100644 index 0000000..58b28ab --- /dev/null +++ b/dev/resources/dev.edn @@ -0,0 +1,3 @@ +{:duct.database/sql + {:connection-uri "jdbc:postgresql://localhost/postgres"} + } diff --git a/dev/src/dev.clj b/dev/src/dev.clj new file mode 100644 index 0000000..caa0d68 --- /dev/null +++ b/dev/src/dev.clj @@ -0,0 +1,31 @@ +(ns dev + (:refer-clojure :exclude [test]) + (:require [clojure.repl :refer :all] + [fipp.edn :refer [pprint]] + [clojure.tools.namespace.repl :refer [refresh]] + [clojure.java.io :as io] + [duct.core :as duct] + [duct.core.repl :as duct-repl] + [duct.repl.figwheel :refer [cljs-repl]] + [eftest.runner :as eftest] + [integrant.core :as ig] + [integrant.repl :refer [clear halt go init prep reset]] + [integrant.repl.state :refer [config system]])) + +(duct/load-hierarchy) + +(defn read-config [] + (duct/read-config (io/resource "pinkblackrose/config.edn"))) + +(defn test [] + (eftest/run-tests (eftest/find-tests "test"))) + +(def profiles + [:duct.profile/dev :duct.profile/local]) + +(clojure.tools.namespace.repl/set-refresh-dirs "dev/src" "src" "test") + +(when (io/resource "local.clj") + (load "local")) + +(integrant.repl/set-prep! #(duct/prep-config (read-config) profiles)) diff --git a/dev/src/user.clj b/dev/src/user.clj new file mode 100644 index 0000000..9cf3c0c --- /dev/null +++ b/dev/src/user.clj @@ -0,0 +1,8 @@ +(ns user) + +(defn dev + "Load and switch to the 'dev' namespace." + [] + (require 'dev) + (in-ns 'dev) + :loaded) diff --git a/env/dev/clj/pinkblackrose/dev_middleware.clj b/env/dev/clj/pinkblackrose/dev_middleware.clj deleted file mode 100644 index e5e7aab..0000000 --- a/env/dev/clj/pinkblackrose/dev_middleware.clj +++ /dev/null @@ -1,11 +0,0 @@ -(ns pinkblackrose.dev-middleware - (:require - [ring.middleware.reload :refer [wrap-reload]] - [selmer.middleware :refer [wrap-error-page]] - [prone.middleware :refer [wrap-exceptions]])) - -(defn wrap-dev [handler] - (-> handler - wrap-reload - wrap-error-page - (wrap-exceptions {:app-namespaces ['pinkblackrose]}))) diff --git a/env/dev/clj/pinkblackrose/env.clj b/env/dev/clj/pinkblackrose/env.clj deleted file mode 100644 index d5b18cf..0000000 --- a/env/dev/clj/pinkblackrose/env.clj +++ /dev/null @@ -1,15 +0,0 @@ -(ns pinkblackrose.env - (:require - [selmer.parser :as parser] - [clojure.tools.logging :as log] - [pinkblackrose.dev-middleware :refer [wrap-dev]])) - -(def defaults - {:init - (fn [] - (parser/cache-off!) - (log/info "\n-=[pinkblackrose started successfully using the development profile]=-")) - :stop - (fn [] - (log/info "\n-=[pinkblackrose has shut down successfully]=-")) - :middleware wrap-dev}) diff --git a/env/dev/clj/user.clj b/env/dev/clj/user.clj deleted file mode 100644 index 509fc0c..0000000 --- a/env/dev/clj/user.clj +++ /dev/null @@ -1,63 +0,0 @@ -(ns user - "Userspace functions you can run by default in your local REPL." - (:require - [pinkblackrose.config :refer [env]] - [clojure.pprint] - [clojure.spec.alpha :as s] - [expound.alpha :as expound] - [mount.core :as mount] - [pinkblackrose.core :refer [start-app]] - [pinkblackrose.db.core] - [conman.core :as conman] - [luminus-migrations.core :as migrations])) - -(alter-var-root #'s/*explain-out* (constantly expound/printer)) - -(add-tap (bound-fn* clojure.pprint/pprint)) - -(defn start - "Starts application. - You'll usually want to run this on startup." - [] - (mount/start-without #'pinkblackrose.core/repl-server)) - -(defn stop - "Stops application." - [] - (mount/stop-except #'pinkblackrose.core/repl-server)) - -(defn restart - "Restarts application." - [] - (stop) - (start)) - -(defn restart-db - "Restarts database." - [] - (mount/stop #'pinkblackrose.db.core/*db*) - (mount/start #'pinkblackrose.db.core/*db*) - (binding [*ns* 'pinkblackrose.db.core] - (conman/bind-connection pinkblackrose.db.core/*db* "sql/queries.sql"))) - -(defn reset-db - "Resets database." - [] - (migrations/migrate ["reset"] (select-keys env [:database-url]))) - -(defn migrate - "Migrates database up for all outstanding migrations." - [] - (migrations/migrate ["migrate"] (select-keys env [:database-url]))) - -(defn rollback - "Rollback latest database migration." - [] - (migrations/migrate ["rollback"] (select-keys env [:database-url]))) - -(defn create-migration - "Create a new up and down migration file with a generated timestamp and `name`." - [name] - (migrations/create name (select-keys env [:database-url]))) - - diff --git a/env/dev/cljs/pinkblackrose/app.cljs b/env/dev/cljs/pinkblackrose/app.cljs deleted file mode 100644 index 01c1e46..0000000 --- a/env/dev/cljs/pinkblackrose/app.cljs +++ /dev/null @@ -1,19 +0,0 @@ -(ns ^:dev/once pinkblackrose.app - (:require - [pinkblackrose.core :as core] - [cljs.spec.alpha :as s] - [expound.alpha :as expound] - [devtools.core :as devtools])) - -(extend-protocol IPrintWithWriter - js/Symbol - (-pr-writer [sym writer _] - (-write writer (str "\"" (.toString sym) "\"")))) - -(set! s/*explain-out* expound/printer) - -(enable-console-print!) - -(devtools/install!) - -(core/init!) diff --git a/env/dev/resources/logback.xml b/env/dev/resources/logback.xml deleted file mode 100644 index fb4c369..0000000 --- a/env/dev/resources/logback.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - UTF-8 - %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n - - - - log/pinkblackrose.log - - log/pinkblackrose.%d{yyyy-MM-dd}.%i.log - - 100MB - - - 30 - - - UTF-8 - %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n - - - - - - - - - - - diff --git a/env/prod/clj/pinkblackrose/env.clj b/env/prod/clj/pinkblackrose/env.clj deleted file mode 100644 index ec6dd2d..0000000 --- a/env/prod/clj/pinkblackrose/env.clj +++ /dev/null @@ -1,11 +0,0 @@ -(ns pinkblackrose.env - (:require [clojure.tools.logging :as log])) - -(def defaults - {:init - (fn [] - (log/info "\n-=[pinkblackrose started successfully]=-")) - :stop - (fn [] - (log/info "\n-=[pinkblackrose has shut down successfully]=-")) - :middleware identity}) diff --git a/env/prod/cljs/pinkblackrose/app.cljs b/env/prod/cljs/pinkblackrose/app.cljs deleted file mode 100644 index 6813f7f..0000000 --- a/env/prod/cljs/pinkblackrose/app.cljs +++ /dev/null @@ -1,7 +0,0 @@ -(ns pinkblackrose.app - (:require [pinkblackrose.core :as core])) - -;;ignore println statements in prod -(set! *print-fn* (fn [& _])) - -(core/init!) diff --git a/env/prod/resources/config.edn b/env/prod/resources/config.edn deleted file mode 100644 index e24ec21..0000000 --- a/env/prod/resources/config.edn +++ /dev/null @@ -1,2 +0,0 @@ -{:prod true - :port 3000} diff --git a/env/prod/resources/logback.xml b/env/prod/resources/logback.xml deleted file mode 100644 index 712a5bd..0000000 --- a/env/prod/resources/logback.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - log/pinkblackrose.log - - log/pinkblackrose.%d{yyyy-MM-dd}.%i.log - - 100MB - - - 30 - - - UTF-8 - %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n - - - - - - - - - - diff --git a/env/test/resources/config.edn b/env/test/resources/config.edn deleted file mode 100644 index 0967ef4..0000000 --- a/env/test/resources/config.edn +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/env/test/resources/logback.xml b/env/test/resources/logback.xml deleted file mode 100644 index fb4c369..0000000 --- a/env/test/resources/logback.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - UTF-8 - %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n - - - - log/pinkblackrose.log - - log/pinkblackrose.%d{yyyy-MM-dd}.%i.log - - 100MB - - - 30 - - - UTF-8 - %date{ISO8601} [%thread] %-5level %logger{36} - %msg %n - - - - - - - - - - - diff --git a/project.clj b/project.clj index bad8912..56e0a40 100644 --- a/project.clj +++ b/project.clj @@ -1,132 +1,28 @@ (defproject pinkblackrose "0.1.0-SNAPSHOT" - :description "FIXME: write description" :url "http://example.com/FIXME" - - :dependencies [[buddy/buddy-auth "2.2.0"] - [buddy/buddy-core "1.6.0"] - [buddy/buddy-hashers "1.4.0"] - [buddy/buddy-sign "3.1.0"] - [ch.qos.logback/logback-classic "1.2.3"] - [cheshire "5.9.0"] - [cljs-ajax "0.8.0"] - [clojure.java-time "0.3.2"] - [com.cognitect/transit-clj "0.8.319"] - [com.google.javascript/closure-compiler-unshaded "v20190618" :scope "provided"] - [com.walmartlabs/lacinia "0.32.0"] - [conman "0.8.4"] - [cprop "0.1.15"] - [day8.re-frame/http-fx "0.1.6"] - [expound "0.8.3"] - [funcool/struct "1.4.0"] - [luminus-aleph "0.1.6"] - [luminus-migrations "0.6.6"] - [luminus-transit "0.1.2"] - [luminus/ring-ttl-session "0.3.3"] - [markdown-clj "1.10.1"] - [metosin/muuntaja "0.6.6"] - [metosin/reitit "0.3.10"] - [metosin/ring-http-response "0.9.1"] - [mount "0.1.16"] - [nrepl "0.6.0"] - [org.clojure/clojure "1.10.1"] - [org.clojure/clojurescript "1.10.597" :scope "provided"] - [org.clojure/core.async "0.4.500"] - [org.clojure/data.json "0.2.6"] - [org.clojure/google-closure-library "0.0-20190213-2033d5d9" :scope "provided"] - [org.clojure/tools.cli "0.4.2"] - [org.clojure/tools.logging "0.5.0"] - [org.postgresql/postgresql "42.2.9"] - [org.webjars.npm/bulma "0.8.0"] - [org.webjars.npm/material-icons "0.3.1"] - [org.webjars/webjars-locator "0.38"] - [re-frame "0.10.9"] - [reagent "0.9.0-rc3"] - [ring-webjars "0.2.0"] - [ring/ring-core "1.8.0"] - [ring/ring-defaults "0.3.2"] - [selmer "1.12.18"] - [thheller/shadow-cljs "2.8.69" :scope "provided"]] - :min-lein-version "2.0.0" - - :source-paths ["src/clj" "src/cljs" "src/cljc"] - :test-paths ["test/clj"] - :resource-paths ["resources" "target/cljsbuild"] - :target-path "target/%s/" - :main ^:skip-aot pinkblackrose.core - - :plugins [[lein-shadow "0.1.7"] - [lein-sassc "0.10.4"] - [lein-auto "0.1.2"]] - :sassc - [{:src "resources/scss/screen.scss" - :output-to "resources/public/css/screen.css" - :style "nested" - :import-path "resources/scss"}] - - :auto - {"sassc" {:file-pattern #"\.(scss|sass)$" :paths ["resources/scss"]}} - - :hooks [leiningen.sassc] - :clean-targets ^{:protect false} - [:target-path "target/cljsbuild"] - :shadow-cljs - {:nrepl {:port 7002} - :builds - {:app - {:target :browser - :output-dir "target/cljsbuild/public/js" - :asset-path "/js" - :modules {:app {:entries [pinkblackrose.app]}} - :devtools - {:watch-dir "resources/public" :preloads [re-frisk.preload]} - :dev - {:closure-defines {"re_frame.trace.trace_enabled_QMARK_" true}}} - :test - {:target :node-test - :output-to "target/test/test.js" - :autorun true}}} - - :npm-deps [[shadow-cljs "2.8.69"] - [create-react-class "15.6.3"] - [react "16.8.6"] - [react-dom "16.8.6"]] - + :dependencies [[org.clojure/clojure "1.10.0"] + [duct/core "0.7.0"] + [duct/module.cljs "0.4.1"] + [duct/module.logging "0.4.0"] + [duct/module.sql "0.5.0"] + [duct/module.web "0.7.0"] + [org.postgresql/postgresql "42.2.5"]] + :plugins [[duct/lein-duct "0.12.1"]] + :main ^:skip-aot pinkblackrose.main + :resource-paths ["resources" "target/resources"] + :prep-tasks ["javac" "compile" ["run" ":duct/compiler"]] + :middleware [lein-duct.plugin/middleware] :profiles - {:uberjar {:omit-source true - :prep-tasks ["compile" ["shadow" "release" "app"]] - - :aot :all - :uberjar-name "pinkblackrose.jar" - :source-paths ["env/prod/clj" "env/prod/cljs"] - :resource-paths ["env/prod/resources"]} - - :dev [:project/dev :profiles/dev] - :test [:project/dev :project/test :profiles/test] - - :project/dev {:jvm-opts ["-Dconf=dev-config.edn"] - :dependencies [[binaryage/devtools "0.9.11"] - [cider/piggieback "0.4.2"] - [pjstadig/humane-test-output "0.10.0"] - [prone "2019-07-08"] - [re-frisk "0.5.4.1"] - [ring/ring-devel "1.8.0"] - [ring/ring-mock "0.4.0"]] - :plugins [[com.jakemccrary/lein-test-refresh "0.24.1"] - [jonase/eastwood "0.3.5"]] - - - :source-paths ["env/dev/clj" "env/dev/cljs" "test/cljs"] - :resource-paths ["env/dev/resources"] - :repl-options {:init-ns user - :timeout 120000} - :injections [(require 'pjstadig.humane-test-output) - (pjstadig.humane-test-output/activate!)]} - :project/test {:jvm-opts ["-Dconf=test-config.edn"] - :resource-paths ["env/test/resources"] - - - } + {:dev [:project/dev :profiles/dev] + :repl {:prep-tasks ^:replace ["javac" "compile"] + :dependencies [[cider/piggieback "0.4.0"]] + :repl-options {:init-ns user, :nrepl-middleware [cider.piggieback/wrap-cljs-repl]}} + :uberjar {:aot :all} :profiles/dev {} - :profiles/test {}}) + :project/dev {:source-paths ["dev/src"] + :resource-paths ["dev/resources"] + :dependencies [[integrant/repl "0.3.1"] + [eftest "0.5.7"] + [kerodon "0.9.0"]]}}) diff --git a/resources/docs/docs.md b/resources/docs/docs.md deleted file mode 100644 index bb28255..0000000 --- a/resources/docs/docs.md +++ /dev/null @@ -1,145 +0,0 @@ -

Congratulations, your Luminus site is ready!

- -This page will help guide you through the first steps of building your site. - -#### Why are you seeing this page? - -The `home-routes` handler in the `pinkblackrose.routes.home` namespace -defines the route that invokes the `home-page` function whenever an HTTP -request is made to the `/` URI using the `GET` method. - -``` -(defn home-routes [] - ["" - {:middleware [middleware/wrap-csrf - middleware/wrap-formats]} - ["/" {:get home-page}] - ["/docs" {:get (fn [_] - (-> (response/ok (-> "docs/docs.md" io/resource slurp)) - (response/header "Content-Type" "text/plain; charset=utf-8")))}]]) -``` - -The `home-page` function will in turn call the `pinkblackrose.layout/render` function -to render the HTML content: - -``` -(defn home-page [_] - (layout/render "home.html")) -``` - -The page contains a link to the compiled ClojureScript found in the `target/cljsbuild/public` folder: - -``` -{% script "/js/app.js" %} -``` - -The rest of this page is rendered by ClojureScript found in the `src/cljs/pinkblackrose/core.cljs` file. - - - -#### Organizing the routes - -The routes are aggregated and wrapped with middleware in the `pinkblackrose.handler` namespace: - -``` -(mount/defstate app - :start - (middleware/wrap-base - (ring/ring-handler - (ring/router - [(home-routes)]) - (ring/routes - (ring/create-resource-handler - {:path "/"}) - (wrap-content-type - (wrap-webjars (constantly nil))) - (ring/create-default-handler - {:not-found - (constantly (error-page {:status 404, :title "404 - Page not found"})) - :method-not-allowed - (constantly (error-page {:status 405, :title "405 - Not allowed"})) - :not-acceptable - (constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))) -``` - -The `app` definition groups all the routes in the application into a single handler. -A default route group is added to handle the `404`, `405`, and `406` errors. - -learn more about routing » - -#### Managing your middleware - -Request middleware functions are located under the `pinkblackrose.middleware` namespace. - -This namespace is reserved for any custom middleware for the application. Some default middleware is -already defined here. The middleware is assembled in the `wrap-base` function. - -Middleware used for development is placed in the `pinkblackrose.dev-middleware` namespace found in -the `env/dev/clj/` source path. - -learn more about middleware » - -
- -#### Database configuration is required - -If you haven't already, then please follow the steps below to configure your database connection and run the necessary migrations. - -* Create the database for your application. -* Update the connection URL in the `dev-config.edn` and `test-config.edn` files with your database name and login credentials. -* Run `lein run migrate` in the root of the project to create the tables. -* Let `mount` know to start the database connection by `require`-ing `pinkblackrose.db.core` in some other namespace. -* Restart the application. - -learn more about database access » - -
- -
- -#### SassC libsass command-line compiler is required - -You must have the SassC command-line compiler installed to use this feature. - -Please follow the instructions at: http://github.com/sass/sassc -to install the compiler for your platform. - -#### Usage -Compile your files once: -``` -$ lein sassc once -``` - -To delete all the files generated by lein-sassc: -``` -$ lein sassc clean -``` - -To recompile when any changes are made: - -``` -$ lein auto sassc once -``` - -#### Hooks -The following hooks are supported by lein-sassc: -``` -$ lein compile -$ lein clean -``` - -Because lein-sassc requires a binary to compile Sass, it often won't work on platforms like Heroku which compile the application on their servers. To get around this limitation, commit the generated CSS files and remove - -``` -:hooks [leiningen.sassc] -``` - -from project.clj. - -
- - -#### Need some help? - -Visit the [official documentation](http://www.luminusweb.net/docs) for examples -on how to accomplish common tasks with Luminus. The `#luminus` channel on the [Clojurians Slack](http://clojurians.net/) and [Google Group](https://groups.google.com/forum/#!forum/luminusweb) are both great places to seek help and discuss projects with other users. diff --git a/resources/graphql/schema.edn b/resources/graphql/schema.edn deleted file mode 100644 index 9bca08f..0000000 --- a/resources/graphql/schema.edn +++ /dev/null @@ -1,25 +0,0 @@ -{:enums - {:episode - {:description "The episodes of the original Star Wars trilogy." - :values [:NEWHOPE :EMPIRE :JEDI]}} - - :objects - {:droid - {:fields {:primary_functions {:type (list String)} - :id {:type Int} - :name {:type String} - :appears_in {:type (list :episode)}}} - - :human - {:fields {:id {:type Int} - :name {:type String} - :home_planet {:type String} - :appears_in {:type (list :episode)}}}} - - :queries - {:hero {:type (non-null :human) - :args {:id {:type String :default-value "2001"}} - :resolve :get-hero} - :droid {:type :droid - :args {:name {:type String}} - :resolve :get-droid}}} diff --git a/resources/html/error.html b/resources/html/error.html deleted file mode 100644 index fd31cc5..0000000 --- a/resources/html/error.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - Something Bad Happened - - - {% style "/assets/bulma/css/bulma.min.css" %} - - - -
-
-

Error: {{status}}

-
- {% if title %} -

{{title}}

- {% endif %} - {% if message %} -

{{message}}

- {% endif %} -
-
- - diff --git a/resources/html/graphiql.html b/resources/html/graphiql.html deleted file mode 100644 index cdc96ac..0000000 --- a/resources/html/graphiql.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - Loading... - - - diff --git a/resources/html/home.html b/resources/html/home.html deleted file mode 100644 index d491b7f..0000000 --- a/resources/html/home.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - Welcome to pinkblackrose - - - -
-
-
-
-

Welcome to pinkblackrose

-

If you're seeing this message, that means you haven't yet compiled your ClojureScript!

-

Please run lein shadow watch app to start the ClojureScript compiler and reload the page.

-

For better ClojureScript development experience in Chrome follow these steps:

-
    -
  • Open DevTools -
  • Go to Settings ("three dots" icon in the upper right corner of DevTools > Menu > Settings F1 > General > Console) -
  • Check-in "Enable custom formatters" -
  • Close DevTools -
  • Open DevTools -
-

See ClojureScript documentation for further details.

-
-
-
-
- - - {% style "/assets/bulma/css/bulma.min.css" %} - {% style "/assets/material-icons/css/material-icons.min.css" %} - {% style "/css/screen.css" %} - - - {% script "/js/app.js" %} - - diff --git a/resources/migrations/20200218062529-add-users-table.down.sql b/resources/migrations/20200218062529-add-users-table.down.sql deleted file mode 100644 index cc1f647..0000000 --- a/resources/migrations/20200218062529-add-users-table.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE users; diff --git a/resources/migrations/20200218062529-add-users-table.up.sql b/resources/migrations/20200218062529-add-users-table.up.sql deleted file mode 100644 index b9c31f1..0000000 --- a/resources/migrations/20200218062529-add-users-table.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE users -(id VARCHAR(20) PRIMARY KEY, - first_name VARCHAR(30), - last_name VARCHAR(30), - email VARCHAR(30), - admin BOOLEAN, - last_login TIMESTAMP, - is_active BOOLEAN, - pass VARCHAR(300)); diff --git a/resources/pinkblackrose/config.edn b/resources/pinkblackrose/config.edn new file mode 100644 index 0000000..1ff69d5 --- /dev/null +++ b/resources/pinkblackrose/config.edn @@ -0,0 +1,18 @@ +{:duct.profile/base + {:duct.core/project-ns pinkblackrose + + :duct.router/cascading []} + + :duct.profile/dev #duct/include "dev" + :duct.profile/local #duct/include "local" + :duct.profile/prod {} + + :duct.module/logging {} + :duct.module.web/site + {} + :duct.module/sql + {} + :duct.module.web/api + {} + :duct.module/cljs + {:main pinkblackrose.client}} diff --git a/resources/public/css/screen.css b/resources/public/css/screen.css deleted file mode 100644 index e90ddb8..0000000 --- a/resources/public/css/screen.css +++ /dev/null @@ -1,35 +0,0 @@ -html, -body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - src: url(/assets/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */ - src: local('Material Icons'), - local('MaterialIcons-Regular'), - url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'), - url(/assets/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'), - url(/assets/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype'); -} -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; /* Preferred icon size */ - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - /* Support for all WebKit browsers. */ - -webkit-font-smoothing: antialiased; - /* Support for Safari and Chrome. */ - text-rendering: optimizeLegibility; - /* Support for Firefox. */ - -moz-osx-font-smoothing: grayscale; -} - diff --git a/resources/public/favicon.ico b/resources/public/favicon.ico deleted file mode 100644 index 0e50cb2fb96b2ae63a2cf81adf4e1b3869cfe152..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 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 P-(WnP}f5EZ_VG%x##xBtZkUi z52fF+r0Vfei094BUkhAHuyZWs0pck_0bXkv&IU!raopZ=m`w5eY)CO9mPjxMb($o z)EftFpgf+@p8bG@3|1(x=zknKFac7+7`Q^QiEd=;9&tfTT>P_@zU^GJv1 z0{xRX3NMd2^F9Hn|LW>wf6PwcLx8T8jG^LnK|QmQgeAb7Pk)jSD7vpNYQPHovC}(D zN|?;HXv|20$dm^Pxxu3jP@xQu6y>%Rig)5ETy$^F7ar3p-RuTO8Pv730PIbC^?YgnLgbFMu6XS&qDpv^H z2e^xrpI|*pUmKElhY%QI;9)P(j_`!ir10{tL{b?%e_~vnUL--=4M?xy3Qb&$X zU=pK$*=~{X{Vtl9H(4%~@8L(((^Iltw+}+X*4tyV;jMWn`K)E5JZcpbNMbHxrjV#Z z;b&Ef@ME50$L+s=d{)960K)^W2XHw;5|-mx$;TSQVbm^|Un2ygbf={KK(9L{m{P!E9Q3tk{; z3PK3-0|8;x2~Oh`Su0DdZB^;~=*pIFPqiC6zq;yWQ0F2GZbl0FbTDb79Q^n#9lgbw z?z_is0A$gWfK%g-k_sYz-Sd@3#~E^QHSBO1&zk6t{Y>RD@5;X>zrWB8MLShf%a+~Z z@y=oo8sg^G8O3j5VBa51Jb%2WYAXxgCGE;Xk)bBsoqb)y>u1eU(|Q@eaa|JRvXq$f zohw0*T#HK{7^`FQWyaSb_WN_^s)XhgHJe&u$xT^ zi`~MxpiKF`X|c9B^P2n_VBuWv%X zgs*H^i+JcIxXXxvo)Qj@IoD+DJALx?kJ-dfN|I%8pUx})UkllhPG7#|pZVv_nf+-M()Z1|B~hYG%r({;%_CH|aI)w#cwu_0=$* zZFs2TtTNXM=zf^}yc&wl)uow&^;x3KNPDWqeBgr?p1lbUMeELEOpIZZ3E{u% z{kdMRpB{Dlf4va-m=ngf7>S7Aq+~U_@N;tb6$E+-P4AD%<^1TpYTnmU(<`Wgww54l8m%rX>PzOifjFe{b*g^=2yc2MWo{dFSlw zUJOyld#A~pT_3kmWw+zm7(#3J=}E-%9r)q(PcsY(F(Dpa_?jQN+?=!h{~o}SI5=FT zFllkwZ2UQF#;{?&0RMAik2!Z@8S|UEblU=M<#l(uyL^nr)f}hOe!rW<@6{pr#YH_# zgPW6+6|qo}_Vw)zO)OqX$*%M5J_rs&Sy|claXWPLUfU<{bh!tDW6k8cJMzP$OKE!~ zF!Wv3i{do>YogHeQQ_8>z8f79^xtp{8ZNtyED!32f;MIPgrJb182m4mC-Y@WN=l&V ze50X-3Uw3`A#b4nD4D!KWaeEuyn1*yZF}>}v=ehRx(hBEOJUZojdSgO4N$A_c{y)h zs?wra``&PAY2fMkyrk#XX*3iQL&Q%G$|xR+AtE3k;CDIz=04D6vy0vF^~H~d=Avc{ zm@?@v+?*yjr{kXSlTGv5ipJ_v2hw9$&*kXk|0>j~JfHSc1&s$B)-vt7fbh?4@5o3^ z<+fdG*&T|7fr5(T0Lu4zr`N;TzXreOn&N_E_4!H$o}FDSEj*(Ow%(Zg@g;icQJ2{!RPwK&D=5JpE?O6^_6@A?m%p3gmv^ra^Ll2ioV-h<$nklTT zK(Y+dXtrH%aoB}M!iNGej^*$=QGfZ;*in#W@=p%ueqXQtEK%fI>8V^yTx_8Wn=hK0 z0;Hm@Zgs->WnWS8lI|}n`=&Vv7TNkH^?ii!$^K_=$+JnMx(>8d-5HuxI?XY&+Iz@5S9 zuUn&cKUr_HX~XY%djy0!atZN4YMpY$!q=}~fg6vEj*bS-kBI?PnIsv?-)~ovpZu?{ zuPfARu(7cZ4iC*t%(8`i_&$_H-;0D*hj#c~2#Gf>9(__DyP-B!rMYu}xztQzvzYz; zu<0jEO+6TR_*rd$IbP<|r%&h@7>5&?ZdW@4cHM8Tr%PHW#60fLx5w&w9(T#=y4yZ? zwY98v5e@?9k39j9kr5HMo}DW63d9L(omvlQ6jIJ7^K?8s%O+z^BESK}?g*dx%HSX{ zygz^byuCb>6crKi|G5{t0B@f|-<|N?3)TDPKBX+AX32kC>|9MK%_tFYS*LMYB#1G0 z7k!yQ6@!3)czeD(U2lI#qWdBAxD_bqbziAbN6ydBPem1sd-wS0ZdX5hdgtNcVcF=p z*5bg@^=LSiBk1$_1##^Y>L*aJ7#hxh$HVR1_tcaW-Wt2gk!hJk&L7uT*INey1pTcVr5+b=^&upWkwmTRdPazEr1@-#=0$!@ob92K{m_wlw z{^PM*T=t_RBqYSMi8?2M&DPrbbw5?F$!4|5<#cf(liO~i{^>IRFLh#>&&z#m6z-J> zcmw9+wXweV-gB}m=Bjf7U3D&f1L7V1zZ?|yeeXU=7{}u<^#-8g{r!C+jaRJH>2Q)Q ze&)q@)9qr7w^dBYeR`|3wDjZsq1!Z+$qoV}!iYVU#p^UztWZ~1N4#^s0W$IiV(aPV zu>0krJrspxa&&ZbY>c7fXIgUd-Q0KKY#tB8tsYTejS0D|H4e%s4yOng7E(5C=sx5w zvwgO15+b_=Z^k)teV@w?HJZPbFwoL2H`{IIbb2xCwEb4ANR}<)u5><{7A=`05^!fd zoDD9a765W`sr;IEF-6$hYsYP&$@x6*Mv2joUOGw4MyQ5SGykMSpXDS z*!R3~$vlIZnOVr`K;G5W6-XB)B{WbR9#St^FMScckkC5-i~wNkiiwHY+A>Qjbu1I{ zIvV$fp&5p|gL69HUbtgrGdaw@$;!$mjez2T)K?rc2x-rB`FOcmRBN|hudFR;Cm;ck`(M=s*&j{k|1Ar> z^p~%^MF7za?Vz6k!$VS#&nbA^4E-o&o;`xcz?UO?x^pVTs+sOivWOUIAdxFGdT;P z<3`Lqz-#QG@dp5cqe*ncv-(j8@bChMiAhPfy)Y!eet^G2>jZ)r;@`Mq27srGjEq1B z<`)#~TyFo(A8pgD4ePI1Bo{fH1HFb2n-X724u;_L;?!&`XLHc)h z`vE8H?(U9UepOVQ&JzvP&HZ@hAKRGmTKl8AUxClGd_qG?O3F$3hlc`kVa-3T8*MYA zK2m>C$gn%Ulp`V{LLf?qnv!yj{-2l(Wx_j9f54l57i+CnOVw}Br#1i8HlUCJBw)ku z&)WW=NLBSfN2AWVHuKM`CU|B8Wk)a%*TY3*)<=Z(DX^ zm_sNs(d^vZlO5Rw{RPpci)p6@`TaBu1uPt#0q!8xKBs~opn5ecG@&{;IQ&P+4${rk zs(v;C{0>NJ6I0V_y>4IN);JZc8drev`87Q^%>ON89Fr}Y1<1t4I!Rbe%>BbdO-02n zPc_h-05Dd&_p?wc(@E&i`u<;1Qc_S*P&$WM^`rhky~$WsW|#_nHlUtf?~ld5aees@ zsQ0C_h>Kdc)8tvdZqxI=NUb%DPQi@x8sh zGvx1Qkkigu5>8?Oztq7Yx%K>7i<@`kr-D58hWvcd zDmAZ*>sijXi*_*3E5S8-Dk~?fL^840Xm|2mjf78bXLNLQj3m;;5^&p&Bv3K?gvq5c z$9JYDBqRs{tP+OU+>ppUus1FxB_%UcE=e>ONV(f1(S9F*Kp7Yq02NQO!Bhd@MuHE* zXktqd)n9l!t6S~CkM|f~S*#R*xr=PL=)Zsf-~qV8E7Ta6|A22;4R+#?ril0K;p&IRL z&ww6--}UV1^t9dk$+2)I4Zt|>r|aH87_Un@Roz|yQJhlKGCP7gm?)A zHnD)cVwGCW&&G0qCgnJ7@mw@a39SOrUOQJ*HuwAYZ+TD8S%4d{4#Ec;0uu=|F+e#4 zCqQHeK&at`ghxeL^L-h_T2KgW25`jhc$Vt@@?@a`=uLiGJm-GAngMx}IZqB$ zqw(Qkdyiuvg@MQ-7p&87d_MQQ|XdN%~MyY_(5dJ@|w^J z<3iPymE%)WG%?(RkD=$nM|aQF>uuk1h1TU@s+5+FF3`jKKf}O4ieXU8rwRM{ii(OV zDaFfuWbL;Ai8FuN0>h7ZchKsFZ1+UHIZ1l+S&Zp3J-!NK5d@xn-uBmhKc2rdVr9rE z%nV&u!0f+2?#42UZ%&$feO$q);O)!-E>d9d_z9z&GhY^k5J7ZK6266D7pd0KD3 zFO^^=JudTlL0ifn1V|wtFWnz=r*}mBh@ED7xyvKXW#o-Xou7Uif*B7e;v@Ej67o>> zxn`f zgB0K*oSc}Ia|oI#=POlK5)wfVkcEFkkiDc&Jhdq~DLKi=dVdn0*+TOVJ{G0w%6|Wz zDqHmK(CMrz^cZ6>)GVb=;u-lc$4Qks`YPt*t-?-HG(V(XeVqsc{gTioo@9g+qs_(2 zP4jU*GxlBZZhkPDKsuKAJpEgp#yBft1-BJvMzPrkg+$l0Ly0vUIXOj`H$_l&sy2*& z+EJQTM&DwURx^MgK0pI7nOZEL-iK049`xsDl@or72@bP%i^&*vMsZ>20QRWbpgHke z1@Uzks5x5eW{FbS!T;xRVZuoAET~bI=zu0U94(j@eufhX7XpODiA0Vs!O6<0h^dHK zK$}M=ucQ&=b2&Nz?h-T@_;&{6?!#F{O$VRdh5!o1G<3B|{UC4y72z7cqAAWm}v+i<%OgA^7$Vm!R3r{wlK%akbTGM@z zrTcg+rlYgY5AF~*Z5z0YL^#Kr{I#9&iZkfV*HMSVaPFe+(yrCz(VHxb*Q$(9gQEG){n!GWmw03^Gr-d+V*+Ao$b{%M&5ixLf6~RXDz|FLf9)sz3}QYqYUY=J;%o2U?=dxRgIPYYJrO@H1T{?-6_+Vy%p zwvD#^)hCe;P*(HBVaUDlqYq|__tIkm8YSMH;QbKeA;TS}Lm2u*>B2iQ$V zk=vYQqokxHz+(BbnE_%6bW60f!=iQsh|uiv?7-X3SDU%V)L{ew#s_GV)|~hlKsz@& z#_58eQM~&+Ab9s)>MCf68hjfal3>NnVyXi;Yz#EDLW2jp6gFfMGybGmBS1IwzP;~vE*=^!f5#Dx$rMoo1k_YsqLp;$ZdN}No%^jdyM^NTMdBiCO12nwXw-VX)ix+edx z5FX*uxTw%jVGm>0A}PX9i^j{*Or$l@5EMZ&kUJn&g-8iQv_QOw;_w=phxkLik#75I zWl70_ntSOsw({hk*)Sc z?JH8(hM(%mu~%>Ha-1|Z%03;Wy|_UBnj?hPlgrloVmM4dlQbbnDrJ?7mGtNVjDm@S zLqkQ?XtTOFrhZ3kSmzGJBJM80vG?>QKLA0ep}rnadVP+q(z7py1nI6k+Wq>woKM(<2URfaD`9 zCp%#15E?^t&AgwCwJG!U>wjuXpEMc)!D^XN3BVabf`id=W5yF%7#RV=H9R`H!xQ5@ zk`fnpIWA0t=x0sKQ~)6o;$yr_1KTG*yzGDCJY5>-S<@n|L3HqOxr?HbyreVC*x^{0 z^l%WS!;)h4+DHTWPx@3PyTe;ry6bdViY?e2H9T&3w(~y39R%ucB`3~m#X&{EUa*ln zxa@n?VLPYD)p=AI)%wtFbu@`376hv95+ljgH7hP6Ah&D)L^)b&_qRH=z{enF_P5mg z)v~$JN#l~7ipolXOrMk>i>Km)=>WuN_|xU;zgu)~&nV#Cid!ddED=LSxSn2IR+!3^ z@|sXSyRZ^kptcHMqHXqfUA3B3>x?k%ck*JCSH`I=Zh;FqM|4As&L3^{U0kTg^iz|H zsgh*AeZ`f~f@j>@2zT#M!)LP}787uHJ?o?)29a6rjO{WL4u2QSdIs9FuSK)c#2&v} zX<3+=b?c2snm2vN>d%(bDRTXcB4TCpZ}0H9UG|1Y#81jOQ(2N!u-98s+|ErcT$1&# ze~}Q$ycFIG?sy3->k#2>72d{=VZl-$+S*`$k_eO;cw*?Cf*O?iS=iesHSI;6Q&8ql zs@k2OjnO7yTC4+L@F7dOqDkFrUiba)sPu*34;PD{7aA&aOpyL)iiIa7VUodXmRd%* zcX7u+pzd@W&lvDy?IgG(;zqW%66jWDE4kd8PXg6My;6e>&?5NHn1Nm!&^$YVRu|y? z%4N=HYjiAKd}?LN0gZ_0Qs{)`$jGXY&M@*2Ak z=Cc$Gat^14`@g8V0HbLA6h(6Qt4RV_0j?Z(oOdG5g~FSSI%$8}JQJaMVieag{#-rx zGIO9`NKQ_UkB?XQ=I#PL*aEof01>EW!p8>K=-A)iud^{c^nTs>F()0n@IG#80QYaGvY|2!W1VP9sLvM{;<}5I`a?R z^Jt=-dSmGOjN33I(sRhKvp!&73B3F^7JXU-w42Ihe@3wWAVP}+3YV?z1)$*z1L{2> zOe+pGTg?xQ~(OnQ9HRNGA42)~N))VJ@eNqI0AP(0YWc z5Mq(Y =khJ4Wyfa554$Xg4rD|vj1X`$|oA?db{in4i1(oRH9FI1#kE)*BSt29K6!VkUlA*&x#AE0TGdr2-TRzz`lv8 zX#y4|gvfry3CCH$SEoU@vxOKxP)p%BKF%kQJ)L*Q?ENoyVrJFOeIm3a{}apYVlyhg z&f1;+0E9jV9J-j;X$A6#&Tw~nK>ZdY8*)6-Y|61znRP_mBcSKsscSUb2ug=(&Ti9! zUsE>3eO2jx|NC!&rkk|Y}c{2Bs&To;EO>6OX@Jd0Bsr^goh>o&i6!n)#j^YBB z@0kcY`Nta_{5ztNB=N?qoAe?q0cd6n`!NIiE&{Sp2)k7)sb@119-eOI6cBQh); zV&sozaK?1LwAi5#(J!N4w0YJPE@PQ z1>`yx%6T1n!g@ zh5e9vZZR>P(Qn0+#1PS}`aLt}6o!0KLSG&n5SQ0_R5vRjK%L)EK=i^zc6spOAcK|S z*nrsOKz;q34rC3FCbX&UnCX+a4=+gpe-q16obnB-E7-VSH_3tJIptOUr<1Na^r-@` z65zn@95S6R0L#=9*2+}%+Rb83Zx;XL&ChCYidAMzkU>c-@UzXD>Y#;AaXkVCpYKGYp1=F+0|)_8aBeheFMQU50VmHs|4 zgtEgsyrSBS3BLAjPn$*`BHrpv&!jxTJ_z~7KRX3mx^f>ydrD}!ff3FskZ6BV9Xg6F zl28zh6$^yYSU4TOTfKBrmcNh+Q2;^OO9fsy{2+4X#rfWDjNwEWV>ZPaYHn zE%WItG2zZI322&Bxd9EWB^sCiUK&@5)7`kambN+aqb2q`nZ0yJ<98FR+TyT7#&9|- zlH{7oRWf3pEt$o|_6%xbiiE+0*?}=bgt_~Etrj;)0W?o|va{#u0sF<7H7`Z9g{RCeWw<{D!BJ&$^;$sN`=|6_6G#l($2L4fUo6cm9A>Itivgh>%Tp_?&|-&4R1DRV?=n(PoF}3dJo2Eqk`iySUlD>x%fIHk zmGU&N5K|y0x|d3;qBM(i@Y|ql$#lSQ#C~qo41>XT+RE@ExYC8|j6gsP#f5R&C{xcY zwcS>Dv@%9as=Y{XFZ@Ihe0>-zcwYEmTT#Ov!a5KWg288Kr?qHCrj1-o)DR*xV4pL1Q)0Al(OAbP_X4 z`xHAU5TX8LI2JOG2x%d%l{7{xHF4#dmYhU+GHhKk3X1J7g&j7r`)ocf6F7o^zlA#J zvDVQ8t_b;aC&+8eSHlSmSKtotj0gKonq5J47>qs56=zt1eTU>|eB_BsM9n|A=5uZf z7}=k)nv^k(*i+>j*-nSM)J-#DkxnrB%@OGpEs)TlQ<>aIR^+SEN|)d&HdK~^CJ<>_ zyaRXf4s4)XinvhJE`5lRhgPbRmYpFFh^uu#B!Cvt(6;AGYP@>Qu9ZI(sagb~@a$nY z4B{dg)I&d{ly~Jg`R^1@7|`iaI?RP2l&!~3Z@gD52m8gK3ZYO4BrHF)?K%bu@vd95 zgaA^3BB2hn>j@nTt&}5Hs!#d^#Yc<3T;{dI5o z8Tm5Cz2;A+*-k*JnVbo))eChP42=z#%n+<}l=)ab81DWab*F_8*=98uCK%W+YoEpS zFGz@H=YmD~k&S(dPUja5aSwux-FE?=+T#qb%zlIf`0(aB%l$IAe7COx?p5cVRJyEQ zeFQdO0~O*mjPFh{YUr_*MUYAvSefpdr$>OI>f4Osxqj;>>R;AV83zZYW@ak|$qS;aUr zIEF3cfSMSxA{d%z5nq%=9ty;X9WF{Q$qqMc(cB+sb4n3=oA znvxwcWY78SLFdxF?c%V#f&h;L7$n_AVeCj(-(rfpdSgkIQv++DqqVzF2qPjUrhs14 zi_bEG-N&%A&UdfBpQA0UUo?yD+%6f8v!_pUrT=9+hiZd2oe1`dxn##*Zik6uD(kQa zZTae4e9XQ6>Fkj(pkxv2st@m@U(74T9dh>6zO#a1;}?x?h|g~sYDiu(V%wQIH^n=B zmaB=snMC5Q9ncGN2^y*?3{j>P`J zJaPflDe^?L&3ftw#EsV7PER%&3DN^IV}GKn$?^^QHQQJI{OCgkL3h%P6B3P+zTN(@ zRj0Gh{p~T!4N1G@mCTO^mCdc#&Z{?!EIYvd_Y#EK&~b)bOEwv04s<*s9V3DQ+#eq& z$uk`*FG_R7l-D&&ubd|S;~BWSAjQ?qpB=#%P-=n>;0SkAd+^pK#4r9oYr4NAO|i$N zrXiv0htsl@Sv5|&7&bAlKwE=#cE8+HV+5bvQ)IjvocDW`R=ub;nYWXtZ~7tKFTwKq zw^A(?c9t3#r#Qcj(@dA01D&CqmkZ7ZWXM0`A#rL9IeF}yr#Iut7++<$=fi-EIX5F$aic(el=j1YJ>n#28; zhfFt$)@DII7}iVFaSQ5X@Fc7nAwJpAQdJzsaV{r>)Iv=YUG|H*%%4ujnNiX0tI{)5sMh7sGUgZdQX;I`zZ*n2zR2gO75=M z^dkyJ;ptC5Gkhhu9o+eKz4OZv=nIzE1d4TJG*tAR(7x*r7JHULgUBD)6(d9$d)e`6 z+uORZc6y(MCk>b2px#VTA8o(&-w+X4JN#ulW~4&vS%ItHAIAWLnxEH#CaAITdsW6h z1xmonR-&>AB_$PW!wH&L#7gW4qS_5opHbib#UXNfbj&$jh(q!;x=8QqXpp4$lU)6V zw#_CQK-R^KwPX`G>?5dxp-KlGjyu**iXEr|^@kr2eX#Fnuf#${E@v;Oa`r4{UOLKg zB$~6RHh!YB0IblU@8IX6HgM@b5aj8(G8Y*x_LRi5@(_}SJVEY)ww`o;fg2h+$0Gq| zn$-uUK2j%=^~7H7L6_6+jmlIH2Ra=vWwww zYvMo3bw$YPV?Mc8oDgks=6)sh{*&0pY2ega)|E3C8s=l1G=zuRQ4WL5(+^Qh+Yd1$ zHkb1>5y96mlET<}v*qdYw>81(l}|+g(Q`L?r#xa7-=l4uWxm9wBy4&Z)LIa(DrgHS z@Bqpa|JftrEx3xmoz~=3cE(SwF&xxsgu?ZKyoP4?G}YI+Fpi)i$b*VfJpw&-J>div z@usk9E3XKTv+p7YbMe-}3&K>QWh}2jD;-HGVRhGLNznTifOpw^XE2I|1Cj>Qbk8Fy zB8jlkOf{=@E2>zgv2l`KVCcjPKN+nALw$Z}d-Xq=6m2>55e%ZmUu`K7rTnx~l-}Gw zlSSxN0SIs4SFft`>~I*fi_e#`x$T(G=;A~mcR5}VD6Q^a!xc1Wd^;im|D9DH4;v5H zz??HnIGrD`e~<`#h}vEa6sE=)?~^cCl)8};gDh|kcHS z57FcFfdqne|~Dl zzKMx5ft|;$Yj|~45FiN?U+i^Xt#WOdQ#9%Pr@s}2KHAu~mtO48Wf+LZT^CL)sdVS! zs`UkWm_j2(fjQs4S(T=A8nw1@0qI^0D&+I1~kM1!w~nC((*Cga*@NlpEx<@*`YlpvN#A+|~ zruG*drJ{JCFzM3xcg~A*+aO#|B!_?31MZE20>B-C&8yAla2*7wT&SKA1&CPB1;PkR z<4@x(iyuct6fi8r)ozW)nWAZ0%lj7s#Zyr}=2^$1reLFEXV= zJE@w`M~rR;J^sAb$?ge>p(XA(c4)O1^`FyfHGf%?U&wn?l-MYz1M=-~UIP_{VQNMO zeM*?u!$ox6#Cbn%36UHE0!n0TtlRJ&;0Exu{BQ-nFkvxQ?A8vWi+kT!#!&Kug^*X( zeU$3z?x)QKUe$Eb1A_7fPB_l5l3WdEG{b(v(IruGjvm=0?b zWyB!%MLOhohL$u|>bF{>U8a(51UZqNn1@(zHO%^AavIv*5{IoJIT*+G4-R#QJEs9V zaHvboh>$ZRG@|`*eswkT!}Wl4NDQhieJIF01NjDpNm$23$VlVrt-kLxIJCOW^%%*s zP}GjHAEq=p-ew*sE`FbP;8^u>I)$u{?|`7_Gel!$7Jh~@>++~45@|UcIrz>_TJRY| zA8uc4CtBb7){_fiP=nmSJcFE>K;Z`olb zRfrJ*CZ;Mt4qZ2Dzq8mpR60NUm8s5NP3&+uo2Y*d5CQZen*JB)$F=isuT^5prxfDH z|W89_IAQbtELTWS-9Y zpLWde2^hnNhoRL~?lYTs2|rW50nUpl;VL-CX$-v+1`hr{CAo-z-ba|BR1;KDu1E0{K{14a3CmP79)Ti3ic(S`8sr!ABLiwe zG~aUcajEI(=;-M!Wm1h53PaqE3GVp5_mg12NI!;3JvJYsGXIdtm>Fz0KAqh5@UFDj zDUx9t1#$2=1B2=-mS z|7h~XUje>0q4ad5a8M^8qzFFsLokwgz>PVdIh$|&1j26En|e})Ep*0ZS}@XK(sEff zhV=%6v5lLNOk>x zb0yy&P8Q_KoV7KQO#Vex{t)!KrQ{za>*WcQt)A7jqh0p$c>CQbN7I$O=26?8&xKw( zCDZvhSNxa1OTZB*lPqB6E!hNkhhhXgd{)RMF4tgpI`x;g=sO%Ugw7C{43Cf_b z7@Zx3$C=2X!MmojS0XH|M$fww3h7u(Z0u^S=8Hoj;qQRc6EJoGmOhX@^(i9A`FM7K zOgzfr+qak|(bYm}ltZS-Z5Z&VcEiEC+TiAjh-alv29v*XFg zbgkd|-F}1wifiTOREP(C({R?e|B)HG!QoD|IjPT9k!MV1xN7OkS820l3C^+x#f=X1 zqBWWLu_Wt^voLI-$jJeqiclgf>;u@k!gbJ>yt{xfu>b~le0=;*^B>@&d40{g_Z0u{ zYY(95?(RZ3kpCaxmk8`HcVPiv?Q{4o;Oa{#7Ewx)t;SzCjc*=ed4E9A>ku$E)ikAx z*0Bqd{KWI`_{r}zt?qwHIrDI+{(pfVBx=Z#YLY#nvSiEBgfFs{twqSL7)$mTOr}J# zlYK8`$&wJVll^PSz9jpaohj?!zCHJMpZov4{u|HCGv}Q7yti{+ug_wFd0f+m9n-@& z8n?x4W}%6%DU_Y0deeWOTPg%_cZskwYb&M)C)OtTtBrn8w{@^Gz2q3l{I@LWe07D4 ztITCu$3o>Gba-OouMEu;pu>(HD$1^qVC@q`n1l9rx12j@%@f1J8P2etVOznz){Aa$ zYrDUbB@N&h{vkZ7anxS_PKjduO0M4X{(w{Mm1gZ|Yf8mhxMt#8+}~Tzo^@!tz4;aX zTjJoQ+9#4=$9D&+xmAUyJsnan1No*$A99~EC6NstH?VBvaZ6zyJ0U;Sx8ZVLJC?td zwE~B;U2TFgP5B}yh$ijs&}YLNh!!6cC>Oxt#siOD_H6M8vYWDM0%qnWu*uWY)3LGW zf~^l-*fvZ=+)>olU8yumiAn;3LuhWlzGnGWcL{Gp)sDz1Fl ziz0G{6b~(@Y204f!l9)(`kRtWD|O06r_L2L&gOqLH)Je7cKm1bnk70NNaMQKv=lFZ z)*-7|BpS0z+s*@&3--xFfcqXkbQ9h(TOF-rRc#nFxt1vNyv(y?rK9}0GfPZz^5=~Q zmw0%jV>+>7Y!LcTzb28lxMhg;+dLJ2`?n8?m{$QRq?yD;Dz_&G>-(Q5ZixC5+~~em zj@<4ywrSqJk|ZluQ2hQ%VGF;RqFMZU^@w4o{h8NF#r!?jQTu^y!RZDOYM+3ntbm*| zh9?Fq?2dWu1*iWW4;yc4yqT_(En1-Q6ky}U{@kQ;mk|FDVdh|X)CDh{zb(S!(|BnB z!-9#IDvb`C*a_ekk>+F%9R-t(@?;4sK$Mc;SreDY!|0 zixz8mJ5cnZ=y=`hWm24^WIlHlkt){}K}u5>>?;`9 z*w~nu{!B+`_V?yb@~_Xrqr=h@kf^{w|po`IeNQrxWMuw{1& z;a~?6=l=eFU0of3&#@5^6o|OEIM^aQcYdVE5}=^3=o3Vz>=uatK?7|NP6f0R^tC8+ z7FJeT1gL+r{8-~PR{%|>qk-*=u;%iEY;x>{GW&BEU{4=EjfP%BQg$BT>0>W4Gx=m? zt7~fHyw1cB_7{!0$@F}LBqh;Evj;7h1{Qj*z>0sw(aCIc?8ssG_~zkapr@c*cXnza z^nneJb9J1k5v30d<}9aAQy>sl0`AAgn4tla>u5`nO-ZyGE*9t-J?T3Ma5VJAP+95q zFoHZh7C=3>n{lv==lodp-9Lce1$hV2Njm@CO;J(N%Ib0>P@S?olzf7MjcAa4&Lc8= zOQJ!->%Y;jKjr2&pa%v9WJzmvuc+u;z5Y#CPf+23A`rLylBgi#D`Vqn@@hz+Fm8@psbU0QhykjBl?`!S~&CnGf@`ookCzSa1ZKoYCJW zr@q1XIdp9x>oA?9)Wg|Bszk0?b#oWUsVFMegNN4a9x?^U9vO0EhJXTq$mQx(?=EDI zR*!>>g}l3$p%KBOE>GPhw!d65CI~$F=F-rpWp~B56g)9|j=rBv4!cQBFApDOmUh9a zs%^19MW#bFjBf2JIL-tw_sLuEpK75^RSNf^!4Kb6pxHl z^dLTF5U|9G#e0*tP8zZ_ojG%c?1h8W1HZI$7rD5Qamb#exTPgq1X|7`H|9*^36NW|{tXU)?9}2$SX3cIm}U2kYYFYpk6uJzS^Alx3q*Rr zeg!oQslkeQ0gv^4Se4`DT(y3ZxKHBTh+qMDF|1`qk zldhR^5K16@`1URKTT#OGr(7p+`OR%@_2|wt|Mt@M$*4}6TA`% zmDgRDbW`c74t8xe{y>{ZE(c#5C{)$Fr5w+&)g&u{rm5#6m($YHf~_`AYhwQC5py&< z@w52p;^N});NTl!uL<9Cd;x&}&Y=Vhs0u-pfOX{Lb#lV9<^|bN%xixXQwIq(YI^}a z1p~G=6M&kR{Vv%5`nFzwT5~PX3MTKrgZbv9zMLHX(Ieqd0{+CrzQWCmY3pat{)TZX zYm+Se>fei58}&oUH}iDOp)p{D#$e?im>wJ$qK<+`$uZSwdo< z&U55M;!c%v;HQ`3o&P|JS))g$ousUOBQNNkjJ_BN#*`bB{klXif8;z$s%l3%7h~$p z)(`Dh4b0*Pzayj))(d3fR@t`%ur^F(!n)Wy5&@z5fdPFg4%$;nshqXcjc7(g2HJtw zTISby20JOMB&t$5G350euBN@&a<|>Z)D*1wf_Vns^e2~Z*L}QWPq$ntU)qMx{{5#- zcCKwK**!7uW~9w31e2?jhXs@3d#x{rhrCg0=7-OdOwko%fxf2bUGc-!C+k6eU=p58 z&nuyv6ef(VmRf*~;aXk^1)=YZUMGJtJ|iOoG>r7Jv#6woo)i?fcbL5w=X8QG|2uq> zwVliCeP>TVLfj{CpXqsdmBvzrP!`Pm>l+ zh*%{s70?HuX8(FTxDqvDN7&yj>cU30JO*4Cs^YdCrhOSYyWtXh<5H)YfHPM?F#(Kp zNO%L=9?ZtV7(MRc;Q?=?LSlrBEkwV9pjBLa-B*77VBa&O`npXv*}JML83~D!>S}fb zFOvd0AT>21?+Od=rQIE=Ca@#qFXc2eG@x(1ckiB#PCqmh+D*udg9ER?hPP2sX&*N^tm zg9nrdeBLJvO1hcn`Pa4U@1Zqfl!sGv$Pp3}I;uC@XBTHK^`-E}*q4Rfa;t^B<2VeN zOJ0S8zQ(VcS2MYOJU>1`BO{`9hAouJxWB)jTn?n-rGGN0G!48JdiY?D_F_qOQ@0n7 zlNlQ5>&yS!ixsoY3)?k1IxQdXcta$bsy-fx~EI6qc7?$V^wL827*J zkn=uqTUhmDU&~!RJ^$IycX$$FV{yb;S|qmJBIU;%aR>O#x5>!|aNTfk>3pX*)Xx>n z3!*=3^D!-{T;H{cRJ{QmMRqov7dU~bsb+XQOJ>WPa~Q`Hr#yXIT2z_zQ>ENi61%WL zBc5Ej>GYmm5Kr3Wa!_a--hce42;-O-bFR)FKwdMgF;nLSF28M+FlmO9O%UqY^ zYfZw`(j=+odwcH}$kwPDc=!Bvk*r(;Xu7<-`~)Wg4J%b-D%`AfsKdUs7T>k2HKF&U ztR5R19qqVWC%gnK*h=rRt4)SuseyFTsT&m^RqXIfFl>P8&CbHY&&Ma}G~GNuKMyxY z$ZD*M$ZO!p65`{Pl$B|ZzZ$aj+L-qLF&Cl2?OM*7F8ebz)BUoP%@-=y@)5w?CmgRptuc*l-GL=s$7@#Uz^%qABMmSQZhF-P8nN` zQQ?-n#7X5>r>(0yzqRKE_m7~qLgTTEmq7rtt;x30aPY{MVq#)4fgRCCDw#;2P11G(q0oULP*Ui-_UW3p; zYAgyTT6NoVOH194+Cq-AfpFIY>bK{gdF^nOKa&k8k$cVw;y7)gLDhQYL*tufr%-0b*iEkfK)q<32E7jQkPhA+Oz=)YEMhQ+=yMF%!8K1zeJ{{2IL zcIt?hY1eW~^?rNe0H6F>|5a~Fa^oukF(3a9pm^`E&N8VK8U8R~nI`?zsd~bHcoE%CBxci;3p=-tMmC zCX4U=-2gls$FHjAdmvCqelmA>ZwnmdFMDHWRX7zmshg@>M&&|0xsHJG1Bh zFE54bj8Vs5B_$<&$_l8lw=^+%QD~u5b%@`}7_v=n+NExjlJz3J@oM>dPs$#}FXBU# vsHWd&7g8)D^d6AH>&G5alL$jF%8z-mNt;|wUcCexr6Q`gG!#F}n|S{h;)aGH diff --git a/resources/scss/screen.scss b/resources/scss/screen.scss deleted file mode 100644 index 7f38ace..0000000 --- a/resources/scss/screen.scss +++ /dev/null @@ -1,5 +0,0 @@ -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; - height: 100%; - padding-top: 40px; -} diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql deleted file mode 100644 index 28d2b8c..0000000 --- a/resources/sql/queries.sql +++ /dev/null @@ -1,21 +0,0 @@ --- :name create-user! :! :n --- :doc creates a new user record -INSERT INTO users -(id, first_name, last_name, email, pass) -VALUES (:id, :first_name, :last_name, :email, :pass) - --- :name update-user! :! :n --- :doc updates an existing user record -UPDATE users -SET first_name = :first_name, last_name = :last_name, email = :email -WHERE id = :id - --- :name get-user :? :1 --- :doc retrieves a user record given the id -SELECT * FROM users -WHERE id = :id - --- :name delete-user! :! :n --- :doc deletes a user record given the id -DELETE FROM users -WHERE id = :id diff --git a/src/clj/pinkblackrose/config.clj b/src/clj/pinkblackrose/config.clj deleted file mode 100644 index a66ebc1..0000000 --- a/src/clj/pinkblackrose/config.clj +++ /dev/null @@ -1,13 +0,0 @@ -(ns pinkblackrose.config - (:require - [cprop.core :refer [load-config]] - [cprop.source :as source] - [mount.core :refer [args defstate]])) - -(defstate env - :start - (load-config - :merge - [(args) - (source/from-system-props) - (source/from-env)])) diff --git a/src/clj/pinkblackrose/core.clj b/src/clj/pinkblackrose/core.clj deleted file mode 100644 index 828f588..0000000 --- a/src/clj/pinkblackrose/core.clj +++ /dev/null @@ -1,74 +0,0 @@ -(ns pinkblackrose.core - (:require - [pinkblackrose.handler :as handler] - [pinkblackrose.nrepl :as nrepl] - [luminus.http-server :as http] - [luminus-migrations.core :as migrations] - [pinkblackrose.config :refer [env]] - [clojure.tools.cli :refer [parse-opts]] - [clojure.tools.logging :as log] - [mount.core :as mount]) - (:gen-class)) - -;; log uncaught exceptions in threads -(Thread/setDefaultUncaughtExceptionHandler - (reify Thread$UncaughtExceptionHandler - (uncaughtException [_ thread ex] - (log/error {:what :uncaught-exception - :exception ex - :where (str "Uncaught exception on" (.getName thread))})))) - -(def cli-options - [["-p" "--port PORT" "Port number" - :parse-fn #(Integer/parseInt %)]]) - -(mount/defstate ^{:on-reload :noop} http-server - :start - (http/start - (-> env - (assoc :handler (handler/app)) - (update :port #(or (-> env :options :port) %)))) - :stop - (http/stop http-server)) - -(mount/defstate ^{:on-reload :noop} repl-server - :start - (when (env :nrepl-port) - (nrepl/start {:bind (env :nrepl-bind) - :port (env :nrepl-port)})) - :stop - (when repl-server - (nrepl/stop repl-server))) - - -(defn stop-app [] - (doseq [component (:stopped (mount/stop))] - (log/info component "stopped")) - (shutdown-agents)) - -(defn start-app [args] - (doseq [component (-> args - (parse-opts cli-options) - mount/start-with-args - :started)] - (log/info component "started")) - (.addShutdownHook (Runtime/getRuntime) (Thread. stop-app))) - -(defn -main [& args] - (mount/start #'pinkblackrose.config/env) - (cond - (nil? (:database-url env)) - (do - (log/error "Database configuration not found, :database-url environment variable must be set before running") - (System/exit 1)) - (some #{"init"} args) - (do - (migrations/init (select-keys env [:database-url :init-script])) - (System/exit 0)) - (migrations/migration? args) - (do - (migrations/migrate args (select-keys env [:database-url])) - (System/exit 0)) - :else - (start-app args))) - diff --git a/src/clj/pinkblackrose/db/core.clj b/src/clj/pinkblackrose/db/core.clj deleted file mode 100644 index 2ccfc60..0000000 --- a/src/clj/pinkblackrose/db/core.clj +++ /dev/null @@ -1,86 +0,0 @@ -(ns pinkblackrose.db.core - (:require - [cheshire.core :refer [generate-string parse-string]] - [clojure.java.jdbc :as jdbc] - [clojure.tools.logging :as log] - [conman.core :as conman] - [java-time :as jt] - [java-time.pre-java8] - [pinkblackrose.config :refer [env]] - [mount.core :refer [defstate]]) - (:import org.postgresql.util.PGobject - java.sql.Array - clojure.lang.IPersistentMap - clojure.lang.IPersistentVector - [java.sql - BatchUpdateException - PreparedStatement])) -(defstate ^:dynamic *db* - :start (if-let [jdbc-url (env :database-url)] - (conman/connect! {:jdbc-url jdbc-url}) - (do - (log/warn "database connection URL was not found, please set :database-url in your config, e.g: dev-config.edn") - *db*)) - :stop (conman/disconnect! *db*)) - -(conman/bind-connection *db* "sql/queries.sql") - - -(extend-protocol jdbc/IResultSetReadColumn - java.sql.Timestamp - (result-set-read-column [v _2 _3] - (.toLocalDateTime v)) - java.sql.Date - (result-set-read-column [v _2 _3] - (.toLocalDate v)) - java.sql.Time - (result-set-read-column [v _2 _3] - (.toLocalTime v)) - Array - (result-set-read-column [v _ _] (vec (.getArray v))) - PGobject - (result-set-read-column [pgobj _metadata _index] - (let [type (.getType pgobj) - value (.getValue pgobj)] - (case type - "json" (parse-string value true) - "jsonb" (parse-string value true) - "citext" (str value) - value)))) - -(defn to-pg-json [value] - (doto (PGobject.) - (.setType "jsonb") - (.setValue (generate-string value)))) - -(extend-type clojure.lang.IPersistentVector - jdbc/ISQLParameter - (set-parameter [v ^java.sql.PreparedStatement stmt ^long idx] - (let [conn (.getConnection stmt) - meta (.getParameterMetaData stmt) - type-name (.getParameterTypeName meta idx)] - (if-let [elem-type (when (= (first type-name) \_) (apply str (rest type-name)))] - (.setObject stmt idx (.createArrayOf conn elem-type (to-array v))) - (.setObject stmt idx (to-pg-json v)))))) - -(extend-protocol jdbc/ISQLValue - java.util.Date - (sql-value [v] - (java.sql.Timestamp. (.getTime v))) - java.time.LocalTime - (sql-value [v] - (jt/sql-time v)) - java.time.LocalDate - (sql-value [v] - (jt/sql-date v)) - java.time.LocalDateTime - (sql-value [v] - (jt/sql-timestamp v)) - java.time.ZonedDateTime - (sql-value [v] - (jt/sql-timestamp v)) - IPersistentMap - (sql-value [value] (to-pg-json value)) - IPersistentVector - (sql-value [value] (to-pg-json value))) - diff --git a/src/clj/pinkblackrose/handler.clj b/src/clj/pinkblackrose/handler.clj deleted file mode 100644 index 1b211e1..0000000 --- a/src/clj/pinkblackrose/handler.clj +++ /dev/null @@ -1,42 +0,0 @@ -(ns pinkblackrose.handler - (:require - [pinkblackrose.middleware :as middleware] - [pinkblackrose.layout :refer [error-page]] - [pinkblackrose.routes.home :refer [home-routes]] - [pinkblackrose.routes.services :refer [service-routes]] - [reitit.swagger-ui :as swagger-ui] - [reitit.ring :as ring] - [ring.middleware.content-type :refer [wrap-content-type]] - [ring.middleware.webjars :refer [wrap-webjars]] - [pinkblackrose.env :refer [defaults]] - [mount.core :as mount])) - -(mount/defstate init-app - :start ((or (:init defaults) (fn []))) - :stop ((or (:stop defaults) (fn [])))) - -(mount/defstate app-routes - :start - (ring/ring-handler - (ring/router - [(home-routes) - (service-routes)]) - (ring/routes - (swagger-ui/create-swagger-ui-handler - {:path "/swagger-ui" - :url "/api/swagger.json" - :config {:validator-url nil}}) - (ring/create-resource-handler - {:path "/"}) - (wrap-content-type - (wrap-webjars (constantly nil))) - (ring/create-default-handler - {:not-found - (constantly (error-page {:status 404, :title "404 - Page not found"})) - :method-not-allowed - (constantly (error-page {:status 405, :title "405 - Not allowed"})) - :not-acceptable - (constantly (error-page {:status 406, :title "406 - Not acceptable"}))})))) - -(defn app [] - (middleware/wrap-base #'app-routes)) diff --git a/src/clj/pinkblackrose/layout.clj b/src/clj/pinkblackrose/layout.clj deleted file mode 100644 index 8fcc2e9..0000000 --- a/src/clj/pinkblackrose/layout.clj +++ /dev/null @@ -1,39 +0,0 @@ -(ns pinkblackrose.layout - (:require - [clojure.java.io] - [selmer.parser :as parser] - [selmer.filters :as filters] - [markdown.core :refer [md-to-html-string]] - [ring.util.http-response :refer [content-type ok]] - [ring.util.anti-forgery :refer [anti-forgery-field]] - [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] - [ring.util.response])) - -(parser/set-resource-path! (clojure.java.io/resource "html")) -(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) -(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) - -(defn render - "renders the HTML template located relative to resources/html" - [request template & [params]] - (content-type - (ok - (parser/render-file - template - (assoc params - :page template - :csrf-token *anti-forgery-token*))) - "text/html; charset=utf-8")) - -(defn error-page - "error-details should be a map containing the following keys: - :status - error status - :title - error title (optional) - :message - detailed error message (optional) - - returns a response map with the error page as the body - and the status specified by the status key" - [error-details] - {:status (:status error-details) - :headers {"Content-Type" "text/html; charset=utf-8"} - :body (parser/render-file "error.html" error-details)}) diff --git a/src/clj/pinkblackrose/middleware.clj b/src/clj/pinkblackrose/middleware.clj deleted file mode 100644 index 22930dd..0000000 --- a/src/clj/pinkblackrose/middleware.clj +++ /dev/null @@ -1,87 +0,0 @@ -(ns pinkblackrose.middleware - (:require - [pinkblackrose.env :refer [defaults]] - [cheshire.generate :as cheshire] - [cognitect.transit :as transit] - [clojure.tools.logging :as log] - [pinkblackrose.layout :refer [error-page]] - [ring.middleware.anti-forgery :refer [wrap-anti-forgery]] - [pinkblackrose.middleware.formats :as formats] - [muuntaja.middleware :refer [wrap-format wrap-params]] - [pinkblackrose.config :refer [env]] - [ring-ttl-session.core :refer [ttl-memory-store]] - [ring.middleware.defaults :refer [site-defaults wrap-defaults]] - [buddy.auth.middleware :refer [wrap-authentication wrap-authorization]] - [buddy.auth.accessrules :refer [restrict]] - [buddy.auth :refer [authenticated?]] - [buddy.auth.backends.token :refer [jwe-backend]] - [buddy.sign.jwt :refer [encrypt]] - [buddy.core.nonce :refer [random-bytes]][buddy.sign.util :refer [to-timestamp]]) - - ) - -(defn wrap-internal-error [handler] - (fn [req] - (try - (handler req) - (catch Throwable t - (log/error t (.getMessage t)) - (error-page {:status 500 - :title "Something very bad has happened!" - :message "We've dispatched a team of highly trained gnomes to take care of the problem."}))))) - -(defn wrap-csrf [handler] - (wrap-anti-forgery - handler - {:error-response - (error-page - {:status 403 - :title "Invalid anti-forgery token"})})) - - -(defn wrap-formats [handler] - (let [wrapped (-> handler wrap-params (wrap-format formats/instance))] - (fn [request] - ;; disable wrap-formats for websockets - ;; since they're not compatible with this middleware - ((if (:websocket? request) handler wrapped) request)))) - -(defn on-error [request response] - (error-page - {:status 403 - :title (str "Access to " (:uri request) " is not authorized")})) - -(defn wrap-restricted [handler] - (restrict handler {:handler authenticated? - :on-error on-error})) - -(def secret (random-bytes 32)) - -(def token-backend - (jwe-backend {:secret secret - :options {:alg :a256kw - :enc :a128gcm}})) - -(defn token [username] - (let [claims {:user (keyword username) - :exp (to-timestamp - (.getTime - (doto (Calendar/getInstance) - (.setTime (Date.)) - (.add Calendar/HOUR_OF_DAY 1))))}] - (encrypt claims secret {:alg :a256kw :enc :a128gcm}))) - -(defn wrap-auth [handler] - (let [backend token-backend] - (-> handler - (wrap-authentication backend) - (wrap-authorization backend)))) - -(defn wrap-base [handler] - (-> ((:middleware defaults) handler) - wrap-auth - (wrap-defaults - (-> site-defaults - (assoc-in [:security :anti-forgery] false) - (assoc-in [:session :store] (ttl-memory-store (* 60 30))))) - wrap-internal-error)) diff --git a/src/clj/pinkblackrose/middleware/exception.clj b/src/clj/pinkblackrose/middleware/exception.clj deleted file mode 100644 index aaf228d..0000000 --- a/src/clj/pinkblackrose/middleware/exception.clj +++ /dev/null @@ -1,24 +0,0 @@ -(ns pinkblackrose.middleware.exception - (:require [clojure.tools.logging :as log] - [expound.alpha :as expound] - [reitit.coercion :as coercion] - [reitit.ring.middleware.exception :as exception])) - -(defn coercion-error-handler [status] - (let [printer (expound/custom-printer {:print-specs? false})] - (fn [exception request] - {:status status - :headers {"Content-Type" "text/html"} - :body (with-out-str (printer (-> exception ex-data :problems)))}))) - -(def exception-middleware - (exception/create-exception-middleware - (merge - exception/default-handlers - {;; log stack-traces for all exceptions - ::exception/wrap (fn [handler e request] - (log/error e (.getMessage e)) - (handler e request)) - ;; human-optimized validation messages - ::coercion/request-coercion (coercion-error-handler 400) - ::coercion/response-coercion (coercion-error-handler 500)}))) diff --git a/src/clj/pinkblackrose/middleware/formats.clj b/src/clj/pinkblackrose/middleware/formats.clj deleted file mode 100644 index 6f6b706..0000000 --- a/src/clj/pinkblackrose/middleware/formats.clj +++ /dev/null @@ -1,15 +0,0 @@ -(ns pinkblackrose.middleware.formats - (:require - [cognitect.transit :as transit] - [luminus-transit.time :as time] - [muuntaja.core :as m])) - -(def instance - (m/create - (-> m/default-options - (update-in - [:formats "application/transit+json" :decoder-opts] - (partial merge time/time-deserialization-handlers)) - (update-in - [:formats "application/transit+json" :encoder-opts] - (partial merge time/time-serialization-handlers))))) diff --git a/src/clj/pinkblackrose/nrepl.clj b/src/clj/pinkblackrose/nrepl.clj deleted file mode 100644 index cf01245..0000000 --- a/src/clj/pinkblackrose/nrepl.clj +++ /dev/null @@ -1,27 +0,0 @@ -(ns pinkblackrose.nrepl - (:require - [nrepl.server :as nrepl] - [clojure.tools.logging :as log])) - -(defn start - "Start a network repl for debugging on specified port followed by - an optional parameters map. The :bind, :transport-fn, :handler, - :ack-port and :greeting-fn will be forwarded to - clojure.tools.nrepl.server/start-server as they are." - [{:keys [port bind transport-fn handler ack-port greeting-fn]}] - (try - (log/info "starting nREPL server on port" port) - (nrepl/start-server :port port - :bind bind - :transport-fn transport-fn - :handler handler - :ack-port ack-port - :greeting-fn greeting-fn) - - (catch Throwable t - (log/error t "failed to start nREPL") - (throw t)))) - -(defn stop [server] - (nrepl/stop-server server) - (log/info "nREPL server stopped")) diff --git a/src/clj/pinkblackrose/routes/home.clj b/src/clj/pinkblackrose/routes/home.clj deleted file mode 100644 index c47aedc..0000000 --- a/src/clj/pinkblackrose/routes/home.clj +++ /dev/null @@ -1,22 +0,0 @@ -(ns pinkblackrose.routes.home - (:require - [pinkblackrose.layout :as layout] - [pinkblackrose.db.core :as db] - [clojure.java.io :as io] - [pinkblackrose.middleware :as middleware] - [ring.util.response] - [ring.util.http-response :as response])) - -(defn home-page [request] - (layout/render request "home.html")) - -(defn home-routes [] - ["" - {:middleware [middleware/wrap-csrf - middleware/wrap-formats]} - ["/" {:get home-page}] - ["/graphiql" {:get (fn [request] (layout/render request "graphiql.html"))}] - ["/docs" {:get (fn [_] - (-> (response/ok (-> "docs/docs.md" io/resource slurp)) - (response/header "Content-Type" "text/plain; charset=utf-8")))}]]) - diff --git a/src/clj/pinkblackrose/routes/services.clj b/src/clj/pinkblackrose/routes/services.clj deleted file mode 100644 index 0e3b968..0000000 --- a/src/clj/pinkblackrose/routes/services.clj +++ /dev/null @@ -1,93 +0,0 @@ -(ns pinkblackrose.routes.services - (:require - [reitit.swagger :as swagger] - [reitit.swagger-ui :as swagger-ui] - [reitit.ring.coercion :as coercion] - [reitit.coercion.spec :as spec-coercion] - [reitit.ring.middleware.muuntaja :as muuntaja] - [reitit.ring.middleware.multipart :as multipart] - [reitit.ring.middleware.parameters :as parameters] - [pinkblackrose.routes.services.graphql :as graphql] - [pinkblackrose.middleware.formats :as formats] - [pinkblackrose.middleware.exception :as exception] - [ring.util.http-response :refer :all] - [clojure.java.io :as io])) - -(defn service-routes [] - ["/api" - {:coercion spec-coercion/coercion - :muuntaja formats/instance - :swagger {:id ::api} - :middleware [;; query-params & form-params - parameters/parameters-middleware - ;; content-negotiation - muuntaja/format-negotiate-middleware - ;; encoding response body - muuntaja/format-response-middleware - ;; exception handling - exception/exception-middleware - ;; decoding request body - muuntaja/format-request-middleware - ;; coercing response bodys - coercion/coerce-response-middleware - ;; coercing request parameters - coercion/coerce-request-middleware - ;; multipart - multipart/multipart-middleware]} - - ;; swagger documentation - ["" {:no-doc true - :swagger {:info {:title "my-api" - :description "https://cljdoc.org/d/metosin/reitit"}}} - - ["/swagger.json" - {:get (swagger/create-swagger-handler)}] - - ["/api-docs/*" - {:get (swagger-ui/create-swagger-ui-handler - {:url "/api/swagger.json" - :config {:validator-url nil}})}]] - - ["/ping" - {:get (constantly (ok {:message "pong"}))}] - - ["/graphql" {:post (fn [req] (ok (graphql/execute-request (-> req :body slurp))))}] - - ["/math" - {:swagger {:tags ["math"]}} - - ["/plus" - {:get {:summary "plus with spec query parameters" - :parameters {:query {:x int?, :y int?}} - :responses {200 {:body {:total pos-int?}}} - :handler (fn [{{{:keys [x y]} :query} :parameters}] - {:status 200 - :body {:total (+ x y)}})} - :post {:summary "plus with spec body parameters" - :parameters {:body {:x int?, :y int?}} - :responses {200 {:body {:total pos-int?}}} - :handler (fn [{{{:keys [x y]} :body} :parameters}] - {:status 200 - :body {:total (+ x y)}})}}]] - - ["/files" - {:swagger {:tags ["files"]}} - - ["/upload" - {:post {:summary "upload a file" - :parameters {:multipart {:file multipart/temp-file-part}} - :responses {200 {:body {:name string?, :size int?}}} - :handler (fn [{{{:keys [file]} :multipart} :parameters}] - {:status 200 - :body {:name (:filename file) - :size (:size file)}})}}] - - ["/download" - {:get {:summary "downloads a file" - :swagger {:produces ["image/png"]} - :handler (fn [_] - {:status 200 - :headers {"Content-Type" "image/png"} - :body (-> "public/img/warning_clojure.png" - (io/resource) - (io/input-stream))})}}]]]) diff --git a/src/clj/pinkblackrose/routes/services/graphql.clj b/src/clj/pinkblackrose/routes/services/graphql.clj deleted file mode 100644 index 251a359..0000000 --- a/src/clj/pinkblackrose/routes/services/graphql.clj +++ /dev/null @@ -1,41 +0,0 @@ -(ns pinkblackrose.routes.services.graphql - (:require - [com.walmartlabs.lacinia.util :refer [attach-resolvers]] - [com.walmartlabs.lacinia.schema :as schema] - [com.walmartlabs.lacinia :as lacinia] - [clojure.data.json :as json] - [clojure.edn :as edn] - [clojure.java.io :as io] - [ring.util.http-response :refer :all] - [mount.core :refer [defstate]])) - -(defn get-hero [context args value] - (let [data [{:id 1000 - :name "Luke" - :home_planet "Tatooine" - :appears_in ["NEWHOPE" "EMPIRE" "JEDI"]} - {:id 2000 - :name "Lando Calrissian" - :home_planet "Socorro" - :appears_in ["EMPIRE" "JEDI"]}]] - (first data))) - -(defstate compiled-schema - :start - (-> "graphql/schema.edn" - io/resource - slurp - edn/read-string - (attach-resolvers {:get-hero get-hero - :get-droid (constantly {})}) - schema/compile)) - -(defn format-params [query] - (let [parsed (json/read-str query)] ;;-> placeholder - need to ensure query meets graphql syntax - (str "query { hero(id: \"1000\") { name appears_in }}"))) - -(defn execute-request [query] - (let [vars nil - context nil] - (-> (lacinia/execute compiled-schema query vars context) - (json/write-str)))) diff --git a/src/cljc/pinkblackrose/validation.cljc b/src/cljc/pinkblackrose/validation.cljc deleted file mode 100644 index fa03f5d..0000000 --- a/src/cljc/pinkblackrose/validation.cljc +++ /dev/null @@ -1,2 +0,0 @@ -(ns pinkblackrose.validation - (:require [struct.core :as st])) diff --git a/src/cljs/pinkblackrose/ajax.cljs b/src/cljs/pinkblackrose/ajax.cljs deleted file mode 100644 index ab8d2a8..0000000 --- a/src/cljs/pinkblackrose/ajax.cljs +++ /dev/null @@ -1,30 +0,0 @@ -(ns pinkblackrose.ajax - (:require - [ajax.core :as ajax] - [luminus-transit.time :as time] - [cognitect.transit :as transit] - [re-frame.core :as rf])) - -(defn local-uri? [{:keys [uri]}] - (not (re-find #"^\w+?://" uri))) - -(defn default-headers [request] - (if (local-uri? request) - (-> request - (update :headers #(merge {"x-csrf-token" js/csrfToken} %))) - request)) - -;; injects transit serialization config into request options -(defn as-transit [opts] - (merge {:raw false - :format :transit - :response-format :transit - :reader (transit/reader :json time/time-deserialization-handlers) - :writer (transit/writer :json time/time-serialization-handlers)} - opts)) - -(defn load-interceptors! [] - (swap! ajax/default-interceptors - conj - (ajax/to-interceptor {:name "default headers" - :request default-headers}))) diff --git a/src/cljs/pinkblackrose/core.cljs b/src/cljs/pinkblackrose/core.cljs deleted file mode 100644 index 1cf46df..0000000 --- a/src/cljs/pinkblackrose/core.cljs +++ /dev/null @@ -1,79 +0,0 @@ -(ns pinkblackrose.core - (:require - [day8.re-frame.http-fx] - [reagent.core :as r] - [re-frame.core :as rf] - [goog.events :as events] - [goog.history.EventType :as HistoryEventType] - [markdown.core :refer [md->html]] - [pinkblackrose.ajax :as ajax] - [pinkblackrose.events] - [reitit.core :as reitit] - [reitit.frontend.easy :as rfe] - [clojure.string :as string]) - (:import goog.History)) - -(defn nav-link [uri title page] - [:a.navbar-item - {:href uri - :class (when (= page @(rf/subscribe [:page])) :is-active)} - title]) - -(defn navbar [] - (r/with-let [expanded? (r/atom false)] - [:nav.navbar.is-info>div.container - [:div.navbar-brand - [:a.navbar-item {:href "/" :style {:font-weight :bold}} "pinkblackrose"] - [:span.navbar-burger.burger - {:data-target :nav-menu - :on-click #(swap! expanded? not) - :class (when @expanded? :is-active)} - [:span][:span][:span]]] - [:div#nav-menu.navbar-menu - {:class (when @expanded? :is-active)} - [:div.navbar-start - [nav-link "#/" "Home" :home] - [nav-link "#/about" "About" :about]]]])) - -(defn about-page [] - [:section.section>div.container>div.content - [:img {:src "/img/warning_clojure.png"}]]) - -(defn home-page [] - [:section.section>div.container>div.content - (when-let [docs @(rf/subscribe [:docs])] - [:div {:dangerouslySetInnerHTML {:__html (md->html docs)}}])]) - -(defn page [] - (if-let [page @(rf/subscribe [:page])] - [:div - [navbar] - [page]])) - -(defn navigate! [match _] - (rf/dispatch [:navigate match])) - -(def router - (reitit/router - [["/" {:name :home - :view #'home-page - :controllers [{:start (fn [_] (rf/dispatch [:page/init-home]))}]}] - ["/about" {:name :about - :view #'about-page}]])) - -(defn start-router! [] - (rfe/start! - router - navigate! - {})) - -;; ------------------------- -;; Initialize app -(defn ^:dev/after-load mount-components [] - (rf/clear-subscription-cache!) - (r/render [#'page] (.getElementById js/document "app"))) - -(defn init! [] - (start-router!) - (ajax/load-interceptors!) - (mount-components)) diff --git a/src/cljs/pinkblackrose/events.cljs b/src/cljs/pinkblackrose/events.cljs deleted file mode 100644 index 4f8aed1..0000000 --- a/src/cljs/pinkblackrose/events.cljs +++ /dev/null @@ -1,78 +0,0 @@ -(ns pinkblackrose.events - (:require - [re-frame.core :as rf] - [ajax.core :as ajax] - [reitit.frontend.easy :as rfe] - [reitit.frontend.controllers :as rfc])) - -;;dispatchers - -(rf/reg-event-db - :navigate - (fn [db [_ match]] - (let [old-match (:common/route db) - new-match (assoc match :controllers - (rfc/apply-controllers (:controllers old-match) match))] - (assoc db :route new-match)))) - -(rf/reg-fx - :navigate-fx! - (fn [[k & [params query]]] - (rfe/push-state k params query))) - -(rf/reg-event-fx - :navigate! - (fn [_ [_ url-key params query]] - {:navigate-fx! [url-key params query]})) - -(rf/reg-event-db - :set-docs - (fn [db [_ docs]] - (assoc db :docs docs))) - -(rf/reg-event-fx - :fetch-docs - (fn [_ _] - {:http-xhrio {:method :get - :uri "/docs" - :response-format (ajax/raw-response-format) - :on-success [:set-docs]}})) - -(rf/reg-event-db - :common/set-error - (fn [db [_ error]] - (assoc db :common/error error))) - -(rf/reg-event-fx - :page/init-home - (fn [_ _] - {:dispatch [:fetch-docs]})) - -;;subscriptions - -(rf/reg-sub - :route - (fn [db _] - (-> db :route))) - -(rf/reg-sub - :page-id - :<- [:route] - (fn [route _] - (-> route :data :name))) - -(rf/reg-sub - :page - :<- [:route] - (fn [route _] - (-> route :data :view))) - -(rf/reg-sub - :docs - (fn [db _] - (:docs db))) - -(rf/reg-sub - :common/error - (fn [db _] - (:common/error db))) diff --git a/env/dev/resources/config.edn b/src/duct_hierarchy.edn similarity index 100% rename from env/dev/resources/config.edn rename to src/duct_hierarchy.edn diff --git a/src/pinkblackrose/client.cljs b/src/pinkblackrose/client.cljs new file mode 100644 index 0000000..5ae3b68 --- /dev/null +++ b/src/pinkblackrose/client.cljs @@ -0,0 +1,3 @@ +(ns pinkblackrose.client) + +(js/console.log "FIXME") diff --git a/src/pinkblackrose/main.clj b/src/pinkblackrose/main.clj new file mode 100644 index 0000000..531d12e --- /dev/null +++ b/src/pinkblackrose/main.clj @@ -0,0 +1,12 @@ +(ns pinkblackrose.main + (:gen-class) + (:require [duct.core :as duct])) + +(duct/load-hierarchy) + +(defn -main [& args] + (let [keys (or (duct/parse-keys args) [:duct/daemon]) + profiles [:duct.profile/prod]] + (-> (duct/resource "pinkblackrose/config.edn") + (duct/read-config) + (duct/exec-config profiles keys)))) diff --git a/test/clj/pinkblackrose/test/db/core.clj b/test/clj/pinkblackrose/test/db/core.clj deleted file mode 100644 index e6fa2e8..0000000 --- a/test/clj/pinkblackrose/test/db/core.clj +++ /dev/null @@ -1,38 +0,0 @@ -(ns pinkblackrose.test.db.core - (:require - [pinkblackrose.db.core :refer [*db*] :as db] - [java-time.pre-java8] - [luminus-migrations.core :as migrations] - [clojure.test :refer :all] - [clojure.java.jdbc :as jdbc] - [pinkblackrose.config :refer [env]] - [mount.core :as mount])) - -(use-fixtures - :once - (fn [f] - (mount/start - #'pinkblackrose.config/env - #'pinkblackrose.db.core/*db*) - (migrations/migrate ["migrate"] (select-keys env [:database-url])) - (f))) - -(deftest test-users - (jdbc/with-db-transaction [t-conn *db*] - (jdbc/db-set-rollback-only! t-conn) - (is (= 1 (db/create-user! - t-conn - {:id "1" - :first_name "Sam" - :last_name "Smith" - :email "sam.smith@example.com" - :pass "pass"}))) - (is (= {:id "1" - :first_name "Sam" - :last_name "Smith" - :email "sam.smith@example.com" - :pass "pass" - :admin nil - :last_login nil - :is_active nil} - (db/get-user t-conn {:id "1"}))))) diff --git a/test/clj/pinkblackrose/test/handler.clj b/test/clj/pinkblackrose/test/handler.clj deleted file mode 100644 index bdf0bb4..0000000 --- a/test/clj/pinkblackrose/test/handler.clj +++ /dev/null @@ -1,52 +0,0 @@ -(ns pinkblackrose.test.handler - (:require - [clojure.test :refer :all] - [ring.mock.request :refer :all] - [pinkblackrose.handler :refer :all] - [pinkblackrose.middleware.formats :as formats] - [muuntaja.core :as m] - [mount.core :as mount])) - -(defn parse-json [body] - (m/decode formats/instance "application/json" body)) - -(use-fixtures - :once - (fn [f] - (mount/start #'pinkblackrose.config/env - #'pinkblackrose.handler/app-routes) - (f))) - -(deftest test-app - (testing "main route" - (let [response ((app) (request :get "/"))] - (is (= 200 (:status response))))) - - (testing "not-found route" - (let [response ((app) (request :get "/invalid"))] - (is (= 404 (:status response))))) - (testing "services" - - (testing "success" - (let [response ((app) (-> (request :post "/api/math/plus") - (json-body {:x 10, :y 6})))] - (is (= 200 (:status response))) - (is (= {:total 16} (m/decode-response-body response))))) - - (testing "parameter coercion error" - (let [response ((app) (-> (request :post "/api/math/plus") - (json-body {:x 10, :y "invalid"})))] - (is (= 400 (:status response))))) - - (testing "response coercion error" - (let [response ((app) (-> (request :post "/api/math/plus") - (json-body {:x -10, :y 6})))] - (is (= 500 (:status response))))) - - (testing "content negotiation" - (let [response ((app) (-> (request :post "/api/math/plus") - (body (pr-str {:x 10, :y 6})) - (content-type "application/edn") - (header "accept" "application/transit+json")))] - (is (= 200 (:status response))) - (is (= {:total 16} (m/decode-response-body response))))))) diff --git a/test/cljs/pinkblackrose/core_test.cljs b/test/cljs/pinkblackrose/core_test.cljs deleted file mode 100644 index c3b4aa6..0000000 --- a/test/cljs/pinkblackrose/core_test.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns pinkblackrose.core-test - (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]] - [pjstadig.humane-test-output] - [reagent.core :as reagent :refer [atom]] - [pinkblackrose.core :as rc])) - -(deftest test-home - (is (= true true))) -