simple_router 0.9.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/LICENSE +19 -0
- data/Manifest +17 -0
- data/README.md +81 -0
- data/Rakefile +30 -0
- data/TODO +1 -0
- data/examples/rack_app.ru +45 -0
- data/lib/simple_router.rb +13 -0
- data/lib/simple_router/dsl.rb +57 -0
- data/lib/simple_router/engines/simple_engine.rb +102 -0
- data/lib/simple_router/routes.rb +36 -0
- data/simple_router.gemspec +17 -0
- data/test/engines/test_simple_engine.rb +145 -0
- data/test/test_dsl.rb +45 -0
- data/test/test_helper.rb +17 -0
- data/test/test_routes.rb +63 -0
- data/test/test_simple_router.rb +25 -0
- metadata +100 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright © 2009 Martin Aumont (mynyml)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
.gitignore
|
2
|
+
LICENSE
|
3
|
+
Manifest
|
4
|
+
README.md
|
5
|
+
Rakefile
|
6
|
+
TODO
|
7
|
+
examples/rack_app.ru
|
8
|
+
lib/simple_router.rb
|
9
|
+
lib/simple_router/dsl.rb
|
10
|
+
lib/simple_router/engines/simple_engine.rb
|
11
|
+
lib/simple_router/routes.rb
|
12
|
+
simple_router.gemspec
|
13
|
+
test/engines/test_simple_engine.rb
|
14
|
+
test/test_dsl.rb
|
15
|
+
test/test_helper.rb
|
16
|
+
test/test_routes.rb
|
17
|
+
test/test_simple_router.rb
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
Summary
|
3
|
+
-------
|
4
|
+
Small and simple standalone router, meant for use with Rack applications.
|
5
|
+
|
6
|
+
Features
|
7
|
+
--------
|
8
|
+
* Familiar Sinatra-like DSL for defining actions
|
9
|
+
* Modular architecture (see ROUTING ENGINES section)
|
10
|
+
* No magic
|
11
|
+
* Low level / Highly flexible
|
12
|
+
* Simple code
|
13
|
+
|
14
|
+
Examples
|
15
|
+
--------
|
16
|
+
|
17
|
+
class App
|
18
|
+
include SimpleRouter::DSL
|
19
|
+
|
20
|
+
get '/' do
|
21
|
+
'home'
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/archives/:year/:month/:day' do |year, month, day|
|
25
|
+
Article.archived.find_by_date(year, month, day)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(env)
|
29
|
+
verb, path = ENV['REQUEST_METHOD'], ENV['PATH_INFO']
|
30
|
+
route = self.class.routes.match(verb, path)
|
31
|
+
route.nil? ?
|
32
|
+
[404, {'Content-Type' => 'text/html'}, '404 page not found'] :
|
33
|
+
[200, {'Content-Type' => 'text/html'}, route.action.call(*route.values)]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
The SimpleRouter::DSL mixin provides 5 class methods:
|
38
|
+
|
39
|
+
#get, #post, #put, #delete and #routes
|
40
|
+
|
41
|
+
The four verb methods allow you to define actions that will match a request for
|
42
|
+
the verb and route signature it defines.
|
43
|
+
|
44
|
+
The #routes method responds to #match and will return the first matching route
|
45
|
+
(in order they were defined), and the values of the variables defined in the
|
46
|
+
route signature if any (:year, :month, ...).
|
47
|
+
|
48
|
+
Finally, the route returned is a simple object: for the second route in the
|
49
|
+
example above:
|
50
|
+
|
51
|
+
route.verb #=> :get
|
52
|
+
route.path #=> '/archives/:year/:month/:day'
|
53
|
+
route.action #=> lambda {|year, month, day| Article.archived.find_by_date(year, month, day) }
|
54
|
+
route.values #=> ['2008', '07', '01'] # after being matched with path '/archives/2008/07/01'
|
55
|
+
|
56
|
+
See examples/ directory for executable code examples.
|
57
|
+
|
58
|
+
Routing Engines
|
59
|
+
---------------
|
60
|
+
By design, the code is seperated in two main components:
|
61
|
+
|
62
|
+
o DSL for defining routes/actions and retrieving requested action block.
|
63
|
+
o Routing engine
|
64
|
+
|
65
|
+
Different engines can be used by assigning them to the router:
|
66
|
+
|
67
|
+
SimpleRouter.engine = UltraFastEngine
|
68
|
+
|
69
|
+
This allows, for instance, a new faster engine to be used by an app without
|
70
|
+
changes in the top level DSL.
|
71
|
+
|
72
|
+
Engines can also allow route definitions to include more (or less!) features.
|
73
|
+
For example, the built-in SimpleEngine allows route variables and extension
|
74
|
+
handling. Others could add variable conditions ( :id => Integer ), mime-type
|
75
|
+
restrictions, etc.
|
76
|
+
|
77
|
+
#### Engine Writers
|
78
|
+
|
79
|
+
Engines only need to conform to a simple interface. See
|
80
|
+
lib/simple_router/engines/simple_engine.rb for details.
|
81
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
def gem_opt
|
2
|
+
defined?(Gem) ? "-rubygems" : ""
|
3
|
+
end
|
4
|
+
|
5
|
+
def ruby(path)
|
6
|
+
path = "-e'%w( #{path.join(' ')} ).each {|p| require p }'" if path.is_a?(Array)
|
7
|
+
system "ruby #{gem_opt} -I.:lib:test #{path}"
|
8
|
+
end
|
9
|
+
|
10
|
+
# --------------------------------------------------
|
11
|
+
# Tests
|
12
|
+
# --------------------------------------------------
|
13
|
+
task(:default => :test)
|
14
|
+
|
15
|
+
desc "Run tests"
|
16
|
+
task(:test) do
|
17
|
+
exit ruby( Dir['test/**/test_*.rb'] - ['test/test_helper.rb'] )
|
18
|
+
end
|
19
|
+
|
20
|
+
# --------------------------------------------------
|
21
|
+
# Docs
|
22
|
+
# --------------------------------------------------
|
23
|
+
desc "Generate YARD Documentation"
|
24
|
+
task(:yardoc) do
|
25
|
+
require 'yard'
|
26
|
+
files = %w( lib/**/*.rb )
|
27
|
+
options = %w( -o doc/yard --readme README.md --files LICENSE )
|
28
|
+
YARD::CLI::Yardoc.run *(options + files)
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
root = Pathname(__FILE__).dirname.parent.expand_path
|
3
|
+
$:.unshift(root.join('lib'))
|
4
|
+
|
5
|
+
# run me with:
|
6
|
+
# $rackup examples/rack_app.ru
|
7
|
+
# --------------------------------------------------
|
8
|
+
require 'rubygems'
|
9
|
+
require 'simple_router'
|
10
|
+
|
11
|
+
class App
|
12
|
+
include SimpleRouter::DSL
|
13
|
+
|
14
|
+
get '/' do |params|
|
15
|
+
<<-html
|
16
|
+
<pre>
|
17
|
+
params: #{params.inspect}
|
18
|
+
</pre>
|
19
|
+
html
|
20
|
+
end
|
21
|
+
|
22
|
+
get '/:foo.:type' do |foo, type, params|
|
23
|
+
<<-html
|
24
|
+
<pre>
|
25
|
+
foo: #{foo.inspect}
|
26
|
+
type: #{type.inspect}
|
27
|
+
params: #{params.inspect}
|
28
|
+
</pre>
|
29
|
+
html
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
request = Rack::Request.new(env)
|
34
|
+
|
35
|
+
verb = request.request_method.downcase.to_sym
|
36
|
+
path = Rack::Utils.unescape(request.path_info)
|
37
|
+
|
38
|
+
route = self.class.routes.match(verb, path)
|
39
|
+
route.nil? ?
|
40
|
+
[404, {'Content-Type' => 'text/html'}, '404 page not found'] :
|
41
|
+
[200, {'Content-Type' => 'text/html'}, [route.action.call(*route.values.push(request.params))]]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
run App.new
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SimpleRouter
|
2
|
+
class << self
|
3
|
+
attr_accessor :engine
|
4
|
+
def engine() @engine || Engines::SimpleEngine end
|
5
|
+
end
|
6
|
+
|
7
|
+
autoload :DSL, 'simple_router/dsl'
|
8
|
+
autoload :Routes, 'simple_router/routes'
|
9
|
+
|
10
|
+
module Engines
|
11
|
+
autoload :SimpleEngine, 'simple_router/engines/simple_engine'
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module SimpleRouter
|
2
|
+
|
3
|
+
# Mixin that provides a Sinatra-line DSL frontend to the routing engine
|
4
|
+
# backend.
|
5
|
+
#
|
6
|
+
# Meant to be minimal, simple and as magic-free as possible.
|
7
|
+
# When mixed in, only adds 5 class methods (#get, #post, #put, #delete and #routes).
|
8
|
+
#
|
9
|
+
# ==== Examples
|
10
|
+
# # simple rack app
|
11
|
+
#
|
12
|
+
# class App
|
13
|
+
# include SimpleRouter::DSL
|
14
|
+
#
|
15
|
+
# get '/' do
|
16
|
+
# 'home'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# get '/users/:id' do |id|
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# put '/:foo/:bar' do |foo, bar, *params|
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def call(env)
|
26
|
+
# request = Rack::Request.new(env)
|
27
|
+
#
|
28
|
+
# verb = request.request_method
|
29
|
+
# path = Rack::Utils.unescape(request.path_info)
|
30
|
+
#
|
31
|
+
# action = self.class.routes.match(verb, path).action
|
32
|
+
# action.nil? ? [404, {}, []] : [200, {}, [action.call]]
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# ==== Notes
|
37
|
+
# Because the DSL is a simple mixin, it can be used in any class (i.e. not
|
38
|
+
# necessarily a rack app).
|
39
|
+
#
|
40
|
+
module DSL
|
41
|
+
def self.included(base)
|
42
|
+
base.extend(ClassMethods)
|
43
|
+
base.class_eval do
|
44
|
+
@routes = Routes.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
attr_reader :routes
|
50
|
+
|
51
|
+
def get( path, opts={}, &block) routes.add(:get, path, opts, &block) end
|
52
|
+
def post( path, opts={}, &block) routes.add(:post, path, opts, &block) end
|
53
|
+
def put( path, opts={}, &block) routes.add(:put, path, opts, &block) end
|
54
|
+
def delete(path, opts={}, &block) routes.add(:delete, path, opts, &block) end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module SimpleRouter
|
4
|
+
module Engines
|
5
|
+
module SimpleEngine #:nodoc:
|
6
|
+
|
7
|
+
def self.match(*args)
|
8
|
+
Base.match(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Base
|
12
|
+
# Finds a route definition that matches a path
|
13
|
+
#
|
14
|
+
# ===== Arguments
|
15
|
+
# * path: actual path to match (e.g. ENV['PATH_INFO'])
|
16
|
+
# * routes: array of 'routes', where each route is composed of [pattern, options]. If route isn't an array, an empty options hash is assumed
|
17
|
+
#
|
18
|
+
# Currently, this engine implementation ignores route options.
|
19
|
+
#
|
20
|
+
# ===== Returns
|
21
|
+
# Array of two elements:
|
22
|
+
#
|
23
|
+
# * index 0: first matching route
|
24
|
+
# * index 1: array of values for the matched route's variables (in the order they were specified in the route)
|
25
|
+
#
|
26
|
+
# ===== Examples
|
27
|
+
#
|
28
|
+
# SimpleEngine.match('/foo', ['/', '/foo', '/bar/baz']) #=> ['/foo', []]
|
29
|
+
# SimpleEngine.match('/80/07/01', ['/:year/:month/:day']) #=> ['/foo', ['80', '07', '01']]
|
30
|
+
#
|
31
|
+
def self.match(path, routes)
|
32
|
+
path = Path.new(path)
|
33
|
+
patterns = routes.map {|route| Pattern.new(Array(route).first) }
|
34
|
+
|
35
|
+
patterns.each do |pattern|
|
36
|
+
return [pattern.to_s, pattern.vars] if pattern == path
|
37
|
+
end
|
38
|
+
|
39
|
+
[nil, []]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Path #:nodoc:
|
44
|
+
attr_accessor :parts, :ext
|
45
|
+
|
46
|
+
def initialize(path)
|
47
|
+
self.parts, self.ext = split_path(path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
'/' + self.parts.join('/') + self.ext
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def split_path(path)
|
56
|
+
path = path.to_s
|
57
|
+
ext = Pathname(path).extname
|
58
|
+
path = path.sub(/#{ext}$/,'')
|
59
|
+
parts = path.split('/').reject {|part| part.empty? }
|
60
|
+
[parts, ext]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Pattern < Path #:nodoc:
|
65
|
+
|
66
|
+
def variables
|
67
|
+
return [] unless @match
|
68
|
+
|
69
|
+
a = []
|
70
|
+
self.parts.each_with_index do |part,i|
|
71
|
+
a << @match.parts[i] if part[0] == ?:
|
72
|
+
end
|
73
|
+
a << @match.ext[1..-1] if self.ext[1] == ?:
|
74
|
+
a
|
75
|
+
end
|
76
|
+
alias :vars :variables
|
77
|
+
|
78
|
+
def ==(path)
|
79
|
+
is_match = size_match?(path) && ext_match?(path) && static_match?(path)
|
80
|
+
@match = path if is_match
|
81
|
+
is_match
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def size_match?(path)
|
86
|
+
self.parts.size == path.parts.size
|
87
|
+
end
|
88
|
+
|
89
|
+
def ext_match?(path)
|
90
|
+
(self.ext == path.ext) || (self.ext[1] == ?: && !path.ext.empty?)
|
91
|
+
end
|
92
|
+
|
93
|
+
def static_match?(path)
|
94
|
+
self.parts.each_with_index do |part,i|
|
95
|
+
return false unless part[0] == ?: || path.parts[i] == part
|
96
|
+
end
|
97
|
+
true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SimpleRouter
|
2
|
+
class Routes < Array
|
3
|
+
|
4
|
+
def add(*args, &action)
|
5
|
+
self << Route.new(*args, &action)
|
6
|
+
end
|
7
|
+
|
8
|
+
def match(verb, path)
|
9
|
+
return nil if self.empty?
|
10
|
+
|
11
|
+
verb = verb.to_s.downcase.strip.to_sym
|
12
|
+
|
13
|
+
routes = self.select {|route| route.verb == verb }
|
14
|
+
paths = routes.map {|route| route.path }
|
15
|
+
|
16
|
+
path, values = SimpleRouter.engine.match(path, paths)
|
17
|
+
return nil if path.nil?
|
18
|
+
|
19
|
+
route = routes.detect {|route| route.path == path }
|
20
|
+
route.values = values
|
21
|
+
route
|
22
|
+
end
|
23
|
+
|
24
|
+
class Route
|
25
|
+
attr_accessor :verb,:path,:options,:action, :values
|
26
|
+
|
27
|
+
def initialize(verb, path, options, &action)
|
28
|
+
self.verb = verb
|
29
|
+
self.path = path
|
30
|
+
self.options = options
|
31
|
+
self.action = action
|
32
|
+
self.values = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'simple_router'
|
3
|
+
s.version = "0.9.8.1"
|
4
|
+
s.summary = "Simple, minimalistic, standalone router meant to be used with pure rack applications"
|
5
|
+
s.description = "Simple, minimalistic, standalone router meant to be used with pure rack applications."
|
6
|
+
s.author = "mynyml"
|
7
|
+
s.email = "mynyml@gmail.com"
|
8
|
+
s.homepage = "http://github.com/mynyml/simple_router"
|
9
|
+
s.rubyforge_project = "simple_router"
|
10
|
+
s.has_rdoc = true
|
11
|
+
s.require_path = "lib"
|
12
|
+
s.files = File.read("Manifest").strip.split("\n")
|
13
|
+
|
14
|
+
s.add_development_dependency 'rack'
|
15
|
+
s.add_development_dependency 'nanotest'
|
16
|
+
s.add_development_dependency 'nanotest_extensions'
|
17
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
## SimpleEngine
|
4
|
+
context do
|
5
|
+
include SimpleRouter::Engines::SimpleEngine
|
6
|
+
|
7
|
+
# matches static paths
|
8
|
+
test do
|
9
|
+
Base.match('/', ['/', '/foo']).first.must == '/'
|
10
|
+
Base.match('/foo', ['/', '/foo']).first.must == '/foo'
|
11
|
+
Base.match('/bar', ['/', '/foo']).first.must == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
# matches variable paths
|
15
|
+
test do
|
16
|
+
path, vars = Base.match('/80/07', ['/foo', '/:year/:month'])
|
17
|
+
path.must == '/:year/:month'
|
18
|
+
vars.must == ['80','07']
|
19
|
+
end
|
20
|
+
|
21
|
+
# matches hybrid paths
|
22
|
+
test do
|
23
|
+
path, vars = Base.match('/archives/80/07', ['/foo', '/archives/:year/:month'])
|
24
|
+
path.must == '/archives/:year/:month'
|
25
|
+
vars.must == ['80','07']
|
26
|
+
end
|
27
|
+
|
28
|
+
# ignores leading slash in path
|
29
|
+
test do
|
30
|
+
path, vars = Base.match('archives/80/07', ['/foo', '/archives/:year/:month'])
|
31
|
+
path.must == '/archives/:year/:month'
|
32
|
+
vars.must == ['80','07']
|
33
|
+
end
|
34
|
+
|
35
|
+
# no matches
|
36
|
+
test do
|
37
|
+
path, vars = Base.match('/80/07/01', ['/foo', '/:year/:month'])
|
38
|
+
path.must == nil
|
39
|
+
vars.must == []
|
40
|
+
end
|
41
|
+
|
42
|
+
# treats extension as pattern part
|
43
|
+
test do
|
44
|
+
path, vars = Base.match('/a/b.xml', ['/:foo/:bar', '/:foo/:bar.:type'])
|
45
|
+
path.must == '/:foo/:bar.:type'
|
46
|
+
vars.must == ['a','b','xml']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
## Pattern
|
51
|
+
context do
|
52
|
+
include SimpleRouter::Engines::SimpleEngine
|
53
|
+
|
54
|
+
# static pattern matches a path
|
55
|
+
test do
|
56
|
+
path = Path.new('/foo/bar')
|
57
|
+
pattern = Pattern.new('/foo/bar')
|
58
|
+
|
59
|
+
assert { pattern == path }
|
60
|
+
end
|
61
|
+
|
62
|
+
# variable pattern matches a path
|
63
|
+
test do
|
64
|
+
path = Path.new('/foo/bar')
|
65
|
+
pattern = Pattern.new('/:foo/:bar')
|
66
|
+
|
67
|
+
assert { pattern == path }
|
68
|
+
end
|
69
|
+
|
70
|
+
# pattern variables
|
71
|
+
test do
|
72
|
+
path = Path.new('/foo/bar/baz')
|
73
|
+
pattern = Pattern.new('/:a/:b/:c')
|
74
|
+
|
75
|
+
assert { pattern == path }
|
76
|
+
pattern.vars.must == %w( foo bar baz )
|
77
|
+
end
|
78
|
+
|
79
|
+
# pattern variables with extension
|
80
|
+
test do
|
81
|
+
path = Path.new('/foo/bar/baz.xml')
|
82
|
+
pattern = Pattern.new('/:a/:b/:c.:type')
|
83
|
+
|
84
|
+
assert { pattern == path }
|
85
|
+
pattern.vars.must == %w( foo bar baz xml )
|
86
|
+
end
|
87
|
+
|
88
|
+
# variable pattern matches a path with static extension
|
89
|
+
test do
|
90
|
+
path = Path.new('/foo/bar.xml')
|
91
|
+
pattern = Pattern.new('/:foo/:bar.xml')
|
92
|
+
|
93
|
+
assert { pattern == path }
|
94
|
+
end
|
95
|
+
|
96
|
+
# variable pattern matches a path with variable extension
|
97
|
+
test do
|
98
|
+
path = Path.new('/foo/bar.xml')
|
99
|
+
pattern = Pattern.new('/:foo/:bar.:type')
|
100
|
+
|
101
|
+
assert { pattern == path }
|
102
|
+
end
|
103
|
+
|
104
|
+
# pattern without extension doesn't match path with extension
|
105
|
+
test do
|
106
|
+
path = Path.new('/foo/bar.xml')
|
107
|
+
pattern = Pattern.new('/:foo/:bar')
|
108
|
+
|
109
|
+
assert { pattern != path }
|
110
|
+
end
|
111
|
+
|
112
|
+
# pattern with static extension doesn't match path without extension
|
113
|
+
test do
|
114
|
+
path = Path.new('/foo/bar')
|
115
|
+
pattern = Pattern.new('/:foo/:bar.xml')
|
116
|
+
|
117
|
+
assert { pattern != path }
|
118
|
+
end
|
119
|
+
|
120
|
+
# pattern with variable extension doesn't match path without extension
|
121
|
+
test do
|
122
|
+
path = Path.new('/foo/bar')
|
123
|
+
pattern = Pattern.new('/:foo/:bar.:type')
|
124
|
+
|
125
|
+
assert { pattern != path }
|
126
|
+
end
|
127
|
+
|
128
|
+
# doesn't ignore dots in path parts
|
129
|
+
test do
|
130
|
+
path = Path.new('/foo/bar.baz/abc')
|
131
|
+
pattern = Pattern.new('/:a/:b/:c')
|
132
|
+
|
133
|
+
assert { pattern == path }
|
134
|
+
pattern.variables.must == %w( foo bar.baz abc )
|
135
|
+
end
|
136
|
+
|
137
|
+
# doesn't get confused with extension when path contains other dots
|
138
|
+
test do
|
139
|
+
path = Path.new('/foo/bar.baz/abc.xml')
|
140
|
+
pattern = Pattern.new('/:a/:b/:c.:type')
|
141
|
+
|
142
|
+
assert { pattern == path }
|
143
|
+
pattern.variables.must == %w( foo bar.baz abc xml )
|
144
|
+
end
|
145
|
+
end
|
data/test/test_dsl.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class App
|
4
|
+
include SimpleRouter::DSL
|
5
|
+
end
|
6
|
+
|
7
|
+
context do
|
8
|
+
|
9
|
+
setup do
|
10
|
+
SimpleRouter.engine = SimpleRouter::Engines::SimpleEngine
|
11
|
+
end
|
12
|
+
|
13
|
+
teardown do
|
14
|
+
App.routes.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
## API
|
18
|
+
|
19
|
+
# provides action verb methods
|
20
|
+
test do
|
21
|
+
App.get( '/foo') {}
|
22
|
+
App.post( '/foo') {}
|
23
|
+
App.put( '/foo') {}
|
24
|
+
App.delete('/foo') {}
|
25
|
+
|
26
|
+
App.routes.size.must == 4
|
27
|
+
end
|
28
|
+
|
29
|
+
# provides routes object
|
30
|
+
test do
|
31
|
+
App.respond_to?(:routes).must == true
|
32
|
+
App.routes.must.kind_of?(SimpleRouter::Routes)
|
33
|
+
end
|
34
|
+
|
35
|
+
## matching
|
36
|
+
|
37
|
+
# matching routes
|
38
|
+
test do
|
39
|
+
App.get('/foo') { 'foo' }
|
40
|
+
App.get('/bar') { 'bar' }
|
41
|
+
|
42
|
+
App.routes.match(:get, '/foo').wont == nil
|
43
|
+
App.routes.match(:get, '/foo').action.call.must == 'foo'
|
44
|
+
end
|
45
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'nanotest'
|
3
|
+
require 'nanotest/spec'
|
4
|
+
require 'nanotest/contexts'
|
5
|
+
begin
|
6
|
+
require 'ruby-debug'
|
7
|
+
require 'redgreen' # gem install mynyml-redgreen
|
8
|
+
require 'nanotest/stats'
|
9
|
+
require 'nanotest/focus'
|
10
|
+
rescue LoadError, RuntimeError
|
11
|
+
end
|
12
|
+
|
13
|
+
$:.unshift Pathname(__FILE__).dirname.parent + 'lib'
|
14
|
+
require 'simple_router'
|
15
|
+
|
16
|
+
include Nanotest
|
17
|
+
include Nanotest::Contexts
|
data/test/test_routes.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
## Routes
|
4
|
+
context do
|
5
|
+
Routes = SimpleRouter::Routes
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@routes = Routes.new
|
9
|
+
@action = lambda {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# stores route definitions
|
13
|
+
test do
|
14
|
+
@routes.add(:get, '/foo', {}, &@action)
|
15
|
+
@routes.first.path.must == '/foo'
|
16
|
+
end
|
17
|
+
|
18
|
+
## matching
|
19
|
+
|
20
|
+
# matches a path
|
21
|
+
test do
|
22
|
+
@routes.add(:get, '/foo', {}, &@action)
|
23
|
+
@routes.add(:get, '/bar', {}, &@action)
|
24
|
+
|
25
|
+
@routes.match(:get, '/bar').wont == nil
|
26
|
+
@routes.match(:get, '/bar').path.must == '/bar'
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns nil when no route matches
|
30
|
+
test do
|
31
|
+
@routes.add(:get, '/foo', {}, &@action)
|
32
|
+
@routes.add(:get, '/bar', {}, &@action)
|
33
|
+
|
34
|
+
@routes.match('/baz', :get).must == nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# normalizes passed in verb string
|
38
|
+
test do
|
39
|
+
@routes.add(:get, '/foo', {}, &@action)
|
40
|
+
@routes.add(:get, '/bar', {}, &@action)
|
41
|
+
|
42
|
+
@routes.match('get', '/bar').path.must == '/bar'
|
43
|
+
@routes.match('GET', '/bar').path.must == '/bar'
|
44
|
+
@routes.match(' GET ','/bar').path.must == '/bar'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
## Route
|
49
|
+
context do
|
50
|
+
|
51
|
+
# internal API
|
52
|
+
test do
|
53
|
+
verb, path, options, action = :get, '/foo', {}, lambda {}
|
54
|
+
|
55
|
+
route = SimpleRouter::Routes::Route.new(verb, path, options, &action)
|
56
|
+
route.verb .must == verb
|
57
|
+
route.path .must == path
|
58
|
+
route.options .must == options
|
59
|
+
route.action .must == action
|
60
|
+
route.values .must == nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class App
|
4
|
+
include SimpleRouter::DSL
|
5
|
+
end
|
6
|
+
|
7
|
+
context do
|
8
|
+
|
9
|
+
setup do
|
10
|
+
SimpleRouter.engine = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
## engine
|
14
|
+
|
15
|
+
# default engine
|
16
|
+
test do
|
17
|
+
SimpleRouter.engine.name.split('::').last.must == 'SimpleEngine'
|
18
|
+
end
|
19
|
+
|
20
|
+
# custom engine
|
21
|
+
test do
|
22
|
+
SimpleRouter.engine = ::Object
|
23
|
+
SimpleRouter.engine.name.must == 'Object'
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_router
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.8.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mynyml
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-06 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rack
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nanotest
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: nanotest_extensions
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description: Simple, minimalistic, standalone router meant to be used with pure rack applications.
|
46
|
+
email: mynyml@gmail.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files: []
|
52
|
+
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- LICENSE
|
56
|
+
- Manifest
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- TODO
|
60
|
+
- examples/rack_app.ru
|
61
|
+
- lib/simple_router.rb
|
62
|
+
- lib/simple_router/dsl.rb
|
63
|
+
- lib/simple_router/engines/simple_engine.rb
|
64
|
+
- lib/simple_router/routes.rb
|
65
|
+
- simple_router.gemspec
|
66
|
+
- test/engines/test_simple_engine.rb
|
67
|
+
- test/test_dsl.rb
|
68
|
+
- test/test_helper.rb
|
69
|
+
- test/test_routes.rb
|
70
|
+
- test/test_simple_router.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/mynyml/simple_router
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project: simple_router
|
95
|
+
rubygems_version: 1.3.5
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: Simple, minimalistic, standalone router meant to be used with pure rack applications
|
99
|
+
test_files: []
|
100
|
+
|