mynyml-simple_router 0.1 → 0.8

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/Rakefile CHANGED
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  spec = Gem::Specification.new do |s|
24
24
  s.name = 'simple_router'
25
- s.version = '0.1'
25
+ s.version = '0.8'
26
26
  s.summary = "Minimalistic, simple router meant to be used with pure rack applications."
27
27
  s.description = "Minimalistic, simple router meant to be used with pure rack applications."
28
28
  s.author = "Martin Aumont"
@@ -30,7 +30,7 @@ spec = Gem::Specification.new do |s|
30
30
  s.homepage = ''
31
31
  s.has_rdoc = true
32
32
  s.require_path = "lib"
33
- s.files = all_except(/doc\/.*/)
33
+ s.files = all_except([/doc\/.*/, /pkg\/*/])
34
34
  end
35
35
 
36
36
  Rake::GemPackageTask.new(spec) do |p|
@@ -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, args = 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(*args.push(request.params))]]
42
+ end
43
+ end
44
+
45
+ run App.new
@@ -1,29 +1,101 @@
1
+ require 'pathname'
2
+
1
3
  module SimpleRouter
2
4
  module Engines
3
- class SimpleEngine
4
-
5
- # Finds a route definition that matches a path
6
- #
7
- # ===== Arguments
8
- # * path: actual path to match (e.g. ENV['PATH_INFO'])
9
- # * routes: array of user defined routes
10
- #
11
- # ===== Returns
12
- # Array of two elements:
13
- #
14
- # * index 0: first matching route
15
- # * index 1: array of values for route variables, in the order they were specified
16
- #
17
- # ===== Examples
18
- #
19
- # SimpleEngine.match('/foo', ['/', '/foo', '/bar/baz']) #=> ['/foo', []]
20
- # SimpleEngine.match('/80/07/01', ['/:year/:month/:day']) #=> ['/foo', ['80', '07', '01']]
21
- #
22
- def self.match(path, routes)
23
- routes.each do |route|
24
- return route if route == path
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
25
52
  end
26
- nil
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
27
99
  end
28
100
  end
29
101
  end
@@ -1,5 +1,5 @@
1
1
  module SimpleRouter
2
- class Routes < Array #:nodoc:
2
+ class Routes < Array
3
3
 
4
4
  # routing engine
5
5
  attr_accessor :engine
@@ -13,16 +13,20 @@ module SimpleRouter
13
13
  end
14
14
 
15
15
  def match(verb, path)
16
- unless self.empty?
17
- routes = self.select {|route| route.verb == verb }
18
- paths = routes.map {|route| route.path }
16
+ none = [nil, nil]
17
+ return none if self.empty?
19
18
 
20
- path = self.engine.match(path, paths)
21
- routes.detect {|route| route.path == path }
22
- end
19
+ routes = self.select {|route| route.verb == verb }
20
+ paths = routes.map {|route| route.path }
21
+
22
+ path, vars = self.engine.match(path, paths)
23
+ return none if path.nil?
24
+
25
+ route = routes.detect {|route| route.path == path }
26
+ [route, vars]
23
27
  end
24
28
 
25
- class Route < Array #:nodoc:
29
+ class Route
26
30
  attr_accessor :verb,:path,:options,:action
27
31
 
28
32
  def initialize(verb, path, options, &action)
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_router
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: "0.8"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Aumont
@@ -30,6 +30,8 @@ files:
30
30
  - test/test_dsl.rb
31
31
  - test/test_routes.rb
32
32
  - test/test_helper.rb
33
+ - examples
34
+ - examples/rack_app.ru
33
35
  - simple_router.gemspec
34
36
  - TODO
35
37
  - lib
@@ -1,12 +1,126 @@
1
1
  require 'test/test_helper'
2
2
 
3
- Engine = SimpleRouter::Engines::SimpleEngine
4
-
5
3
  class SimpleEngineTest < Test::Unit::TestCase
4
+ include SimpleRouter::Engines::SimpleEngine
5
+
6
+ test "matches static paths" do
7
+ Base.match('/', ['/', '/foo']).first.should be('/')
8
+ Base.match('/foo', ['/', '/foo']).first.should be('/foo')
9
+ Base.match('/bar', ['/', '/foo']).first.should be(nil)
10
+ end
11
+
12
+ test "matches variable paths" do
13
+ path, vars = Base.match('/80/07', ['/foo', '/:year/:month'])
14
+ path.should be('/:year/:month')
15
+ vars.should be(['80','07'])
16
+ end
17
+
18
+ test "matches hybrid paths" do
19
+ path, vars = Base.match('/archives/80/07', ['/foo', '/archives/:year/:month'])
20
+ path.should be('/archives/:year/:month')
21
+ vars.should be(['80','07'])
22
+ end
23
+
24
+ test "ignores leading slash in path" do
25
+ path, vars = Base.match('archives/80/07', ['/foo', '/archives/:year/:month'])
26
+ path.should be('/archives/:year/:month')
27
+ vars.should be(['80','07'])
28
+ end
29
+
30
+ test "no matches" do
31
+ path, vars = Base.match('/80/07/01', ['/foo', '/:year/:month'])
32
+ path.should be(nil)
33
+ vars.should be([])
34
+ end
35
+
36
+ test "treats extention as pattern part" do
37
+ path, vars = Base.match('/a/b.xml', ['/:foo/:bar', '/:foo/:bar.:type'])
38
+ path.should be('/:foo/:bar.:type')
39
+ vars.should be(['a','b','xml'])
40
+ end
41
+ end
42
+
43
+ class PatternTest < Test::Unit::TestCase
44
+ include SimpleRouter::Engines::SimpleEngine
45
+
46
+ test "static pattern matches a path" do
47
+ path = Path.new('/foo/bar')
48
+ pattern = Pattern.new('/foo/bar')
49
+
50
+ assert pattern == path
51
+ end
52
+
53
+ test "variable pattern matches a path" do
54
+ path = Path.new('/foo/bar')
55
+ pattern = Pattern.new('/:foo/:bar')
56
+
57
+ assert pattern == path
58
+ end
59
+
60
+ test "pattern variables" do
61
+ path = Path.new('/foo/bar/baz')
62
+ pattern = Pattern.new('/:a/:b/:c')
63
+
64
+ assert pattern == path
65
+ pattern.vars.should be(%w( foo bar baz ))
66
+ end
67
+
68
+ test "pattern variables with extention" do
69
+ path = Path.new('/foo/bar/baz.xml')
70
+ pattern = Pattern.new('/:a/:b/:c.:type')
71
+
72
+ assert pattern == path
73
+ pattern.vars.should be(%w( foo bar baz xml ))
74
+ end
75
+
76
+ test "variable pattern matches a path with static extention" do
77
+ path = Path.new('/foo/bar.xml')
78
+ pattern = Pattern.new('/:foo/:bar.xml')
79
+
80
+ assert pattern == path
81
+ end
82
+
83
+ test "variable pattern matches a path with variable extention" do
84
+ path = Path.new('/foo/bar.xml')
85
+ pattern = Pattern.new('/:foo/:bar.:type')
86
+
87
+ assert pattern == path
88
+ end
89
+
90
+ test "pattern without extention doesn't match path with extention" do
91
+ path = Path.new('/foo/bar.xml')
92
+ pattern = Pattern.new('/:foo/:bar')
93
+
94
+ assert pattern != path
95
+ end
96
+
97
+ test "pattern with static extention doesn't match path without extention" do
98
+ path = Path.new('/foo/bar')
99
+ pattern = Pattern.new('/:foo/:bar.xml')
100
+
101
+ assert pattern != path
102
+ end
103
+
104
+ test "pattern with variable extention doesn't match path without extention" do
105
+ path = Path.new('/foo/bar')
106
+ pattern = Pattern.new('/:foo/:bar.:type')
107
+
108
+ assert pattern != path
109
+ end
110
+
111
+ test "doesn't ignore dots in path parts" do
112
+ path = Path.new('/foo/bar.baz/abc')
113
+ pattern = Pattern.new('/:a/:b/:c')
114
+
115
+ assert pattern == path
116
+ pattern.variables.should be(%w( foo bar.baz abc ))
117
+ end
118
+
119
+ test "doesn't get confused with extention when path contains other dots" do
120
+ path = Path.new('/foo/bar.baz/abc.xml')
121
+ pattern = Pattern.new('/:a/:b/:c.:type')
6
122
 
7
- test "matches simple paths" do
8
- Engine.match('/', ['/', '/foo']).should be('/')
9
- Engine.match('/foo', ['/', '/foo']).should be('/foo')
10
- Engine.match('/bar', ['/', '/foo']).should be(nil)
123
+ assert pattern == path
124
+ pattern.variables.should be(%w( foo bar.baz abc xml ))
11
125
  end
12
126
  end
data/test/test_dsl.rb CHANGED
@@ -33,6 +33,6 @@ class DslTest < Test::Unit::TestCase
33
33
  App.get('/bar') { 'bar' }
34
34
 
35
35
  App.routes.match(:get, '/foo').should_not be( nil )
36
- App.routes.match(:get, '/foo').action.call.should be('foo')
36
+ App.routes.match(:get, '/foo').first.action.call.should be('foo')
37
37
  end
38
38
  end
data/test/test_routes.rb CHANGED
@@ -21,14 +21,14 @@ class RoutesTest < Test::Unit::TestCase
21
21
  @routes.add(:get, '/bar', {}, &@action)
22
22
 
23
23
  @routes.match(:get, '/bar').should_not be(nil)
24
- @routes.match(:get, '/bar').path.should be('/bar')
24
+ @routes.match(:get, '/bar').first.path.should be('/bar')
25
25
  end
26
26
 
27
27
  test "returns nil when no route matches" do
28
28
  @routes.add(:get, '/foo', {}, &@action)
29
29
  @routes.add(:get, '/bar', {}, &@action)
30
30
 
31
- @routes.match('/baz', :get).should be(nil)
31
+ @routes.match('/baz', :get).should be([nil,nil])
32
32
  end
33
33
 
34
34
  ## engine
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mynyml-simple_router
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: "0.8"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Aumont
@@ -30,6 +30,8 @@ files:
30
30
  - test/test_dsl.rb
31
31
  - test/test_routes.rb
32
32
  - test/test_helper.rb
33
+ - examples
34
+ - examples/rack_app.ru
33
35
  - simple_router.gemspec
34
36
  - TODO
35
37
  - lib