simple_router 0.9.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|