Usher
Tree-based router library. Useful for (specifically) for Rails and Rack, but probably generally useful for anyone interested in doing routing. Based on Ilya Grigorik suggestion, turns out looking up in a hash and following a tree is faster than Krauter’s massive regex approach.
Features
-
Understands single and path-globbing variables
-
Understands arbitrary regex variables
-
Arbitrary HTTP header requirements
-
No optimization phase, so routes are always alterable after the fact
-
Understands Proc and Regex transformations, validations
-
Really, really fast
-
Relatively light and happy code-base, should be easy and fun to alter (it hovers around 1,000 LOC, 800 for the core)
-
Interface and implementation are separate, encouraging cross-pollination
-
Works in 1.9!
Route format
From the rdoc:
Creates a route from path
and options
path
A path consists a mix of dynamic and static parts delimited by /
Dynamic
Dynamic parts are prefixed with either :, *. :variable matches only one part of the path, whereas *variable can match one or more parts.
Example: /path/:variable/path
would match
-
/path/test/path
-
/path/something_else/path
-
/path/one_more/path
In the above examples, ‘test’, ‘something_else’ and ‘one_more’ respectively would be bound to the key :variable
. However, /path/test/one_more/path
would not be matched.
Example: /path/*variable/path
would match
-
/path/one/two/three/path
-
/path/four/five/path
In the above examples, [‘one’, ‘two’, ‘three’] and [‘four’, ‘five’] respectively would be bound to the key :variable.
As well, variables can have a regex matcher.
Example: /product/{:id,\d+}
would match
-
/product/123
-
/product/4521
But not
-
/product/AE-35
As well, the same logic applies for * variables as well, where only parts matchable by the supplied regex will actually be bound to the variable
Variables can also have a greedy regex matcher. These matchers ignore all delimiters, and continue matching for as long as much as their regex allows.
Example: /product/{!id,hello/world|hello}
would match
-
/product/hello/world
-
/product/hello
Static
Static parts of literal character sequences. For instance, /path/something.html
would match only the same path. As well, static parts can have a regex pattern in them as well, such as /path/something.{html|xml}
which would match only /path/something.html
and /path/something.xml
Optional sections
Sections of a route can be marked as optional by surrounding it with brackets. For instance, in the above static example, /path/something(.html)
would match both /path/something
and /path/something.html
.
One and only one sections
Sections of a route can be marked as “one and only one” by surrounding it with brackets and separating parts of the route with pipes. For instance, the path, /path/something(.xml|.html)
would only match /path/something.xml
and /path/something.html
. Generally its more efficent to use one and only sections over using regex.
options
-
requirements
- After transformation, tests the condition using ===. If it returns false, it raises anUsher::ValidationException
-
conditions
- Accepts any of therequest_methods
specificied in the construction of Usher. This can be either astring
or a regular expression. -
Any other key is interpreted as a requirement for the variable of its name.
Rails
script/plugin install git://github.com/joshbuddy/usher.git
Rack
config.ru
require 'usher'
app = proc do |env|
body = "Hi there #{env['usher.params'][:name]}"
[
200, # Status code
{ # Response headers
'Content-Type' => 'text/plain',
'Content-Length' => body.size.to_s,
},
[body] # Response body
]
end
routes = Usher::Interface.for(:rack) do
add('/hello/:name').to(app)
end
run routes
>> curl http://127.0.0.1:3000/hello/samueltanders
<< Hi there samueltanders
DONE
-
add support for () optional parts
-
Add support for arbitrary HTTP header checks
-
Emit exceptions inline with relevant interfaces
-
More RDoc! (optionally cowbell)
TODO
-
Make it integrate with merb
-
Make it integrate with rails3
-
Create decent DSL for use with rack
(Let me show you to your request)