diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec686b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +/target +/classes +/checkouts +profiles.clj +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +/out +/.repl +*.log +/.env +/.sass-cache diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..722c2fb --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: java $JVM_OPTS -cp target/pequod-cljs.jar clojure.main -m pequod-cljs.server diff --git a/README.md b/README.md new file mode 100644 index 0000000..b5cbcfa --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# pequod-cljs + +This is the pequod-cljs project. + +## Development mode + +To start the Figwheel compiler, navigate to the project folder and run the following command in the terminal: + +``` +lein figwheel +``` + +Figwheel will automatically push cljs changes to the browser. The server will be available at [http://localhost:3449](http://localhost:3449) once Figwheel starts up. + +Figwheel also starts `nREPL` using the value of the `:nrepl-port` in the `:figwheel` +config found in `project.clj`. By default the port is set to `7002`. + +The figwheel server can have unexpected behaviors in some situations such as when using +websockets. In this case it's recommended to run a standalone instance of a web server as follows: + +``` +lein do clean, run +``` + +The application will now be available at [http://localhost:3000](http://localhost:3000). + + +### Optional development tools + +Start the browser REPL: + +``` +$ lein repl +``` +The Jetty server can be started by running: + +```clojure +(start-server) +``` +and stopped by running: +```clojure +(stop-server) +``` + +## Running the tests +To run [cljs.test](https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/test.cljs) tests, please use + +``` +lein doo +``` + +For installation instructions of PhantomJS, please see [this](http://phantomjs.org/download.html). + +## Building for release + +``` +lein do clean, uberjar +``` + +## Deploying to Heroku + +Make sure you have [Git](http://git-scm.com/downloads) and [Heroku toolbelt](https://toolbelt.heroku.com/) installed, then simply follow the steps below. + +Optionally, test that your application runs locally with foreman by running. + +``` +foreman start +``` + +Now, you can initialize your git repo and commit your application. + +``` +git init +git add . +git commit -m "init" +``` +create your app on Heroku + +``` +heroku create +``` + +optionally, create a database for the application + +``` +heroku addons:add heroku-postgresql +``` + +The connection settings can be found at your [Heroku dashboard](https://dashboard.heroku.com/apps/) under the add-ons for the app. + +deploy the application + +``` +git push heroku master +``` + +Your application should now be deployed to Heroku! +For further instructions see the [official documentation](https://devcenter.heroku.com/articles/clojure). diff --git a/env/dev/clj/pequod_cljs/middleware.clj b/env/dev/clj/pequod_cljs/middleware.clj new file mode 100644 index 0000000..43def81 --- /dev/null +++ b/env/dev/clj/pequod_cljs/middleware.clj @@ -0,0 +1,10 @@ +(ns pequod-cljs.middleware + (:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]] + [prone.middleware :refer [wrap-exceptions]] + [ring.middleware.reload :refer [wrap-reload]])) + +(defn wrap-middleware [handler] + (-> handler + (wrap-defaults site-defaults) + wrap-exceptions + wrap-reload)) diff --git a/env/dev/clj/pequod_cljs/repl.clj b/env/dev/clj/pequod_cljs/repl.clj new file mode 100644 index 0000000..f31fd5b --- /dev/null +++ b/env/dev/clj/pequod_cljs/repl.clj @@ -0,0 +1,33 @@ +(ns pequod-cljs.repl + (:use pequod-cljs.handler + figwheel-sidecar.repl-api + ring.server.standalone + [ring.middleware file-info file])) + +(defonce server (atom nil)) + +(defn get-handler [] + ;; #'app expands to (var app) so that when we reload our code, + ;; the server is forced to re-resolve the symbol in the var + ;; rather than having its own copy. When the root binding + ;; changes, the server picks it up without having to restart. + (-> #'app + ; Makes static assets in $PROJECT_DIR/resources/public/ available. + (wrap-file "resources") + ; Content-Type, Content-Length, and Last Modified headers for files in body + (wrap-file-info))) + +(defn start-server + "used for starting the server in development mode from REPL" + [& [port]] + (let [port (if port (Integer/parseInt port) 3000)] + (reset! server + (serve (get-handler) + {:port port + :auto-reload? true + :join? false})) + (println (str "You can view the site at http://localhost:" port)))) + +(defn stop-server [] + (.stop @server) + (reset! server nil)) diff --git a/env/dev/clj/user.clj b/env/dev/clj/user.clj new file mode 100644 index 0000000..4ba997c --- /dev/null +++ b/env/dev/clj/user.clj @@ -0,0 +1,11 @@ +(ns user + (:require [figwheel-sidecar.repl-api :as ra])) + +(defn start-fw [] + (ra/start-figwheel!)) + +(defn stop-fw [] + (ra/stop-figwheel!)) + +(defn cljs [] + (ra/cljs-repl)) diff --git a/env/dev/cljs/pequod_cljs/dev.cljs b/env/dev/cljs/pequod_cljs/dev.cljs new file mode 100644 index 0000000..e8f4b13 --- /dev/null +++ b/env/dev/cljs/pequod_cljs/dev.cljs @@ -0,0 +1,10 @@ +(ns ^:figwheel-no-load pequod-cljs.dev + (:require + [pequod-cljs.core :as core] + [devtools.core :as devtools])) + +(devtools/install!) + +(enable-console-print!) + +(core/init!) diff --git a/env/prod/clj/pequod_cljs/middleware.clj b/env/prod/clj/pequod_cljs/middleware.clj new file mode 100644 index 0000000..f3ff997 --- /dev/null +++ b/env/prod/clj/pequod_cljs/middleware.clj @@ -0,0 +1,5 @@ +(ns pequod-cljs.middleware + (:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]])) + +(defn wrap-middleware [handler] + (wrap-defaults handler site-defaults)) diff --git a/env/prod/cljs/pequod_cljs/prod.cljs b/env/prod/cljs/pequod_cljs/prod.cljs new file mode 100644 index 0000000..0fa9ebe --- /dev/null +++ b/env/prod/cljs/pequod_cljs/prod.cljs @@ -0,0 +1,7 @@ +(ns pequod-cljs.prod + (:require [pequod-cljs.core :as core])) + +;;ignore println statements in prod +(set! *print-fn* (fn [& _])) + +(core/init!) diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..78867ce --- /dev/null +++ b/project.clj @@ -0,0 +1,120 @@ +(defproject pequod-cljs "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + + :dependencies [[org.clojure/clojure "1.9.0"] + [ring-server "0.5.0"] + [reagent "0.8.1"] + [reagent-utils "0.3.1"] + [ring "1.6.3"] + [ring/ring-defaults "0.3.1"] + [compojure "1.6.1"] + [hiccup "1.0.5"] + [yogthos/config "1.1.1"] + [org.clojure/clojurescript "1.10.339" + :scope "provided"] + [secretary "1.2.3"] + [venantius/accountant "0.2.4" + :exclusions [org.clojure/tools.reader]]] + + :plugins [[lein-environ "1.1.0"] + [lein-cljsbuild "1.1.7"] + [lein-asset-minifier "0.2.7" + :exclusions [org.clojure/clojure]]] + + :ring {:handler pequod-cljs.handler/app + :uberwar-name "pequod-cljs.war"} + + :min-lein-version "2.5.0" + :uberjar-name "pequod-cljs.jar" + :main pequod-cljs.server + :jvm-opts ["--add-modules" "java.xml.bind"] + :clean-targets ^{:protect false} + [:target-path + [:cljsbuild :builds :app :compiler :output-dir] + [:cljsbuild :builds :app :compiler :output-to]] + + :source-paths ["src/clj" "src/cljc"] + :resource-paths ["resources" "target/cljsbuild"] + + :minify-assets + {:assets + {"resources/public/css/site.min.css" "resources/public/css/site.css"}} + + :cljsbuild + {:builds {:min + {:source-paths ["src/cljs" "src/cljc" "env/prod/cljs"] + :compiler + {:output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js" + :source-map "target/cljsbuild/public/js/app.js.map" + :optimizations :advanced + :pretty-print false}} + :app + {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] + :figwheel {:on-jsload "pequod-cljs.core/mount-root"} + :compiler + {:main "pequod-cljs.dev" + :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}} + :test + {:source-paths ["src/cljs" "src/cljc" "test/cljs"] + :compiler {:main pequod-cljs.doo-runner + :asset-path "/js/out" + :output-to "target/test.js" + :output-dir "target/cljstest/public/js/out" + :optimizations :whitespace + :pretty-print true}} + + + } + } + :doo {:build "test" + :alias {:default [:chrome]}} + + :figwheel + {:http-server-root "public" + :server-port 3449 + :nrepl-port 7002 + :nrepl-middleware [cider.piggieback/wrap-cljs-repl + ] + :css-dirs ["resources/public/css"] + :ring-handler pequod-cljs.handler/app} + + + + :profiles {:dev {:repl-options {:init-ns pequod-cljs.repl} + :dependencies [[cider/piggieback "0.3.8"] + [binaryage/devtools "0.9.10"] + [ring/ring-mock "0.3.2"] + [ring/ring-devel "1.6.3"] + [prone "1.5.2"] + [figwheel-sidecar "0.5.16"] + [nrepl "0.4.4"] + [cider/piggieback "0.3.8"] + [pjstadig/humane-test-output "0.8.3"] + + ] + + :source-paths ["env/dev/clj"] + :plugins [[lein-figwheel "0.5.16"] + [lein-doo "0.1.10"] +] + + :injections [(require 'pjstadig.humane-test-output) + (pjstadig.humane-test-output/activate!)] + + :env {:dev true}} + + :uberjar {:hooks [minify-assets.plugin/hooks] + :source-paths ["env/prod/clj"] + :prep-tasks ["compile" ["cljsbuild" "once" "min"]] + :env {:production true} + :aot :all + :omit-source true}}) diff --git a/resources/public/css/site.css b/resources/public/css/site.css new file mode 100644 index 0000000..04f6e7d --- /dev/null +++ b/resources/public/css/site.css @@ -0,0 +1,34 @@ +.body-container { + font-family: 'Helvetica Neue', Verdana, Helvetica, Arial, sans-serif; + max-width: 900px; + margin: 0 auto; + padding-top: 40px; + -webkit-font-smoothing: antialiased; + font-size: 1.125em; + color: #333; + line-height: 1.5em; +} + +h1, h2, h3 { + color: #000; +} +h1 { + font-size: 2.5em +} + +h2 { + font-size: 2em +} + +h3 { + font-size: 1.5em +} + +a { + text-decoration: none; + color: #09f; +} + +a:hover { + text-decoration: underline; +} diff --git a/src/clj/pequod_cljs/handler.clj b/src/clj/pequod_cljs/handler.clj new file mode 100644 index 0000000..7fe73d5 --- /dev/null +++ b/src/clj/pequod_cljs/handler.clj @@ -0,0 +1,37 @@ +(ns pequod-cljs.handler + (:require [compojure.core :refer [GET defroutes]] + [compojure.route :refer [not-found resources]] + [hiccup.page :refer [include-js include-css html5]] + [pequod-cljs.middleware :refer [wrap-middleware]] + [config.core :refer [env]])) + +(def mount-target + [:div#app + [:h3 "ClojureScript has not been compiled!"] + [:p "please run " + [:b "lein figwheel"] + " in order to start the compiler"]]) + +(defn head [] + [:head + [:meta {:charset "utf-8"}] + [:meta {:name "viewport" + :content "width=device-width, initial-scale=1"}] + (include-css (if (env :dev) "/css/site.css" "/css/site.min.css"))]) + +(defn loading-page [] + (html5 + (head) + [:body {:class "body-container"} + mount-target + (include-js "/js/app.js")])) + + +(defroutes routes + (GET "/" [] (loading-page)) + (GET "/about" [] (loading-page)) + + (resources "/") + (not-found "Not Found")) + +(def app (wrap-middleware #'routes)) diff --git a/src/clj/pequod_cljs/server.clj b/src/clj/pequod_cljs/server.clj new file mode 100644 index 0000000..d50ce73 --- /dev/null +++ b/src/clj/pequod_cljs/server.clj @@ -0,0 +1,9 @@ +(ns pequod-cljs.server + (:require [pequod-cljs.handler :refer [app]] + [config.core :refer [env]] + [ring.adapter.jetty :refer [run-jetty]]) + (:gen-class)) + +(defn -main [& args] + (let [port (or (env :port) 3000)] + (run-jetty app {:port port :join? false}))) diff --git a/src/cljc/pequod_cljs/util.cljc b/src/cljc/pequod_cljs/util.cljc new file mode 100644 index 0000000..6f2d659 --- /dev/null +++ b/src/cljc/pequod_cljs/util.cljc @@ -0,0 +1,6 @@ +(ns pequod-cljs.util) + +(defn foo-cljc [x] + "I don't do a whole lot." + [x] + (println x "Hello, World!")) diff --git a/src/cljs/pequod_cljs/core.cljs b/src/cljs/pequod_cljs/core.cljs new file mode 100644 index 0000000..d4cd057 --- /dev/null +++ b/src/cljs/pequod_cljs/core.cljs @@ -0,0 +1,123 @@ +(ns pequod-cljs.core + (:require [reagent.core :as reagent :refer [atom]] + [secretary.core :as secretary :include-macros true] + [accountant.core :as accountant])) + +;; Pequod code + +(def globals + (atom {:init-final-price 100 + :init-intermediate-price 100 + :init-labor-price 150 + :init-nature-price 150 + :finals 4 + :inputs 4 + :resources 1 + :labors 1 + :final-prices [] + :input-prices [] + :labor-prices [] + :nature-prices [] + :old-final-prices [] + :old-input-prices [] + :old-nature-prices [] + :old-labor-prices [] + :final-surpluses [] + :input-surpluses [] + :nature-surpluses [] + :labor-surpluses [] + :threshold-met false + :pdlist [] + :delta-delay 0 + :price-deltas []})) + +(defn standardize-prices [t] + (assoc t + :init-final-price 80 + :init-intermediate-price 80 + :init-nature-price 80 + :init-labor-price 80)) + +(defn randomize-prices [t] + (assoc t + :init-final-price (+ 40 (rand-nth (range 0 40))) + :init-intermediate-price (+ 40 (rand-nth (range 0 40))) + :init-nature-price (+ 30 (rand-nth (range 0 30))) + :init-labor-price (+ 30 (rand-nth (range 0 30))))) + +(defn initialize-prices [t] + (let [finals (t :finals) + inputs (t :inputs) + resources (t :resources) + labor (t :labors)] + (assoc t + :final-prices (repeat finals (t :init-final-price)) + :input-prices (repeat inputs (t :init-intermediate-price)) + :nature-prices (repeat resources (t :init-nature-price)) + :labor-prices (repeat labor (t :init-labor-price)) + :price-deltas (repeat 4 0.05) + :pdlist (repeat (+ finals inputs resources labor) 0.05)))) + +(defn setup [t] + (initialize-prices t) + (assoc t + :price-delta 0.1 + :delta-delay 5 + :final-goods (range 1 (inc (t :finals))) + :intermediate-inputs (range 1 (inc (t :inputs))) + :nature-types (range 1 (inc (t :resources))) + :labor-types (range 1 (inc (t :labors))))) + +;; ------------------------- +;; Views- + +(defn setup-button [] + [:input {:type "button" :value "Setup" + :on-click #(swap! globals setup globals)}]) + +; TODO: Show globals in a more organized way +(defn show-globals [] + [:div " " @globals + [:p] + (setup-button)]) + +(defn home-page [] + [:div [:h2 "Welcome to pequod-cljs"] + [:div [:a {:href "/about"} "go to about"] + [:p] + (show-globals)]]) + +(defn about-page [] + [:div [:h2 "About pequod-cljs"] + [:div [:a {:href "/"} "go to the other page"]]]) + +;; ------------------------- +;; Routes + +(defonce page (atom #'home-page)) + +(defn current-page [] + [:div [@page]]) + +(secretary/defroute "/" [] + (reset! page #'home-page)) + +(secretary/defroute "/about" [] + (reset! page #'about-page)) + +;; ------------------------- +;; Initialize app + +(defn mount-root [] + (reagent/render [current-page] (.getElementById js/document "app"))) + +(defn init! [] + (accountant/configure-navigation! + {:nav-handler + (fn [path] + (secretary/dispatch! path)) + :path-exists? + (fn [path] + (secretary/locate-route path))}) + (accountant/dispatch-current!) + (mount-root)) diff --git a/test/cljs/pequod_cljs/core_test.cljs b/test/cljs/pequod_cljs/core_test.cljs new file mode 100644 index 0000000..8a15807 --- /dev/null +++ b/test/cljs/pequod_cljs/core_test.cljs @@ -0,0 +1,39 @@ +(ns pequod-cljs.core-test + (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]] + [reagent.core :as reagent :refer [atom]] + [pequod-cljs.core :as rc])) + + +(def isClient (not (nil? (try (.-document js/window) + (catch js/Object e nil))))) + +(def rflush reagent/flush) + +(defn add-test-div [name] + (let [doc js/document + body (.-body js/document) + div (.createElement doc "div")] + (.appendChild body div) + div)) + +(defn with-mounted-component [comp f] + (when isClient + (let [div (add-test-div "_testreagent")] + (let [comp (reagent/render-component comp div #(f comp div))] + (reagent/unmount-component-at-node div) + (reagent/flush) + (.removeChild (.-body js/document) div))))) + + +(defn found-in [re div] + (let [res (.-innerHTML div)] + (if (re-find re res) + true + (do (println "Not found: " res) + false)))) + + +(deftest test-home + (with-mounted-component (rc/home-page) + (fn [c div] + (is (found-in #"Welcome to" div))))) diff --git a/test/cljs/pequod_cljs/doo_runner.cljs b/test/cljs/pequod_cljs/doo_runner.cljs new file mode 100644 index 0000000..cd96b0f --- /dev/null +++ b/test/cljs/pequod_cljs/doo_runner.cljs @@ -0,0 +1,5 @@ +(ns pequod-cljs.doo-runner + (:require [doo.runner :refer-macros [doo-tests]] + [pequod-cljs.core-test])) + +(doo-tests 'pequod-cljs.core-test)