Time to start over
+cljs +sqlite +sassc +reagent
This commit is contained in:
commit
c2d1dd3227
43 changed files with 1164 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
/target
|
||||
/lib
|
||||
/classes
|
||||
/checkouts
|
||||
pom.xml
|
||||
*.jar
|
||||
*.class
|
||||
/.lein-*
|
||||
profiles.clj
|
||||
/.env
|
||||
.nrepl-port
|
||||
/log
|
28
Capstanfile
Normal file
28
Capstanfile
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
#
|
||||
# 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 /yenu/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:
|
||||
/yenu/app.jar: ./target/uberjar/yenu.jar
|
||||
|
8
Dockerfile
Normal file
8
Dockerfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
FROM java:8-alpine
|
||||
MAINTAINER Your Name <you@example.com>
|
||||
|
||||
ADD target/uberjar/yenu.jar /yenu/app.jar
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["java", "-jar", "/yenu/app.jar"]
|
1
Procfile
Normal file
1
Procfile
Normal file
|
@ -0,0 +1 @@
|
|||
web: java $JVM_OPTS -cp target/uberjar/yenu.jar clojure.main -m yenu.core
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# yenu
|
||||
|
||||
generated using Luminus version "2.9.11.32"
|
||||
|
||||
FIXME
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need [Leiningen][1] 2.0 or above installed.
|
||||
|
||||
[1]: https://github.com/technomancy/leiningen
|
||||
|
||||
## Running
|
||||
|
||||
To start a web server for the application, run:
|
||||
|
||||
lein run
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2017 FIXME
|
18
env/dev/clj/user.clj
vendored
Normal file
18
env/dev/clj/user.clj
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
(ns user
|
||||
(:require [mount.core :as mount]
|
||||
[yenu.figwheel :refer [start-fw stop-fw cljs]]
|
||||
yenu.core))
|
||||
|
||||
(defn start []
|
||||
(mount/start-without #'yenu.core/http-server
|
||||
#'yenu.core/repl-server))
|
||||
|
||||
(defn stop []
|
||||
(mount/stop-except #'yenu.core/http-server
|
||||
#'yenu.core/repl-server))
|
||||
|
||||
(defn restart []
|
||||
(stop)
|
||||
(start))
|
||||
|
||||
|
10
env/dev/clj/yenu/dev_middleware.clj
vendored
Normal file
10
env/dev/clj/yenu/dev_middleware.clj
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
(ns yenu.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))
|
14
env/dev/clj/yenu/env.clj
vendored
Normal file
14
env/dev/clj/yenu/env.clj
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
(ns yenu.env
|
||||
(:require [selmer.parser :as parser]
|
||||
[clojure.tools.logging :as log]
|
||||
[yenu.dev-middleware :refer [wrap-dev]]))
|
||||
|
||||
(def defaults
|
||||
{:init
|
||||
(fn []
|
||||
(parser/cache-off!)
|
||||
(log/info "\n-=[yenu started successfully using the development profile]=-"))
|
||||
:stop
|
||||
(fn []
|
||||
(log/info "\n-=[yenu has shut down successfully]=-"))
|
||||
:middleware wrap-dev})
|
12
env/dev/clj/yenu/figwheel.clj
vendored
Normal file
12
env/dev/clj/yenu/figwheel.clj
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
(ns yenu.figwheel
|
||||
(:require [figwheel-sidecar.repl-api :as ra]))
|
||||
|
||||
(defn start-fw []
|
||||
(ra/start-figwheel!))
|
||||
|
||||
(defn stop-fw []
|
||||
(ra/stop-figwheel!))
|
||||
|
||||
(defn cljs []
|
||||
(ra/cljs-repl))
|
||||
|
14
env/dev/cljs/yenu/app.cljs
vendored
Normal file
14
env/dev/cljs/yenu/app.cljs
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
(ns ^:figwheel-no-load yenu.app
|
||||
(:require [yenu.core :as core]
|
||||
[devtools.core :as devtools]
|
||||
[figwheel.client :as figwheel :include-macros true]))
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
(figwheel/watch-and-reload
|
||||
:websocket-url "ws://localhost:3449/figwheel-ws"
|
||||
:on-jsload core/mount-components)
|
||||
|
||||
(devtools/install!)
|
||||
|
||||
(core/init!)
|
4
env/dev/resources/config.edn
vendored
Normal file
4
env/dev/resources/config.edn
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{:dev true
|
||||
:port 3000
|
||||
;; when :nrepl-port is set the application starts the nREPL server on load
|
||||
:nrepl-port 7000}
|
43
env/dev/resources/logback.xml
vendored
Normal file
43
env/dev/resources/logback.xml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- encoders are assigned the type
|
||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/yenu.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/yenu.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- keep 30 days of history -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="org.apache.http" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="org.xnio.nio" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="com.zaxxer.hikari" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
11
env/prod/clj/yenu/env.clj
vendored
Normal file
11
env/prod/clj/yenu/env.clj
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
(ns yenu.env
|
||||
(:require [clojure.tools.logging :as log]))
|
||||
|
||||
(def defaults
|
||||
{:init
|
||||
(fn []
|
||||
(log/info "\n-=[yenu started successfully]=-"))
|
||||
:stop
|
||||
(fn []
|
||||
(log/info "\n-=[yenu has shut down successfully]=-"))
|
||||
:middleware identity})
|
7
env/prod/cljs/yenu/app.cljs
vendored
Normal file
7
env/prod/cljs/yenu/app.cljs
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
(ns yenu.app
|
||||
(:require [yenu.core :as core]))
|
||||
|
||||
;;ignore println statements in prod
|
||||
(set! *print-fn* (fn [& _]))
|
||||
|
||||
(core/init!)
|
2
env/prod/resources/config.edn
vendored
Normal file
2
env/prod/resources/config.edn
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
{:production true
|
||||
:port 3000}
|
31
env/prod/resources/logback.xml
vendored
Normal file
31
env/prod/resources/logback.xml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/yenu.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/yenu.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- keep 30 days of history -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="org.apache.http" level="warn">
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="org.xnio.nio" level="warn">
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="com.zaxxer.hikari" level="warn">
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
4
env/test/resources/config.edn
vendored
Normal file
4
env/test/resources/config.edn
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{:dev true
|
||||
:port 3000
|
||||
;; when :nrepl-port is set the application starts the nREPL server on load
|
||||
:nrepl-port 7000}
|
43
env/test/resources/logback.xml
vendored
Normal file
43
env/test/resources/logback.xml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- encoders are assigned the type
|
||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/yenu.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/yenu.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- keep 30 days of history -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="org.apache.http" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="org.xnio.nio" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="com.zaxxer.hikari" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
139
project.clj
Normal file
139
project.clj
Normal file
|
@ -0,0 +1,139 @@
|
|||
(defproject yenu "0.1.0-SNAPSHOT"
|
||||
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
|
||||
:dependencies [[bouncer "1.0.0"]
|
||||
[cljs-ajax "0.5.8"]
|
||||
[compojure "1.5.2"]
|
||||
[conman "0.6.3"]
|
||||
[cprop "0.1.10"]
|
||||
[luminus-immutant "0.2.3"]
|
||||
[luminus-migrations "0.2.9"]
|
||||
[luminus-nrepl "0.1.4"]
|
||||
[markdown-clj "0.9.94"]
|
||||
[metosin/ring-http-response "0.8.1"]
|
||||
[mount "0.1.11"]
|
||||
[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/clojurescript "1.9.473" :scope "provided"]
|
||||
[org.clojure/tools.cli "0.3.5"]
|
||||
[org.clojure/tools.logging "0.3.1"]
|
||||
[org.webjars.bower/tether "1.4.0"]
|
||||
[org.webjars/bootstrap "4.0.0-alpha.5"]
|
||||
[org.webjars/font-awesome "4.7.0"]
|
||||
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
|
||||
[org.xerial/sqlite-jdbc "3.16.1"]
|
||||
[reagent "0.6.0"]
|
||||
[reagent-utils "0.2.0"]
|
||||
[ring-middleware-format "0.7.2"]
|
||||
[ring-webjars "0.1.1"]
|
||||
[ring/ring-core "1.5.1"]
|
||||
[ring/ring-defaults "0.2.3"]
|
||||
[secretary "1.2.3"]
|
||||
[selmer "1.10.6"]]
|
||||
|
||||
:min-lein-version "2.0.0"
|
||||
|
||||
:jvm-opts ["-server" "-Dconf=.lein-env"]
|
||||
:source-paths ["src/clj" "src/cljc"]
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
:target-path "target/%s/"
|
||||
:main yenu.core
|
||||
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
|
||||
|
||||
:plugins [[lein-cprop "1.0.1"]
|
||||
[migratus-lein "0.4.3"]
|
||||
[lein-cljsbuild "1.1.4"]
|
||||
[lein-immutant "2.1.0"]
|
||||
[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 [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]]
|
||||
:figwheel
|
||||
{:http-server-root "public"
|
||||
:nrepl-port 7002
|
||||
:css-dirs ["resources/public/css"]
|
||||
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
|
||||
|
||||
|
||||
:profiles
|
||||
{:uberjar {:omit-source true
|
||||
:prep-tasks ["compile" ["cljsbuild" "once" "min"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:min
|
||||
{:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/cljsbuild/public/js/app.js"
|
||||
:optimizations :advanced
|
||||
:pretty-print false
|
||||
:closure-warnings
|
||||
{:externs-validation :off :non-standard-jsdoc :off}
|
||||
:externs ["react/externs/react.js"]}}}}
|
||||
|
||||
|
||||
:aot :all
|
||||
:uberjar-name "yenu.jar"
|
||||
:source-paths ["env/prod/clj"]
|
||||
:resource-paths ["env/prod/resources"]}
|
||||
|
||||
:dev [:project/dev :profiles/dev]
|
||||
:test [:project/dev :project/test :profiles/test]
|
||||
|
||||
:project/dev {:dependencies [[prone "1.1.4"]
|
||||
[ring/ring-mock "0.3.0"]
|
||||
[ring/ring-devel "1.5.1"]
|
||||
[pjstadig/humane-test-output "0.8.1"]
|
||||
[binaryage/devtools "0.9.0"]
|
||||
[com.cemerick/piggieback "0.2.2-SNAPSHOT"]
|
||||
[doo "0.1.7"]
|
||||
[figwheel-sidecar "0.5.9"]]
|
||||
:plugins [[com.jakemccrary/lein-test-refresh "0.18.1"]
|
||||
[lein-doo "0.1.7"]
|
||||
[lein-figwheel "0.5.9"]
|
||||
[org.clojure/clojurescript "1.9.473"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:app
|
||||
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
|
||||
:compiler
|
||||
{:main "yenu.app"
|
||||
:asset-path "/js/out"
|
||||
:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js/out"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true}}}}
|
||||
|
||||
|
||||
|
||||
:doo {:build "test"}
|
||||
:source-paths ["env/dev/clj" "test/clj"]
|
||||
:resource-paths ["env/dev/resources"]
|
||||
:repl-options {:init-ns user}
|
||||
:injections [(require 'pjstadig.humane-test-output)
|
||||
(pjstadig.humane-test-output/activate!)]}
|
||||
:project/test {:resource-paths ["env/test/resources"]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:test
|
||||
{:source-paths ["src/cljc" "src/cljs" "test/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/test.js"
|
||||
:main "yenu.doo-runner"
|
||||
:optimizations :whitespace
|
||||
:pretty-print true}}}}
|
||||
|
||||
}
|
||||
:profiles/dev {}
|
||||
:profiles/test {}})
|
137
resources/docs/docs.md
Normal file
137
resources/docs/docs.md
Normal file
|
@ -0,0 +1,137 @@
|
|||
<h2 class="alert alert-success">Congratulations, your <a class="alert-link" href="http://luminusweb.net">Luminus</a> site is ready!</h2>
|
||||
|
||||
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 `yenu.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.
|
||||
|
||||
```
|
||||
(defroutes home-routes
|
||||
(GET "/" []
|
||||
(home-page))
|
||||
(GET "/docs" []
|
||||
(-> (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 `yenu.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/yenu/core.cljs` file.
|
||||
|
||||
|
||||
|
||||
#### Organizing the routes
|
||||
|
||||
The routes are aggregated and wrapped with middleware in the `yenu.handler` namespace:
|
||||
|
||||
```
|
||||
(def app-routes
|
||||
(routes
|
||||
(-> #'home-routes
|
||||
(wrap-routes middleware/wrap-csrf)
|
||||
(wrap-routes middleware/wrap-formats))
|
||||
(route/not-found
|
||||
(:body
|
||||
(error-page {:status 404
|
||||
:title "page not found"})))))
|
||||
```
|
||||
|
||||
The `app-routes` definition groups all the routes in the application into a single handler.
|
||||
A default route group is added to handle the `404` case.
|
||||
|
||||
<a class="btn btn-primary" href="http://www.luminusweb.net/docs/routes.md">learn more about routing »</a>
|
||||
|
||||
The `home-routes` are wrapped with two middleware functions. The first enables CSRF protection.
|
||||
The second takes care of serializing and deserializing various encoding formats, such as JSON.
|
||||
|
||||
#### Managing your middleware
|
||||
|
||||
Request middleware functions are located under the `yenu.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 `yenu.dev-middleware` namespace found in
|
||||
the `env/dev/clj/` source path.
|
||||
|
||||
<a class="btn btn-primary" href="http://www.luminusweb.net/docs/middleware.md">learn more about middleware »</a>
|
||||
|
||||
<div class="bs-callout bs-callout-danger">
|
||||
|
||||
#### 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 `profiles.clj` file with your database name and login.
|
||||
* 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 `yenu.db.core` in some other namespace.
|
||||
* Restart the application.
|
||||
|
||||
<a class="btn btn-primary" href="http://www.luminusweb.net/docs/database.md">learn more about database access »</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bs-callout bs-callout-danger">
|
||||
|
||||
#### 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: <a href="http://github.com/sass/sassc">http://github.com/sass/sassc</a>
|
||||
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.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
#### 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.
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE users;
|
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE users
|
||||
(id VARCHAR(20) PRIMARY KEY,
|
||||
first_name VARCHAR(30),
|
||||
last_name VARCHAR(30),
|
||||
email VARCHAR(30),
|
||||
admin BOOLEAN,
|
||||
last_login TIME,
|
||||
is_active BOOLEAN,
|
||||
pass VARCHAR(300));
|
69
resources/public/css/screen.css
Normal file
69
resources/public/css/screen.css
Normal file
|
@ -0,0 +1,69 @@
|
|||
html,
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
height: 100%;
|
||||
}
|
||||
.navbar {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
.navbar-brand {
|
||||
float: none;
|
||||
}
|
||||
.navbar-nav .nav-item {
|
||||
float: none;
|
||||
}
|
||||
.navbar-divider,
|
||||
.navbar-nav .nav-item+.nav-item,
|
||||
.navbar-nav .nav-link + .nav-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
@media (min-width: 34em) {
|
||||
.navbar-brand {
|
||||
float: left;
|
||||
}
|
||||
.navbar-nav .nav-item {
|
||||
float: left;
|
||||
}
|
||||
.navbar-divider,
|
||||
.navbar-nav .nav-item+.nav-item,
|
||||
.navbar-nav .nav-link + .nav-link {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes three-quarters-loader {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-moz-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes three-quarters-loader {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes three-quarters-loader {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-moz-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
BIN
resources/public/favicon.ico
Normal file
BIN
resources/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/public/img/warning_clojure.png
Normal file
BIN
resources/public/img/warning_clojure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
5
resources/scss/screen.scss
Normal file
5
resources/scss/screen.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
html, body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
height: 100%;
|
||||
padding-top: 40px;
|
||||
}
|
21
resources/sql/queries.sql
Normal file
21
resources/sql/queries.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
-- :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 update 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 retrieve a user given the id.
|
||||
SELECT * FROM users
|
||||
WHERE id = :id
|
||||
|
||||
-- :name delete-user! :! :n
|
||||
-- :doc delete a user given the id
|
||||
DELETE FROM users
|
||||
WHERE id = :id
|
56
resources/templates/error.html
Normal file
56
resources/templates/error.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Something bad happened</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
|
||||
{% style "/assets/bootstrap/css/bootstrap-theme.min.css" %}
|
||||
<style type="text/css">
|
||||
html {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
html body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
html .container-fluid {
|
||||
display: table;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
html .row-fluid {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="col-lg-12">
|
||||
<div class="centering text-center">
|
||||
<div class="text-center">
|
||||
<h1><span class="text-danger">Error: {{status}}</span></h1>
|
||||
<hr>
|
||||
{% if title %}
|
||||
<h2 class="without-margin">{{title}}</h2>
|
||||
{% endif %}
|
||||
{% if message %}
|
||||
<h4 class="text-danger">{{message}}</h4>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
46
resources/templates/home.html
Normal file
46
resources/templates/home.html
Normal file
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Welcome to yenu</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<div id="app">
|
||||
<div class="container-fluid">
|
||||
<div class="card-deck">
|
||||
<div class="card-block">
|
||||
<h4>Welcome to yenu</h4>
|
||||
<p>If you're seeing this message, that means you haven't yet compiled your ClojureScript!</p>
|
||||
<p>Please run <code>lein figwheel</code> to start the ClojureScript compiler and reload the page.</p>
|
||||
<h4>For better ClojureScript development experience in Chrome follow these steps:</h4>
|
||||
<ul>
|
||||
<li>Open DevTools
|
||||
<li>Go to Settings ("three dots" icon in the upper right corner of DevTools > Menu > Settings F1 > General > Console)
|
||||
<li>Check-in "Enable custom formatters"
|
||||
<li>Close DevTools
|
||||
<li>Open DevTools
|
||||
</ul>
|
||||
<p>See <a href="http://www.luminusweb.net/docs/clojurescript.md">ClojureScript</a> documentation for further details.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- scripts and styles -->
|
||||
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
|
||||
{% style "/assets/font-awesome/css/font-awesome.min.css" %}
|
||||
{% style "/css/screen.css" %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var context = "{{servlet-context}}";
|
||||
var csrfToken = "{{csrf-token}}";
|
||||
</script>
|
||||
{% script "/js/app.js" %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
10
src/clj/yenu/config.clj
Normal file
10
src/clj/yenu/config.clj
Normal file
|
@ -0,0 +1,10 @@
|
|||
(ns yenu.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)]))
|
58
src/clj/yenu/core.clj
Normal file
58
src/clj/yenu/core.clj
Normal file
|
@ -0,0 +1,58 @@
|
|||
(ns yenu.core
|
||||
(:require [yenu.handler :as handler]
|
||||
[luminus.repl-server :as repl]
|
||||
[luminus.http-server :as http]
|
||||
[luminus-migrations.core :as migrations]
|
||||
[yenu.config :refer [env]]
|
||||
[clojure.tools.cli :refer [parse-opts]]
|
||||
[clojure.tools.logging :as log]
|
||||
[mount.core :as mount])
|
||||
(:gen-class))
|
||||
|
||||
(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-let [nrepl-port (env :nrepl-port)]
|
||||
(repl/start {:port nrepl-port}))
|
||||
:stop
|
||||
(when repl-server
|
||||
(repl/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]
|
||||
(cond
|
||||
(some #{"migrate" "rollback"} args)
|
||||
(do
|
||||
(mount/start #'yenu.config/env)
|
||||
(migrations/migrate args (select-keys env [:database-url]))
|
||||
(System/exit 0))
|
||||
:else
|
||||
(start-app args)))
|
||||
|
12
src/clj/yenu/db/core.clj
Normal file
12
src/clj/yenu/db/core.clj
Normal file
|
@ -0,0 +1,12 @@
|
|||
(ns yenu.db.core
|
||||
(:require
|
||||
[conman.core :as conman]
|
||||
[mount.core :refer [defstate]]
|
||||
[yenu.config :refer [env]]))
|
||||
|
||||
(defstate ^:dynamic *db*
|
||||
:start (conman/connect! {:jdbc-url (env :database-url)})
|
||||
:stop (conman/disconnect! *db*))
|
||||
|
||||
(conman/bind-connection *db* "sql/queries.sql")
|
||||
|
25
src/clj/yenu/handler.clj
Normal file
25
src/clj/yenu/handler.clj
Normal file
|
@ -0,0 +1,25 @@
|
|||
(ns yenu.handler
|
||||
(:require [compojure.core :refer [routes wrap-routes]]
|
||||
[yenu.layout :refer [error-page]]
|
||||
[yenu.routes.home :refer [home-routes]]
|
||||
[compojure.route :as route]
|
||||
[yenu.env :refer [defaults]]
|
||||
[mount.core :as mount]
|
||||
[yenu.middleware :as middleware]))
|
||||
|
||||
(mount/defstate init-app
|
||||
:start ((or (:init defaults) identity))
|
||||
:stop ((or (:stop defaults) identity)))
|
||||
|
||||
(def app-routes
|
||||
(routes
|
||||
(-> #'home-routes
|
||||
(wrap-routes middleware/wrap-csrf)
|
||||
(wrap-routes middleware/wrap-formats))
|
||||
(route/not-found
|
||||
(:body
|
||||
(error-page {:status 404
|
||||
:title "page not found"})))))
|
||||
|
||||
|
||||
(defn app [] (middleware/wrap-base #'app-routes))
|
38
src/clj/yenu/layout.clj
Normal file
38
src/clj/yenu/layout.clj
Normal file
|
@ -0,0 +1,38 @@
|
|||
(ns yenu.layout
|
||||
(:require [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*]]))
|
||||
|
||||
(declare ^:dynamic *app-context*)
|
||||
(parser/set-resource-path! (clojure.java.io/resource "templates"))
|
||||
(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/templates"
|
||||
[template & [params]]
|
||||
(content-type
|
||||
(ok
|
||||
(parser/render-file
|
||||
template
|
||||
(assoc params
|
||||
:page template
|
||||
:csrf-token *anti-forgery-token*
|
||||
:servlet-context *app-context*)))
|
||||
"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)})
|
66
src/clj/yenu/middleware.clj
Normal file
66
src/clj/yenu/middleware.clj
Normal file
|
@ -0,0 +1,66 @@
|
|||
(ns yenu.middleware
|
||||
(:require [yenu.env :refer [defaults]]
|
||||
[clojure.tools.logging :as log]
|
||||
[yenu.layout :refer [*app-context* error-page]]
|
||||
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
|
||||
[ring.middleware.webjars :refer [wrap-webjars]]
|
||||
[ring.middleware.format :refer [wrap-restful-format]]
|
||||
[yenu.config :refer [env]]
|
||||
[ring.middleware.flash :refer [wrap-flash]]
|
||||
[immutant.web.middleware :refer [wrap-session]]
|
||||
[ring.middleware.defaults :refer [site-defaults wrap-defaults]])
|
||||
(:import [javax.servlet ServletContext]))
|
||||
|
||||
(defn wrap-context [handler]
|
||||
(fn [request]
|
||||
(binding [*app-context*
|
||||
(if-let [context (:servlet-context request)]
|
||||
;; If we're not inside a servlet environment
|
||||
;; (for example when using mock requests), then
|
||||
;; .getContextPath might not exist
|
||||
(try (.getContextPath ^ServletContext context)
|
||||
(catch IllegalArgumentException _ context))
|
||||
;; if the context is not specified in the request
|
||||
;; we check if one has been specified in the environment
|
||||
;; instead
|
||||
(:app-context env))]
|
||||
(handler request))))
|
||||
|
||||
(defn wrap-internal-error [handler]
|
||||
(fn [req]
|
||||
(try
|
||||
(handler req)
|
||||
(catch Throwable t
|
||||
(log/error 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 (wrap-restful-format
|
||||
handler
|
||||
{:formats [:json-kw :transit-json :transit-msgpack]})]
|
||||
(fn [request]
|
||||
;; disable wrap-formats for websockets
|
||||
;; since they're not compatible with this middleware
|
||||
((if (:websocket? request) handler wrapped) request))))
|
||||
|
||||
(defn wrap-base [handler]
|
||||
(-> ((:middleware defaults) handler)
|
||||
wrap-webjars
|
||||
wrap-flash
|
||||
(wrap-session {:cookie-attrs {:http-only true}})
|
||||
(wrap-defaults
|
||||
(-> site-defaults
|
||||
(assoc-in [:security :anti-forgery] false)
|
||||
(dissoc :session)))
|
||||
wrap-context
|
||||
wrap-internal-error))
|
16
src/clj/yenu/routes/home.clj
Normal file
16
src/clj/yenu/routes/home.clj
Normal file
|
@ -0,0 +1,16 @@
|
|||
(ns yenu.routes.home
|
||||
(:require [yenu.layout :as layout]
|
||||
[compojure.core :refer [defroutes GET]]
|
||||
[ring.util.http-response :as response]
|
||||
[clojure.java.io :as io]))
|
||||
|
||||
(defn home-page []
|
||||
(layout/render "home.html"))
|
||||
|
||||
(defroutes home-routes
|
||||
(GET "/" []
|
||||
(home-page))
|
||||
(GET "/docs" []
|
||||
(-> (response/ok (-> "docs/docs.md" io/resource slurp))
|
||||
(response/header "Content-Type" "text/plain; charset=utf-8"))))
|
||||
|
3
src/cljc/yenu/validation.cljc
Normal file
3
src/cljc/yenu/validation.cljc
Normal file
|
@ -0,0 +1,3 @@
|
|||
(ns yenu.validation
|
||||
(:require [bouncer.core :as b]
|
||||
[bouncer.validators :as v]))
|
20
src/cljs/yenu/ajax.cljs
Normal file
20
src/cljs/yenu/ajax.cljs
Normal file
|
@ -0,0 +1,20 @@
|
|||
(ns yenu.ajax
|
||||
(:require [ajax.core :as ajax]))
|
||||
|
||||
(defn local-uri? [{:keys [uri]}]
|
||||
(not (re-find #"^\w+?://" uri)))
|
||||
|
||||
(defn default-headers [request]
|
||||
(if (local-uri? request)
|
||||
(-> request
|
||||
(update :uri #(str js/context %))
|
||||
(update :headers #(merge {"x-csrf-token" js/csrfToken} %)))
|
||||
request))
|
||||
|
||||
(defn load-interceptors! []
|
||||
(swap! ajax/default-interceptors
|
||||
conj
|
||||
(ajax/to-interceptor {:name "default headers"
|
||||
:request default-headers})))
|
||||
|
||||
|
86
src/cljs/yenu/core.cljs
Normal file
86
src/cljs/yenu/core.cljs
Normal file
|
@ -0,0 +1,86 @@
|
|||
(ns yenu.core
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.session :as session]
|
||||
[secretary.core :as secretary :include-macros true]
|
||||
[goog.events :as events]
|
||||
[goog.history.EventType :as HistoryEventType]
|
||||
[markdown.core :refer [md->html]]
|
||||
[yenu.ajax :refer [load-interceptors!]]
|
||||
[ajax.core :refer [GET POST]])
|
||||
(:import goog.History))
|
||||
|
||||
(defn nav-link [uri title page collapsed?]
|
||||
[:li.nav-item
|
||||
{:class (when (= page (session/get :page)) "active")}
|
||||
[:a.nav-link
|
||||
{:href uri
|
||||
:on-click #(reset! collapsed? true)} title]])
|
||||
|
||||
(defn navbar []
|
||||
(let [collapsed? (r/atom true)]
|
||||
(fn []
|
||||
[:nav.navbar.navbar-dark.bg-primary
|
||||
[:button.navbar-toggler.hidden-sm-up
|
||||
{:on-click #(swap! collapsed? not)} "☰"]
|
||||
[:div.collapse.navbar-toggleable-xs
|
||||
(when-not @collapsed? {:class "in"})
|
||||
[:a.navbar-brand {:href "#/"} "yenu"]
|
||||
[:ul.nav.navbar-nav
|
||||
[nav-link "#/" "Home" :home collapsed?]
|
||||
[nav-link "#/about" "About" :about collapsed?]]]])))
|
||||
|
||||
(defn about-page []
|
||||
[:div.container
|
||||
[:div.row
|
||||
[:div.col-md-12
|
||||
[:img {:src (str js/context "/img/warning_clojure.png")}]]]])
|
||||
|
||||
(defn home-page []
|
||||
[:div.container
|
||||
(when-let [docs (session/get :docs)]
|
||||
[:div.row>div.col-sm-12
|
||||
[:div {:dangerouslySetInnerHTML
|
||||
{:__html (md->html docs)}}]])])
|
||||
|
||||
(def pages
|
||||
{:home #'home-page
|
||||
:about #'about-page})
|
||||
|
||||
(defn page []
|
||||
[(pages (session/get :page))])
|
||||
|
||||
;; -------------------------
|
||||
;; Routes
|
||||
(secretary/set-config! :prefix "#")
|
||||
|
||||
(secretary/defroute "/" []
|
||||
(session/put! :page :home))
|
||||
|
||||
(secretary/defroute "/about" []
|
||||
(session/put! :page :about))
|
||||
|
||||
;; -------------------------
|
||||
;; History
|
||||
;; must be called after routes have been defined
|
||||
(defn hook-browser-navigation! []
|
||||
(doto (History.)
|
||||
(events/listen
|
||||
HistoryEventType/NAVIGATE
|
||||
(fn [event]
|
||||
(secretary/dispatch! (.-token event))))
|
||||
(.setEnabled true)))
|
||||
|
||||
;; -------------------------
|
||||
;; Initialize app
|
||||
(defn fetch-docs! []
|
||||
(GET "/docs" {:handler #(session/put! :docs %)}))
|
||||
|
||||
(defn mount-components []
|
||||
(r/render [#'navbar] (.getElementById js/document "navbar"))
|
||||
(r/render [#'page] (.getElementById js/document "app")))
|
||||
|
||||
(defn init! []
|
||||
(load-interceptors!)
|
||||
(fetch-docs!)
|
||||
(hook-browser-navigation!)
|
||||
(mount-components))
|
36
test/clj/yenu/test/db/core.clj
Normal file
36
test/clj/yenu/test/db/core.clj
Normal file
|
@ -0,0 +1,36 @@
|
|||
(ns yenu.test.db.core
|
||||
(:require [yenu.db.core :refer [*db*] :as db]
|
||||
[luminus-migrations.core :as migrations]
|
||||
[clojure.test :refer :all]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[yenu.config :refer [env]]
|
||||
[mount.core :as mount]))
|
||||
|
||||
(use-fixtures
|
||||
:once
|
||||
(fn [f]
|
||||
(mount/start
|
||||
#'yenu.config/env
|
||||
#'yenu.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"})))))
|
13
test/clj/yenu/test/handler.clj
Normal file
13
test/clj/yenu/test/handler.clj
Normal file
|
@ -0,0 +1,13 @@
|
|||
(ns yenu.test.handler
|
||||
(:require [clojure.test :refer :all]
|
||||
[ring.mock.request :refer :all]
|
||||
[yenu.handler :refer :all]))
|
||||
|
||||
(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))))))
|
9
test/cljs/yenu/core_test.cljs
Normal file
9
test/cljs/yenu/core_test.cljs
Normal file
|
@ -0,0 +1,9 @@
|
|||
(ns yenu.core-test
|
||||
(:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
|
||||
[pjstadig.humane-test-output]
|
||||
[reagent.core :as reagent :refer [atom]]
|
||||
[yenu.core :as rc]))
|
||||
|
||||
(deftest test-home
|
||||
(is (= true true)))
|
||||
|
6
test/cljs/yenu/doo_runner.cljs
Normal file
6
test/cljs/yenu/doo_runner.cljs
Normal file
|
@ -0,0 +1,6 @@
|
|||
(ns yenu.doo-runner
|
||||
(:require [doo.runner :refer-macros [doo-tests]]
|
||||
[yenu.core-test]))
|
||||
|
||||
(doo-tests 'yenu.core-test)
|
||||
|
Loading…
Reference in a new issue