Skip to content

Commit

Permalink
First commit. Work on the router
Browse files Browse the repository at this point in the history
  • Loading branch information
retro committed Jan 3, 2016
0 parents commit f165b41
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
@@ -0,0 +1,14 @@
/resources/public/js/compiled/**
figwheel_server.log
pom.xml
*jar
/lib/
/classes/
/out/
/target/
.lein-deps-sum
.lein-repl-history
.lein-plugins/
.repl
.nrepl-port
node_modules
39 changes: 39 additions & 0 deletions README.md
@@ -0,0 +1,39 @@
# ashiba

FIXME: Write a one-line description of your library/project.

## Overview

FIXME: Write a paragraph about the library/project and highlight its goals.

## Setup

To get an interactive development environment run:

lein figwheel

and open your browser at [localhost:3449](http://localhost:3449/).
This will auto compile and send all changes to the browser without the
need to reload. After the compilation process is complete, you will
get a Browser Connected REPL. An easy way to try it is:

(js/alert "Am I connected?")

and you should see an alert in the browser window.

To clean all compiled files:

lein clean

To create a production build run:

lein cljsbuild once min

And open your browser in `resources/public/index.html`. You will not
get live reloading, nor a REPL.

## License

Copyright © 2014 FIXME

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.
6 changes: 6 additions & 0 deletions karma.conf.js
@@ -0,0 +1,6 @@
module.exports = function(config){
config.set({
reporters: ['mocha'],
plugins: ['karma-mocha-reporter']
});
}
12 changes: 12 additions & 0 deletions package.json
@@ -0,0 +1,12 @@
{
"name": "ashiba",
"version": "1.0.0",
"description": "FIXME: Write a one-line description of your library/project.",
"author": "",
"license": "ISC",
"devDependencies": {
"karma": "^0.13.16",
"karma-chrome-launcher": "^0.2.2",
"karma-cljs-test": "^0.1.0"
}
}
80 changes: 80 additions & 0 deletions project.clj
@@ -0,0 +1,80 @@
(defproject ashiba "0.1.0-SNAPSHOT"
:description "FIXME: write this!"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}

:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.170"]
[org.clojure/core.async "0.2.374"]
[reagent "0.5.0"]
[lein-doo "0.1.6"]
[secretary "1.2.3"]
[com.cemerick/url "0.1.1"]
[com.stuartsierra/component "0.3.1"]]

:plugins [[lein-cljsbuild "1.1.1"]
[lein-figwheel "0.5.0-1"]
[lein-doo "0.1.6"]]

:source-paths ["src"]

:clean-targets ^{:protect false} ["resources/public/js/compiled" "target"]

:cljsbuild {:builds
[{:id "dev"
:source-paths ["src"]

:figwheel {:on-jsload "ashiba.core/on-js-reload"}

:compiler {:main ashiba.core
:asset-path "js/compiled/out"
:output-to "resources/public/js/compiled/ashiba.js"
:output-dir "resources/public/js/compiled/out"
:source-map-timestamp true}}
;; This next build is an compressed minified build for
;; production. You can build this with:
;; lein cljsbuild once min
{:id "min"
:source-paths ["src"]
:compiler {:output-to "resources/public/js/compiled/ashiba.js"
:main ashiba.core
:optimizations :advanced
:pretty-print false}}
{:id "test"
:source-paths ["src" "test"]
:compiler {:output-to
"resources/public/js/compiled/test.js"
:optimizations :none
:main ashiba.test.core}}]}

:figwheel {;; :http-server-root "public" ;; default and assumes "resources"
;; :server-port 3449 ;; default
;; :server-ip "127.0.0.1"

:css-dirs ["resources/public/css"] ;; watch and update CSS

;; Start an nREPL server into the running figwheel process
;; :nrepl-port 7888

;; Server Ring Handler (optional)
;; if you want to embed a ring handler into the figwheel http-kit
;; server, this is for simple ring servers, if this
;; doesn't work for you just run your own server :)
;; :ring-handler hello_world.server/handler

;; To be able to open files in your editor from the heads up display
;; you will need to put a script on your path.
;; that script will have to take a file path and a line number
;; ie. in ~/bin/myfile-opener
;; #! /bin/sh
;; emacsclient -n +$2 $1
;;
;; :open-file-command "myfile-opener"

;; if you want to disable the REPL
;; :repl false

;; to configure a different figwheel logfile path
;; :server-logfile "tmp/logs/figwheel-logfile.log"
})
2 changes: 2 additions & 0 deletions resources/public/css/style.css
@@ -0,0 +1,2 @@
/* some style */

15 changes: 15 additions & 0 deletions resources/public/index.html
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="app">
<h2>Figwheel template</h2>
<p>Checkout your developer console.</p>
</div>
<script src="js/compiled/ashiba.js" type="text/javascript"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions src/ashiba/app_state.cljs
@@ -0,0 +1,28 @@
(ns ashiba.app-state
(:require [reagent.core :as reagent :refer [atom cursor]]
[com.stuartsierra.component :as component]))

(defn app-db []
(atom {:route {}
:entity-db {}
:kv {}}))

(defn route-store [app-db]
(cursor app-db [:route]))

(defn kv-store [app-db]
(cursor app-db [:kv]))

(defn entity-db [app-db]
(cursor app-db [:entity-db]))



(defrecord AppState [route-component app-db services]
component/Lifecycle
(start [component]
(-> component
(assoc :route-chan (:changes route-component))
(assoc :app-db app-db)
(assoc :services services)))
(stop [component]))
23 changes: 23 additions & 0 deletions src/ashiba/core.cljs
@@ -0,0 +1,23 @@
(ns ashiba.core
(:require [reagent.core :as reagent :refer [atom]]))

(enable-console-print!)

(println "Edits to this text should show up in your developer console.")

;; define your app data so that it doesn't get over-written on reload

(defonce app-state (atom {:text "Hello world!"}))

(defn hello-world []
[:h1 (:text @app-state)])

(reagent/render-component [hello-world]
(. js/document (getElementById "app")))


(defn on-js-reload []
;; optionally touch your app-state to force rerendering depending on
;; your application
;; (swap! app-state update-in [:__figwheel_counter] inc)
)
89 changes: 89 additions & 0 deletions src/ashiba/router.cljs
@@ -0,0 +1,89 @@
(ns ashiba.router
(:require [cemerick.url :as u]
[clojure.walk :refer [keywordize-keys]]))

(defn process-route-part [default-keys part]
(let [is-placeholder? (= ":" (first part))
key (when is-placeholder? (keyword (subs part 1)))
has-default? (contains? default-keys key)
min-matches (if has-default? "*" "+")
re-match (if is-placeholder? (str "(" "[^/]" min-matches ")") part)]
{:is-placeholder? is-placeholder?
:key key
:has-default has-default?
:re-match re-match}))

(defn route-regex [parts]
(let [base-regex (clojure.string/join "/" (map (fn [p] (:re-match p)) parts))
full-regex (str "^" base-regex "$")]
(re-pattern full-regex)))

(defn route-placeholders [parts]
(remove nil? (map (fn [p] (:key p)) parts)))

(defn add-default-params [route]
(if (string? route) [route {}] route))

(defn strip-slashes [route]
(clojure.string/replace (clojure.string/trim route) #"^/+|/+$" ""))

(defn process-route [[route defaults]]
(let [parts (clojure.string/split route #"/")
processed-parts (map (partial process-route-part (set (keys defaults))) parts)]
{:parts processed-parts
:regex (route-regex processed-parts)
:placeholders (route-placeholders processed-parts)
:route route
:defaults (or defaults {})}))

(defn remove-empty-matches [matches]
(apply dissoc matches (for [[k v] matches :when (empty? v)] k)))

(defn expand-route [route]
(let [strip-slashes (fn [[route defaults]] [(strip-slashes route) defaults])]
(-> route
add-default-params
strip-slashes
process-route)))

(defn match-path-with-route [route url]
(let [matches (first (re-seq (:regex route) url))]
(when-not (nil? matches)
(zipmap (:placeholders route) (rest matches)))))

(defn match-path [processed-routes path]
(let [route-count (count processed-routes)
max-index (dec route-count)]
(if (pos? route-count)
(loop [index 0]
(let [route (get processed-routes index)
matches (match-path-with-route route path)
end? (= max-index index)]
(cond
matches {:route (:route route)
:data (merge (:defaults route)
(remove-empty-matches matches))}
end? nil
:else (recur (inc index))))))))

(defn url->map [processed-routes url]
(let [parsed-url (u/url url)
path (strip-slashes (:path parsed-url))
query (remove-empty-matches (keywordize-keys (or (:query parsed-url) {})))
;; Try to match a normal url or a url with "/" appended
;; that way we ensure that routes with defaults get matched
;; even if the last segment is missing
matched-path (or (match-path processed-routes path)
(match-path processed-routes (str path "/")))]
(if matched-path
(assoc matched-path :data (merge query (:data matched-path)))
{:data query})))

(defn map->url [processed-routes url])

(defn expand-routes [routes]
;; sort routes in desc order by count of placeholders
(into [] (sort-by (fn [r]
(- (count (:placeholders r))))
(map expand-route routes))))

7 changes: 7 additions & 0 deletions test/ashiba/test/app_state.cljs
@@ -0,0 +1,7 @@
(ns ashiba.test.app-state
(:require [cljs.test :refer-macros [deftest is]]
[ashiba.app-state :as app-state]))




8 changes: 8 additions & 0 deletions test/ashiba/test/core.cljs
@@ -0,0 +1,8 @@
(ns ashiba.test.core
(:require [doo.runner :refer-macros [doo-tests]]
[cljs.test :as test]
[ashiba.test.app-state]
[ashiba.test.router]))

(doo-tests ;;'ashiba.test.app-state
'ashiba.test.router)

0 comments on commit f165b41

Please sign in to comment.