Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2f7ea75
Showing
7 changed files
with
272 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
((clojure-mode . ((clojure-swank-command . "lein2 jack-in %s")))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
pom.xml | ||
*jar | ||
/lib/ | ||
/classes/ | ||
.lein-failures | ||
.lein-deps-sum | ||
TAGS | ||
checkouts/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
language: clojure | ||
lein: lein2 | ||
script: lein2 all test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# What is Route One | ||
|
||
Route One is a tiny Clojure library that generates HTTP resource routes (as in Ruby on Rails, Jersey, Noir and similar | ||
modern Web application frameworks). It is meant to be used in HTTP clients, programs that deliver emails with links, | ||
testing environments and so on. | ||
|
||
Route One is intentionally small and has very limited feature scope. | ||
|
||
|
||
## Documentation & Examples | ||
|
||
``` clojure | ||
(ns my.app | ||
(:use [clojurewerkz.route-one.core :only [route-map route path-for named-path]])) | ||
|
||
;; define a route map | ||
(route-map | ||
(route "/about" :named "about page") | ||
(route "/faq") | ||
(route "/help" :named "help page") | ||
(route "/docs/:title" :named "documents")) | ||
|
||
;; generate relative paths | ||
(path-for "/docs/title" { :title "ohai" }) ;; => "/docs/title" | ||
(path-for "/docs/:title" { :title "ohai" }) ;; => "/docs/ohai" | ||
(path-for "/docs/:category/:title" { :category "greetings" :title "ohai" }) ;; => "/docs/greetings/ohai" | ||
(path-for "/docs/:category/:title" { :category "greetings" }) ;; => IllegalArgumentException, because :title value is missing | ||
|
||
(named-path "about page") ;; => "/about" | ||
(named-path "documents" {:title "a-title"}) ;; => "/docs/a-title" | ||
|
||
(with-base-url "https://myservice.com" | ||
(url-for "/docs/title" { :title "ohai" }) ;; => "https://myservice.com/docs/title" | ||
(url-for "/docs/:title" { :title "ohai" }) ;; => "https://myservice.com/docs/ohai" | ||
(url-for "/docs/:category/:title" { :category "greetings" :title "ohai" }) ;; => "https://myservice.com/docs/greetings/ohai" | ||
(url-for "/docs/:category/:title" { :category "greetings" }) ;; => IllegalArgumentException, because :title value is missing | ||
|
||
(named-url "about page") ;; => "https://myservice.com/about" | ||
(named-url "documents" {:title "a-title"}) ;; => "https://myservice.com/docs/a-title" | ||
) | ||
``` | ||
|
||
Documentation site for Urly is coming in the future (sorry!). Please see our test suite for more code examples. | ||
|
||
|
||
## Maven Artifacts | ||
|
||
### The Most Recent Release | ||
|
||
With Leiningen: | ||
|
||
[clojurewerkz/route-one "1.0.0-beta1"] | ||
|
||
With Maven: | ||
|
||
<dependency> | ||
<groupId>clojurewerkz</groupId> | ||
<artifactId>route-one</artifactId> | ||
<version>1.0.0-beta1</version> | ||
</dependency> | ||
|
||
|
||
## Supported Clojure versions | ||
|
||
Route One is built from the ground up for Clojure 1.3+ and JDK 6+. | ||
|
||
|
||
## Route One Is a ClojureWerkz Project | ||
|
||
Route One is part of the group of libraries known as ClojureWerkz, together with | ||
[Neocons](https://github.com/michaelklishin/neocons), [Monger](https://github.com/michaelklishin/monger), [Langohr](https://github.com/michaelklishin/langohr), [Elastisch](https://github.com/clojurewerkz/elastisch), [Quartzite](https://github.com/michaelklishin/quartzite), [Urly](https://github.com/michaelklishin/urly) and several others. | ||
|
||
|
||
## Continuous Integration | ||
|
||
[![Continuous Integration status](https://secure.travis-ci.org/clojurewerkz/route-one.png)](http://travis-ci.org/clojurewerkz/route-one) | ||
|
||
CI is hosted by [travis-ci.org](http://travis-ci.org) | ||
|
||
|
||
## Development | ||
|
||
Route One uses [Leiningen 2](https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md). Make sure you have it installed and then run tests against Clojure 1.3.0 and 1.4.0[-beta6] using | ||
|
||
lein2 all test | ||
|
||
Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit | ||
a pull request on Github. | ||
|
||
|
||
|
||
## License | ||
|
||
Copyright © 2012 Michael S. Klishin, Alex Petrov | ||
|
||
Distributed under the Eclipse Public License, the same as Clojure. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
(defproject clojurewerkz/route-one "1.0.0-beta1" | ||
:description "A tiny URL/route generation library" | ||
:url "http://github.com/clojurewerkz/route-one" | ||
:license {:name "Eclipse Public License" | ||
:url "http://www.eclipse.org/legal/epl-v10.html"} | ||
:dependencies [[org.clojure/clojure "1.3.0"] | ||
[clojurewerkz/urly "1.0.0-rc2"]] | ||
:source-paths ["src/clojure"] | ||
:profiles {:1.4 {:dependencies [[org.clojure/clojure "1.4.0-beta6"]]}} | ||
:aliases { "all" ["with-profile" "dev:dev,1.4"] } | ||
:repositories {"clojure-releases" "http://build.clojure.org/releases", | ||
"sonatype" {:url "http://oss.sonatype.org/content/repositories/releases", | ||
:snapshots false, | ||
:releases {:checksum :fail, :update :always}}} | ||
:warn-on-reflection true) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
(ns clojurewerkz.route-one.core | ||
(:import clojurewerkz.urly.UrlLike) | ||
(:use [clojure.string :only [split join]] | ||
[clojurewerkz.urly.core :only [url-like]])) | ||
|
||
;; | ||
;; Implementation | ||
;; | ||
|
||
(defrecord Route [path name opts]) | ||
|
||
(def route-maps (atom [])) | ||
(def empty-route-map []) | ||
|
||
|
||
(def ^{:constant true :private true} | ||
slash-re #"/") | ||
(def ^{:constant true :private true} | ||
slash "/") | ||
|
||
(defn- replace-segment | ||
[^String s data] | ||
(if (.startsWith s ":") | ||
(if-let [v (get data (keyword (.substring s 1)))] | ||
v | ||
(throw (IllegalArgumentException. (str "Segment " s " is not found in the map " data)))) | ||
s)) | ||
|
||
(defn replace-segments | ||
"Replaces segments that start with a colon with respective values from the data map. | ||
Example: (\"/docs/title\" { :title \"ohai\" }) ;; => \"/docs/title\"" | ||
[^String s data] | ||
(let [parts (split s slash-re)] | ||
(join slash (map #(replace-segment % data) parts)))) | ||
|
||
|
||
;; | ||
;; API | ||
;; | ||
|
||
(def ^{:dynamic true} *base-url*) | ||
|
||
(defn route | ||
"Defines an individual route. | ||
Example: | ||
(route \"/about\" :named \"about page\") | ||
(route \"/faq\") | ||
(route \"/help\" :named \"help page\") | ||
(route \"/docs/:title\" :named \"documents\")" | ||
[v path &{ :as opts }] | ||
(conj v (Route. path (:named opts) opts))) | ||
|
||
(defmacro route-map | ||
"Defines a route map. | ||
Example: | ||
(route-map | ||
(route \"/about\" :named \"about page\") | ||
(route \"/faq\") | ||
(route \"/help\" :named \"help page\") | ||
(route \"/docs/:title\" :named \"documents\"))" | ||
[& routes] | ||
`(let [coll# (-> empty-route-map ~@routes)] | ||
(reset! route-maps coll#))) | ||
|
||
(defn path-for | ||
"Generates a regular path, replacing segments that start with a colon with respective values from the data map" | ||
[^String s data] | ||
(replace-segments s data)) | ||
|
||
(defn named-path | ||
"Like path-for but generates named paths" | ||
([^String s] | ||
(if-let [p (some (fn [r] (when (= s (:name r)) | ||
r)) @route-maps)] | ||
(:path p) | ||
(throw (IllegalArgumentException. (str "Route with name " s " is not found"))))) | ||
([^String s data] | ||
(let [path (named-path s)] | ||
(replace-segments path data)))) | ||
|
||
|
||
(defmacro with-base-url | ||
[s & body] | ||
`(binding [*base-url* ~s] | ||
~@body)) | ||
|
||
(defn url-for | ||
"Like path-for but generates full URLs. Use together with with-base-url." | ||
[^String s data] | ||
(str (.mutatePath (url-like *base-url*) (path-for s data)))) | ||
|
||
(defn named-url | ||
"Like url-for but generates named paths. Use together with with-base-url." | ||
([^String s] | ||
(str (.mutatePath ^UrlLike (url-like *base-url*) (named-path s)))) | ||
([^String s data] | ||
(str (.mutatePath ^UrlLike (url-like *base-url*) (named-path s data))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
(ns clojurewerkz.route-one.core-test | ||
(:use clojure.test | ||
clojurewerkz.route-one.core)) | ||
|
||
|
||
(route-map | ||
(route "/about" :named "about page") | ||
(route "/faq") | ||
(route "/help" :named "help page") | ||
(route "/docs/:title" :named "documents")) | ||
|
||
|
||
(deftest test-replace-segments | ||
(testing "cases with all segments present in the data map" | ||
(are [path data output] (is (= output (replace-segments path data))) | ||
"/docs/title" { :title "ohai" } "/docs/title" | ||
"/docs/:title" { :title "ohai" } "/docs/ohai" | ||
"/docs/:category/:title" { :category "greetings" :title "ohai" } "/docs/greetings/ohai")) | ||
(testing "cases with some segments missing from the data map" | ||
(are [path data] (is (thrown? IllegalArgumentException (replace-segments path data))) | ||
"/docs/:title" { :greeting "ohai" } | ||
"/docs/:category/:title" { :title "ohai" }))) | ||
|
||
(deftest test-path-generation | ||
(testing "generation of routes w/o segments" | ||
(is (= "/about" (path-for "/about" {}))) | ||
(is (= "/about/project" (path-for "/about/project" {})))) | ||
(testing "generation of routes with segments" | ||
(is (= "/clojurewerkz/route-one" (path-for "/:organization/:project" {:organization "clojurewerkz" :project "route-one"})))) | ||
(testing "generation of named routes w/o segments" | ||
(is (= "/about" (named-path "about page")))) | ||
(testing "generation of named routes with segments" | ||
(is (= "/docs/a-title" (named-path "documents" {:title "a-title"}))))) | ||
|
||
(deftest test-url-generation | ||
(with-base-url "http://giove.local" | ||
(testing "generation of routes w/o segments" | ||
(is (= "http://giove.local/about" (url-for "/about" {}))) | ||
(is (= "http://giove.local/about/project" (url-for "/about/project" {}))))) | ||
;; really broken input | ||
(with-base-url "HTTP://https://API.MYAPP.COM/" | ||
(testing "generation of routes with segments" | ||
(is (= "https://api.myapp.com/clojurewerkz/route-one" (url-for "/:organization/:project" {:organization "clojurewerkz" :project "route-one"})))) | ||
(testing "generation of named routes w/o segments" | ||
(is (= "https://api.myapp.com/about" (named-url "about page")))) | ||
(testing "generation of named routes with segments" | ||
(is (= "https://api.myapp.com/docs/a-title" (named-url "documents" {:title "a-title"})))))) |