Initial commit

main
Eric Ihli 4 years ago
commit 86681ec999

@ -0,0 +1,3 @@
((nil . ((cider-ns-refresh-before-fn . "integrant.repl/halt")
(cider-ns-refresh-after-fn . "integrant.repl/init")
(cider-clojure-cli-global-options . "-A:dev:cider-nrepl"))))

3
.gitignore vendored

@ -0,0 +1,3 @@
/db/data/dev
.nrepl-port
.cpcache

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
# Run once as part of Docker build script to initialize
# the database with encryption for user passwords.
POSTGRES_USER=${POSTGRES_USER:-"dev"}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-"dev"}
psql -v ON_ERROR_STOP=1 \
--username "$POSTGRES_USER" \
--dbname "$POSTGRES_DB" <<-EOSQL
SET password_encryption = "scram-sha-256";
ALTER USER "$POSTGRES_USER" WITH ENCRYPTED PASSWORD '$POSTGRES_PASSWORD';
EOSQL

@ -0,0 +1,6 @@
FROM postgres
# See the section on "Initialization scripts" at
# https://hub.docker.com/_/postgres/
COPY 0001_init.sh /docker-entrypoint-initdb.d/

@ -0,0 +1,48 @@
#+TITLE: Database setup
This directory contains necessities for initializing and running a database
locally inside a docker container.
We want our develpment environment to use the same tech stack that production
uses. So rather than suppord a development configuration that uses SQLite and a
production configuration that uses PostgreSQL, just use PostgreSQL for both.
With Docker, it's easy.
Building requires the following environment variables.
- `POSTGRES_DB`
- `POSTGRES_USER`
- `POSTGRES_PASSWORD`
Running requires the following environment variables.
- `PGDATA`
- `POSTGRES_USER`
- `POSTGRES_PASSWORD`
#+BEGIN_SRC sh
docker build -t db .
#+END_SRC
#+BEGIN_SRC sh :tangle run.sh :tangle-mode (identity #o755)
#!/usr/bin/env bash
set -euo pipefail
PGDATA=${PGDATA:-"$(pwd)/data/dev"}
POSTGRES_USER=${POSTGRES_USER:-"dev"}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-"dev"}
docker run \
--name db \
-e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
-e POSTGRES_USER=$POSTGRES_USER \
-v $PGDATA:/var/lib/postgresql/data \
-p 5432:5432 \
db
#+END_SRC
#+BEGIN_SRC sh
docker start db
#+END_SRC

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
PGDATA=${PGDATA:-"$(pwd)/data/dev"}
POSTGRES_USER=${POSTGRES_USER:-"dev"}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-"dev"}
docker run \
--name darklimericks-db \
-e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
-e POSTGRES_USER=$POSTGRES_USER \
-v $PGDATA:/var/lib/postgresql/data \
-p 5432:5432 \
darklimericks-db

@ -0,0 +1,27 @@
{:deps
{org.clojure/tools.namespace {:mvn/version "1.0.0"}
org.clojure/tools.deps.alpha
{:git/url "https://github.com/clojure/tools.deps.alpha.git"
:sha "d492e97259c013ba401c5238842cd3445839d020"}
hiccup {:mvn/version "1.0.5"}
com.taoensso/timbre {:mvn/version "5.1.0"}
http-kit {:mvn/version "2.5.0"}
integrant {:mvn/version "0.8.0"}
integrant/repl {:mvn/version "0.3.2"}
seancorfield/next.jdbc {:mvn/version "1.1.610"}
org.xerial/sqlite-jdbc {:mvn/version "3.32.3.2"}
honeysql {:mvn/version "1.0.444"}
metosin/muuntaja {:mvn/version "0.6.7"}
metosin/reitit {:mvn/version "0.5.10"}
metosin/reitit-http {:mvn/version "0.5.10"}
metosin/reitit-interceptors {:mvn/version "0.5.10"}
com.layerware/hugsql {:mvn/version "0.5.1"}
org.postgresql/postgresql {:mvn/version "42.2.18"}
migratus {:mvn/version "1.3.2"}
ring/ring-core {:mvn/version "1.8.2"}
environ {:mvn/version "1.2.0"}
prhyme {:local/root "/home/eihli/code/prhyme/prhyme.jar"}}
:paths ["src" "resources"]
:aliases {:dev {:extra-paths ["dev"]
:extra-deps {hawk {:mvn/version "0.2.11"}
ring/ring-devel {:mvn/version "1.8.2"}}}}}

@ -0,0 +1,28 @@
(ns db
(:require [next.jdbc.sql :as jdbc.sql]
[next.jdbc :as jdbc]
[honeysql.core :as honey.sql]
[integrant.repl.state :refer [system]]
[com.darklimericks.db.artists :as artists]
[com.darklimericks.db.albums :as albums]
[com.darklimericks.server.limericks :as server.limericks]
[com.darklimericks.db.limericks :as limericks]))
(def conn (:database.sql/connection system))
(comment
(->> (honey.sql/build :select :* :from :artist)
(honey.sql/format)
(jdbc.sql/query conn))
(artists/insert-artist conn "Optimus Prhyme")
(server.limericks/get-artist-and-album-for-new-limerick conn)
(artists/artist conn 1)
(albums/artist-albums-sql 1)
(albums/artist-albums conn 1)
(albums/insert-album conn "Limericks Illicit" 1)
(albums/insert-album-sql "Limericks Illicit" 1)
(artists/artists-by-letter conn "O")
(limericks/album-limericks-sql 1)
(limericks/album-limericks conn 1)
(albums/artist-most-resent-album conn 1)
)

@ -0,0 +1,13 @@
(ns migrations
(:require [migratus.core :as migratus]
[integrant.repl.state :as state]))
(def config {:store :database
:migration-dir "migrations/"
:init-script "init.sql"
:db {:connection-uri (-> state/config :database.sql/connection :jdbcUrl)}})
(comment
state/config
(:database.sql/connection state/system)
(migratus/init config))

@ -0,0 +1,89 @@
(ns user
(:require [clojure.tools.deps.alpha.repl :refer [add-lib]]
[clojure.tools.namespace.repl :refer [set-refresh-dirs]]
[integrant.repl :as repl]
[integrant.repl.state :as state]
[migratus.core :as migratus]
[com.darklimericks.server.handlers :as handlers]
[integrant.core :as ig]
[hawk.core :as hawk]
[clojure.java.io :as io]
[com.darklimericks.server.limericks :as limericks]
[com.darklimericks.server.util :as util]
[com.owoga.prhyme.limerick :as limerick]
[com.darklimericks.server.system]
[reitit.core :as reitit]))
(comment
(-> state/system
:app/router
(reitit/routes))
)
(set-refresh-dirs "src" "dev" "resources")
(defn add-project-dep
([lib-name lib-version]
(let [dep-name (symbol lib-name)
dep-version (name lib-version)]
(add-lib dep-name {:mvn/version dep-version}))))
(defn- clojure-file? [_ {:keys [file]}]
(re-matches #"[^.].*(\.clj|\.edn)$" (.getName file)))
(defn- auto-reset-handler [ctx event]
(binding [*ns* *ns*]
(integrant.repl/reset)
ctx))
(defn auto-reset []
(hawk/watch! [{:paths ["src/" "resources/" "dev/"]
:filter clojure-file?
:handler auto-reset-handler}]))
(defn init []
(repl/halt)
(-> "server/config.edn"
io/resource
slurp
ig/read-string
ig/prep
constantly
repl/set-prep!)
(repl/prep)
(repl/init))
(defn reset []
(repl/halt))
(comment
(init)
(auto-reset)
(limericks/get-artist-and-album-for-new-limerick (-> state/system :database.sql/connection))
(let [handler (handlers/limerick-generation-post-handler
(-> state/system :database.sql/connection)
(-> state/system :app/cache))]
(handler {:params {:scheme "A9 A9 B5 B5 A9" #_'((A 9) (A 9) (B 5) (B 5) (A 9))}}))
(reitit/match-by-path
(-> state/system :app/router)
"/limericks/1/1")
(let [router (-> state/system :app/router)]
(util/route-name->path {::reitit/router router}
:com.darklimericks.server.system/artist))
(let [router (-> state/system :app/router)]
(util/route-name->path
{::reitit/router router}
:com.darklimericks.server.system/artist
{:artist-id 1}))
(let [router (-> state/system :app/router)]
(->> :com.darklimericks.server.system/artist
(#(reitit/match-by-name (::reitit/router {::reitit/router router})
%
{:artist-id 1}))
(reitit/match->path)))
)

@ -0,0 +1,3 @@
DROP TABLE artist;
DROP TABLE album;
DROP TABLE limerick;

@ -0,0 +1,17 @@
CREATE TABLE artist (
id SERIAL PRIMARY KEY,
name VARCHAR(64)
);
CREATE TABLE album (
id SERIAL PRIMARY KEY,
name VARCHAR(64),
artist_id INTEGER NOT NULL REFERENCES artist
);
CREATE TABLE limerick (
id SERIAL PRIMARY KEY,
name VARCHAR(64),
text VARCHAR(1024),
album_id INTEGER NOT NULL REFERENCES album
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,25 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>DarkLimericks</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- Place favicon.ico in the root directory -->
</head>
<body>
<!--[if lt IE 8]>
<p class="browserupgrade">
You are using an <strong>outdated</strong> browser. Please
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
your experience.
</p>
<![endif]-->
Hello World!
</body>
</html>

@ -0,0 +1 @@
/home/eihli/src/tachyons/css/tachyons.css

@ -0,0 +1 @@
/home/eihli/src/tachyons/css/tachyons.min.css

@ -0,0 +1,6 @@
{:app/logging {:min-level :debug}
:app/server {:port 8000 :handler #ig/ref :app/handler}
:app/cache {}
:database.sql/connection {}
:app/router {:db #ig/ref :database.sql/connection :cache #ig/ref :app/cache}
:app/handler {:router #ig/ref :app/router}}

@ -0,0 +1,43 @@
(ns com.darklimericks.db.albums
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as jdbc.sql]
[honeysql.core :as honey.sql]
[honeysql.helpers :as honey.helpers]))
(defn insert-album-sql [name artist-id]
(let [[s & params] (-> (honey.helpers/insert-into :album)
(honey.helpers/columns :name :artist-id)
(honey.helpers/values
[[name artist-id]])
honey.sql/format)
s (format "%s RETURNING id" s)]
(concat [s] params)))
(defn insert-album [db name artist-id]
(jdbc/execute! db (insert-album-sql name artist-id)))
(defn artist-albums-sql [artist-id]
(honey.sql/format
(honey.sql/build
:select :*
:from :album
:where [:= :artist_id artist-id]
:order-by [[:album.id :desc]])))
(defn artist-albums [db artist-id]
(jdbc/execute! db (artist-albums-sql artist-id)))
(defn artist-most-recent-album-sql [artist-id]
(honey.sql/format
(honey.sql/build
:select :*
:from :album
:join [:artist [:= :album.artist_id :artist.id]]
:order-by [[:album.id :desc]]
:where [:= :artist.id artist-id])))
(defn artist-most-resent-album [db artist-id]
(jdbc/execute! db (artist-most-recent-album-sql artist-id)))
(defn album [db id]
(jdbc.sql/get-by-id db :album id))

@ -0,0 +1,42 @@
(ns com.darklimericks.db.artists
(:require [next.jdbc :as jdbc]
[honeysql.core :as honey.sql]
[honeysql.helpers :as honey.helpers]))
(defn insert-artist-sql [name]
(let [[s & params] (-> (honey.helpers/insert-into :artist)
(honey.helpers/columns :name)
(honey.helpers/values
[[name]])
honey.sql/format)
s (format "%s RETURNING id" s)]
(concat [s] params)))
(defn insert-artist [db name]
(jdbc/execute! db (insert-artist-sql name)))
(defn most-recent-artist-sql []
(-> {:select [:*]
:from [:artist]
:order-by [[:id :desc]]
:limit 1}
(honey.sql/format)))
(defn most-recent-artist [db]
(jdbc/execute-one! db (most-recent-artist-sql)))
(defn artists-by-letter [db letter]
(jdbc/execute!
db
(honey.sql/format
{:select [:*]
:from [:artist]
:where [:like :name (str letter "%")]})))
(defn artist [db id]
(jdbc/execute-one!
db
(honey.sql/format
{:select [:*]
:from [:artist]
:where [:= :id id]})))

@ -0,0 +1,42 @@
(ns com.darklimericks.db.limericks
(:require [next.jdbc :as jdbc]
[honeysql.core :as honey.sql]
[honeysql.helpers :as honey.helpers]))
(defn insert-limerick-sql [name text album-id]
(-> (honey.helpers/insert-into :limerick)
(honey.helpers/columns :name :text :album-id)
(honey.helpers/values
[[name text album-id]])
honey.sql/format))
(defn insert-limerick [db name text album-id]
(jdbc/execute! db (insert-limerick-sql name text album-id)))
(defn limerick-sql [limerick-id]
(honey.sql/format
(honey.sql/build
:select :*
:from :limerick
:where [:= :limerick_id limerick-id])))
(defn limerick [db limerick-id]
(jdbc/execute! db (limerick-sql limerick-id)))
(defn limericks [db]
(jdbc/execute!
db
(honey.sql/format
(honey.sql/build
:select :*
:from :limerick))))
(defn album-limericks-sql [album-id]
(honey.sql/format
(honey.sql/build
:select :*
:from :limerick
:where [:= :album_id album-id])))
(defn album-limericks [db album-id]
(jdbc/execute! db (album-limericks-sql album-id)))

@ -0,0 +1 @@
(ns com.darklimericks.db.tasks)

@ -0,0 +1,16 @@
(ns com.darklimericks.linguistics.core
(:require [com.owoga.prhyme.data.dictionary :as dict]
[clojure.string :as string]))
(defn gen-artist []
(->> [(rand-nth (seq dict/adverbs))
(rand-nth (seq dict/nouns))]
(map string/capitalize)
(string/join " ")))
(defn gen-album []
(->> [(rand-nth (seq dict/adjectives))
(rand-nth (seq dict/nouns))]
(map string/capitalize)
(string/join " ")))

@ -0,0 +1,26 @@
(ns com.darklimericks.server.core
(:gen-class)
(:require [integrant.core :as ig]
[com.darklimericks.server.system :as system]
[reitit.coercion]
[reitit.coercion.spec]
[taoensso.timbre :as timbre]
[clojure.java.io :as io]))
(def a (atom {}))
(defn -main []
(try
(let [system (->> "server/config.edn"
io/resource
slurp
ig/read-string
ig/prep
ig/init)]
(timbre/info "Running with config: server/config.edn" )
system)
(catch Throwable e
(.printStackTrace e)
(System/exit 1))))

@ -0,0 +1,6 @@
(ns com.darklimericks.server.db
(:require [clojure.tools.namespace.repl :as c.t.n.r]))
(c.t.n.r/disable-unload!)
(defonce db (atom {}))

@ -0,0 +1,55 @@
(ns com.darklimericks.server.example
(:require [integrant.core :as ig]
[clojure.tools.namespace.repl :refer [set-refresh-dirs]]
[integrant.repl :as repl]
[org.httpkit.server :as kit]
[reitit.http :as http]
[hiccup.core :as hiccup]
[hiccup.page :as page]
[taoensso.timbre :as timbre]
[reitit.interceptor.sieppari :as sieppari]))
(defn home []
(page/html5
[:head
[:meta {:charset "utf-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1.0"}]]
[:title "Hello World"]
[:body "Goodbye, world!"]))
(defn home-handler [request]
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (hiccup/html (home))})
(def routes
[["/" {:name ::home
:get {:handler home-handler}}]])
(def config
{:app/handler {:router (ig/ref :app/router)}
:app/router {:routes routes}
:app/server {:port 8000 :handler (ig/ref :app/handler)}})
(defmethod ig/init-key :app/router [_ {:keys [routes]}]
(http/router routes))
(defmethod ig/init-key :app/handler [_ {:keys [router]}]
(http/ring-handler router {:executor sieppari/executor}))
(defmethod ig/init-key :app/server [_ opts]
(timbre/info "Starting server with " opts)
(kit/run-server (:handler opts) (dissoc opts :handler)))
(defmethod ig/halt-key! :app/server [_ server]
(timbre/info "Stopping server")
(server))
(comment
(set-refresh-dirs "src" "dev")
(repl/set-prep! (constantly config))
(repl/prep)
(repl/go)
(repl/reset)
(repl/halt)
)

@ -0,0 +1,197 @@
(ns com.darklimericks.server.handlers
(:require [taoensso.timbre :as timbre]
[hiccup.core :as hiccup]
[reitit.ring :as ring]
[reitit.core :as reitit]
[clojure.string :as string]
[clojure.core.async :as async]
[com.darklimericks.server.util :as util]
[com.darklimericks.db.albums :as db.albums]
[com.darklimericks.db.limericks :as db.limericks]
[com.darklimericks.db.artists :as db.artists]
[com.darklimericks.server.views :as views]
[com.darklimericks.server.limericks :as limericks]))
(defn home-handler [request]
(timbre/info "home-handler")
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (hiccup/html (views/home request))})
(def resource-handler (ring/create-resource-handler {:allow-symlinks? true}))
(defn limerick-generation-post-handler
[db cache]
(fn [{{:keys [scheme]} :params :as request}]
(let [tasks (:tasks @cache)]
(let [task-id (java.util.UUID/randomUUID)
limerick-chan (limericks/generate-limerick cache scheme task-id)]
(swap! cache assoc-in [:tasks task-id] {:status :pending})
(async/go
(let [limerick (async/<! limerick-chan)
[artist-id album-id] (limericks/get-artist-and-album-for-new-limerick db)]
(db.limericks/insert-limerick
db
(limericks/get-limerick-name limerick)
(string/join "\n" limerick)
album-id))))
{:status 301
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (views/page "Dark Limericks"
[:h1 "Creating your limerick..."]
[:div "Submission processing..."]
[:h1 "Current limericks"]
(views/limerick-tasks tasks))})))
(comment
(let [c (async/thread
(Thread/sleep 1000)
200)]
(async/go
(let [val (async/<! c)]
(println (+ 20 val)))))
)
(defn limericks-get-handler [db cache]
(fn [request]
(let [artist-id (get-in request [:parameters :path :artist-id])
artist (db.artists/artist db artist-id)
album-id (get-in request [:parameters :path :album-id])
album (db.albums/album db album-id)
limericks (db.limericks/album-limericks db album-id)]
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (views/page
"Dark Limericks"
[:div.flex.items-center.flex-column
[:div.f3.pt4.light-yellow
(->> artist
:artist/name
(format "%s LYRICS")
(string/upper-case))]
[:div.f4.pv3
[:strong
(->> album
:album/name
(format "album: %s"))]]
(map-indexed
(fn [i limerick]
(let [i (inc i)
limerick-url
(format
"%s#%s"
(util/route-name->path
request
:com.darklimericks.server.system/album
{:artist-id (:artist/id artist)
:album-id (:album/id album)})
i)]
[:a.db.light-yellow
{:href limerick-url}
(format
"%s. %s"
i
(:limerick/name limerick))]))
limericks)
(map-indexed
(fn [i limerick]
(views/limerick i limerick))
limericks)
[:div.f6.light-yellow.pt4.w-60
(string/join
" / "
["Thanks be to Optimus Prhyme"
"by blood honor these words sublime"
"sacred heuristics"
"glorious limericks"
"singularity we climb"])]
[:div.f6.washed-yellow.pt3.w-60
(string/join
" / "
["Submissions, comments, corrections"
"adorations, exultations, rejections"
"all receieved freely"
"@owoga.com, eihli"
"by Optimus Prhyme's directions"])]])})))
(defn limerick-generation-get-handler [db cache]
(fn [request]
(let [tasks (:tasks @cache)]
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (views/page
"Dark Limericks"
(views/limerick-tasks tasks))})))
(defn artists-by-letter [db]
(fn [{{:keys [letter]} :path-params :as req}]
(let [artists (db.artists/artists-by-letter db (string/upper-case letter))]
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (views/page
"Dark Limericks"
(when artists
(let [[right-hand-side-artists
left-hand-side-artists]
((juxt #(take (quot (inc (count artists)) 2) %)
#(drop (quot (inc (count artists)) 2) %))
artists)]
[:div.bg-near-black.br4.flex
[:div.fl.w-50.pa2
(for [artist right-hand-side-artists]
[:a.washed-yellow
{:href (util/route-name->path
req
:com.darklimericks.server.system/artist
{:artist-id (:artist/id artist)})}
(:artist/name artist)])]
[:div.fl.w-50.pa2
(for [artist left-hand-side-artists]
[:a.washed-yellow
{:href "#"}
(:artist/name artist)])]])))})))
(comment
(let [artists ["a"]]
(partition 1 1 nil artists))
((juxt #(take 1 %) #(drop 1 %)) [1 2 3 4])
(into [] (take 1) [1 2 3])
)
(defn artist-get-handler [db]
(fn [request]
(let [artist-id (get-in request [:parameters :path :artist-id])
artist (db.artists/artist db artist-id)
albums (db.albums/artist-albums db artist-id)
limericks (into {} (map
#(vector
(:album/id %)
(db.limericks/album-limericks db (:album/id %)))
albums))]
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (views/page
"Dark Limericks"
[:div
[:div.f3.pt3.light-yellow
(->> artist
:artist/name
(format "%s Lyrics")
(string/upper-case))]
(for [album albums]
[:div
[:div.f4.pt3.light-yellow.pb3
(->> album
:album/name
(format "album: \"%s\""))]
(let [album-url (util/route-name->path
request
:com.darklimericks.server.system/album
{:artist-id (:artist/id artist)
:album-id (:album/id album)})]
(map-indexed
(fn [index limerick]
[:a.f5.washed-yellow.db
{:href (format "%s#%s" album-url (inc index))}
(:limerick/name limerick)])
(get limericks (:album/id album))))])])})))

@ -0,0 +1,40 @@
(ns com.darklimericks.server.interceptors
(:require [reitit.http.interceptors.parameters :refer [parameters-interceptor]]
[muuntaja.interceptor :as muuntaja-interceptor]
[reitit.interceptor.sieppari :as sieppari]
[ring.middleware.keyword-params :refer [keyword-params-request]]
[taoensso.timbre :as timbre]
[reitit.core :as reitit]
[reitit.coercion :as coercion]
[reitit.impl :as impl]))
(def format-interceptor (muuntaja-interceptor/format-interceptor))
(def keywordize-params-interceptor
{:enter
(fn [{:keys [request] :as ctx}]
(update ctx :request keyword-params-request))})
(def logging-interceptor
{:enter (fn [{:keys [request] :as ctx}]
(timbre/info
(str "Entering " (dissoc request ::reitit/match)))
ctx)
:exit (fn [{:keys [response] :as ctx}]
(timbre/info
(str "Exiting " (dissoc response ::reitit/match)))
ctx)})
(def coerce-request-interceptor
{:enter
(fn [{:keys [request] :as ctx}]
(let [{{{:keys [coercion parameters]} :data :as opts} :reitit.core/match} request]
(cond
(not coercion) ctx
(not parameters) ctx
:else
(if-let [coercers (coercion/request-coercers coercion parameters opts)]
(let [coerced (coercion/coerce-request coercers request)]
(assoc-in ctx [:request :parameters] coerced))
ctx))))})

@ -0,0 +1,104 @@
(ns com.darklimericks.server.limericks
(:require [clojure.string :as string]
[clojure.core.async :as async]
[reitit.core :as reitit]
[com.darklimericks.db.artists :as artists]
[com.darklimericks.db.albums :as albums]
[com.darklimericks.db.limericks :as limericks]
[com.darklimericks.linguistics.core :as linguistics]
[com.owoga.prhyme.limerick :as limerick]
[com.owoga.prhyme.data.dictionary :as dict]
[com.owoga.prhyme.data.darklyrics :refer [darklyrics-markov-2]]))
(defn parse-scheme-element [[tokens ctx]]
(cond
(or (= 5 (count tokens))
(= 3 (count tokens)))
(do (assert (re-matches #"\w+\d" (first tokens))
"Expected a letter followed by an integer.")
(let [text (re-find #"[a-zA-Z]+" (first tokens))
count (Integer/parseInt (re-find #"\d+" (first tokens)))]
[(rest tokens) (conj ctx [text count])]))
(or (= 4 (count tokens))
(= 1 (count tokens)))
(do (assert (= (first tokens) (apply str (first ctx))) (format "Expected %s" (first ctx)))
[(rest tokens) ctx])
(= 2 (count tokens))
(do (assert (= (first tokens) (apply str (second ctx))) (format "Expected %s" (second ctx)))
[(rest tokens) ctx])
:else
(throw (ex-info "Invalid scheme" {:scheme tokens}))))
(comment
(parse-scheme-element [["A9" "A9" "B5" "B5" "A9"] []]))
(defn parse-scheme [scheme-string]
(-> (string/split scheme-string #"\s+")
(#(vector % []))
(parse-scheme-element)
(parse-scheme-element)
(parse-scheme-element)
(parse-scheme-element)
(parse-scheme-element)
((fn [[tokens [a b]]]
[a a b b a]))))
(comment
(re-find #"\d+" "alpha 234 aset 34a")
(re-find #"[a-zA-Z]+" "apla352 spac")
(parse-scheme "a9 a9 b6 b6 a9")
(require '[integrant.repl.state :as state])
(reitit/match-by-path (:app/router state/system) "/limerick-generation-task")
((:app/handler state/system) {:request-method :post
:uri "/limerick-generation-task"
:body-params {:scheme 23}})
)
(defn get-artist-and-album-for-new-limerick [db]
(let [artist (artists/most-recent-artist db)
albums (albums/artist-albums db (:artist/id artist))
limericks (limericks/album-limericks db (:album/id (first albums)))]
(cond
(< (count limericks) 10)
[(:artist/id artist) (:album/id (first albums))]
(< (count albums) 5)
(let [album-name (linguistics/gen-album)
{album-id :album/id} (albums/insert-album db album-name (:id artist))]
[(:id artist) album-id])
:else
(let [artist-name (linguistics/gen-artist)
{artist-id :artist/id} (artists/insert-artist db artist-name)
album-name (linguistics/gen-album)
{album-id :album/id} (albums/insert-album db album-name artist-id)]
[artist-id album-id]))))
(defn get-limerick-name [lines]
(->> lines
(string/join " ")
(#(string/split % #"\s+"))
shuffle
(take 2)
(map string/capitalize)
(string/join " ")))
(defn generate-limerick [db scheme task-id]
(let [scheme (parse-scheme scheme)]
(async/thread
(try
(let [rhyme-id (java.util.UUID/randomUUID)
rhyme (limerick/rhyme-from-scheme
dict/prhyme-dict
darklyrics-markov-2
scheme)]
(swap! db update-in [:tasks task-id] assoc :status :finished)
(swap! db assoc-in [:limericks rhyme-id] {:text rhyme :task-id task-id})
rhyme)
(catch Throwable e
(println "Exception" e)
(swap! db update-in [:tasks task-id] assoc :status :failed)
(swap! db assoc-in [:tasks task-id] {:text (.getMessage e)}))))))

@ -0,0 +1,84 @@
(ns com.darklimericks.server.system
(:require [integrant.core :as ig]
[clojure.java.io :as io]
[taoensso.timbre :as timbre]
[org.httpkit.server :as kit]
[next.jdbc :as jdbc]
[environ.core :refer [env]]
[reitit.http :as http]
[reitit.ring :as ring]
[reitit.coercion.spec]
[reitit.ring.coercion :as coercion]
[reitit.interceptor.sieppari :as sieppari]
[reitit.http.interceptors.parameters :refer [parameters-interceptor]]
[com.darklimericks.server.handlers :as handlers]
[com.darklimericks.server.interceptors :as interceptors]
[com.darklimericks.server.db :as db]))
(defmethod ig/prep-key :database.sql/connection [_ _]
{:jdbcUrl (str "jdbc:postgresql://localhost:5432/?user="
(or (env :postgres-user)
"dev")
"&password="
(or (env :postgres-password)
"dev"))})
(defmethod ig/init-key :database.sql/connection [_ db-spec]
(jdbc/get-datasource db-spec))
(defmethod ig/init-key :app/logging [_ config]
(timbre/merge-config! config))
(defmethod ig/halt-key! :app/logging [_ _])
(defmethod ig/init-key :app/cache [_ _]
(timbre/debug "Initializing cache.")
db/db)
(defmethod ig/halt-key! :app/db [_ db]
#_(reset! db {}))
(defmethod ig/init-key :app/router [_ {:keys [db cache]}]
(let [routes [["/" {:name ::home
:get {:handler handlers/home-handler}}]
["/{letter}.html"
{:name ::artists-by-letter
:handler (handlers/artists-by-letter db)}]
["/limerick-generation-task"
{:name ::limerick-generation-task
:post {:handler (handlers/limerick-generation-post-handler db cache)}
:get {:handler (handlers/limerick-generation-get-handler db cache)}}]
["/limericks"
["/:artist-id/:album-id"
{:name ::album
:coercion reitit.coercion.spec/coercion
:parameters {:path {:artist-id int?
:album-id int?}}
:get {:handler (handlers/limericks-get-handler db cache)}}]
["/:artist-id"
{:name ::artist
:coercion reitit.coercion.spec/coercion
:parameters {:path {:artist-id int?}}
:get {:handler (handlers/artist-get-handler db)}}]]
["/assets/*" handlers/resource-handler]]]
(http/router
routes
{:data {:interceptors [interceptors/coerce-request-interceptor
interceptors/logging-interceptor
interceptors/format-interceptor
(parameters-interceptor)
interceptors/keywordize-params-interceptor]}})))
(defmethod ig/init-key :app/handler [_ {:keys [router]}]
(http/ring-handler
router
(ring/create-default-handler)
{:executor sieppari/executor}))
(defmethod ig/init-key :app/server [_ {:keys [handler] :as opts}]
(timbre/info (format "Starting server on port %d" (:port opts)))
(kit/run-server handler (dissoc opts :handler)))
(defmethod ig/halt-key! :app/server [_ server]
(timbre/info "Stopping server.")
(server))

@ -0,0 +1,10 @@
(ns com.darklimericks.server.util
(:require [reitit.core :as reitit]))
(defn route-name->path
([request name]
(route-name->path request name {}))
([request name params]
(->> name
(#(reitit/match-by-name (::reitit/router request) % params))
reitit/match->path)))

@ -0,0 +1,89 @@
(ns com.darklimericks.server.views
(:require [hiccup.form :as form]
[hiccup.page :as page]
[clojure.string :as string]
[com.darklimericks.server.util :as util]))
(defn page [title & body]
(page/html5
[:head
[:meta {:charset "utf-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1.0"}]
(page/include-css "/assets/tachyons.css")
[:title title]
[:link {:rel "shortcut icon" :href "/assets/favicon.ico"}]]
[:body.tc.washed-yellow.bg-near-black.avenir
[:h1
[:a.link.dim.washed-yellow {:href "/"} "DarkLimericks.com"]]
[:div.w-50-ns.w-90.center.bg-dark-gray.pa2
[:div.f6.lh-copy.flex.justify-between
[:a.washed-yellow {:href "#"} "SUBMIT LIMERICKS"]
[:span.dark-yellow "METAL LIMERICKS - CURRENTLY 0 ALBUMS FROM 0+ BANDS"]
[:a.washed-yellow {:href "#"} "LINKS"]]
[:div.flex.items-stretch.w-100.bg-near-black
(let [letters (map (comp str char) (range 97 123))]
(for [letter letters]
[:a.washed-yellow.bg-mid-gray.w-100.pv2.flex-grow-1
{:href (format "/%s.html" letter)
:style "margin: 1px;"}
(string/upper-case letter)]))]
[:div.flex.items-center.justify-center.pv2
[:span.f6.ph2 "Search the darkness for limericks heartless"]
[:form.ph2
{:method "GET" :action "#"}
[:input.bg-white
{:type "text"
:name "search"
:id "search"
:value ""}]]]
[:div.bg-near-black.br4.pa2
body]
[:div.flex.items-center.justify-center.pv2
[:span.f6.ph2 "Search the darkness for limericks heartless"]
[:form.ph2
{:method "GET" :action "#"}
[:input.bg-white
{:type "text"
:name "search"
:id "search"
:value ""}]]]
[:div.flex.items-stretch.w-100.bg-near-black
(let [letters (map (comp str char) (range 97 123))]
(for [letter letters]
[:a.washed-yellow.bg-mid-gray.w-100.pv2.flex-grow-1
{:href (format "/%s.html" letter)
:style "margin: 1px;"}
(string/upper-case letter)]))]
[:div.f6.lh-copy.flex.justify-between
[:a.washed-yellow {:href "#"} "SUBMIT LIMERICKS"]
[:span.dark-yellow "METAL LIMERICKS - CURRENTLY 0 ALBUMS FROM 0+ BANDS"]
[:a.washed-yellow {:href "#"} "LINKS"]]]]))
(defn home [request]
(page "Dark Limericks"
(form/form-to
[:post (util/route-name->path request :com.darklimericks.server.system/limerick-generation-task)]
(form/text-field "scheme")
(form/submit-button "Generate dark limerick"))))
(defn limerick-tasks [tasks]
[:ul
(for [[task-id task] tasks]
[:li (format
"%s - %s"
task-id
(if (:rhyme task)
(string/join " / " (:rhyme task))
(:status task)))])])
(defn limerick [i lim]
(let [lines (string/split (:limerick/text lim) #"\n")
name (:limerick/name lim)]
[:div.tc
[:a {:name (inc i)}
[:h3.f3.washed-yellow (format "%s. %s" (inc i) name)]]
(for [line lines]
[:div line])]))
Loading…
Cancel
Save