sinatra-named-routes 0.1.0 → 0.1.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/Gemfile.lock +15 -15
- data/README.md +67 -1
- data/lib/sinatra/named_routes.rb +8 -1
- data/lib/sinatra/route_parser.rb +141 -141
- data/lib/sinatra/version.rb +1 -1
- data/spec/route_parser_spec.rb +10 -10
- metadata +4 -4
data/Gemfile.lock
CHANGED
@@ -1,38 +1,38 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sinatra-named-routes (0.0
|
4
|
+
sinatra-named-routes (0.1.0)
|
5
5
|
sinatra
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
backports (2.
|
10
|
+
backports (2.5.1)
|
11
11
|
diff-lcs (1.1.3)
|
12
12
|
eventmachine (0.12.10)
|
13
13
|
ffi (1.0.11)
|
14
|
-
guard (1.0.
|
14
|
+
guard (1.0.1)
|
15
15
|
ffi (>= 0.5.0)
|
16
16
|
thor (~> 0.14.6)
|
17
|
-
guard-rspec (0.
|
17
|
+
guard-rspec (0.7.0)
|
18
18
|
guard (>= 0.10.0)
|
19
|
-
multi_json (1.0
|
19
|
+
multi_json (1.2.0)
|
20
20
|
rack (1.4.1)
|
21
21
|
rack-protection (1.2.0)
|
22
22
|
rack
|
23
23
|
rack-test (0.6.1)
|
24
24
|
rack (>= 1.0)
|
25
25
|
rake (0.9.2.2)
|
26
|
-
rspec (2.
|
27
|
-
rspec-core (~> 2.
|
28
|
-
rspec-expectations (~> 2.
|
29
|
-
rspec-mocks (~> 2.
|
30
|
-
rspec-core (2.
|
31
|
-
rspec-expectations (2.
|
32
|
-
diff-lcs (~> 1.1.
|
33
|
-
rspec-mocks (2.
|
34
|
-
simplecov (0.
|
35
|
-
multi_json (~> 1.0
|
26
|
+
rspec (2.9.0)
|
27
|
+
rspec-core (~> 2.9.0)
|
28
|
+
rspec-expectations (~> 2.9.0)
|
29
|
+
rspec-mocks (~> 2.9.0)
|
30
|
+
rspec-core (2.9.0)
|
31
|
+
rspec-expectations (2.9.1)
|
32
|
+
diff-lcs (~> 1.1.3)
|
33
|
+
rspec-mocks (2.9.0)
|
34
|
+
simplecov (0.6.1)
|
35
|
+
multi_json (~> 1.0)
|
36
36
|
simplecov-html (~> 0.5.3)
|
37
37
|
simplecov-html (0.5.3)
|
38
38
|
sinatra (1.3.2)
|
data/README.md
CHANGED
@@ -1,3 +1,69 @@
|
|
1
1
|
# Sinatra Named Routes [](http://travis-ci.org/ckhampus/sinatra-named-routes)
|
2
2
|
|
3
|
-
This gem allows the use of named routes in Sinatra applications.
|
3
|
+
This gem allows the use of named routes in Sinatra applications.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
To use this gem you must register it in your Sinatra application.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'sinatra/base'
|
11
|
+
require 'sinatra/named_routes'
|
12
|
+
|
13
|
+
class MyApp < Sinatra::Base
|
14
|
+
register Sinatra::NamedRoutes
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
The you use the `map` method to map a route to a name, and use that name when defining your routes.
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'sinatra/base'
|
22
|
+
require 'sinatra/named_routes'
|
23
|
+
|
24
|
+
class MyApp < Sinatra::Base
|
25
|
+
register Sinatra::NamedRoutes
|
26
|
+
|
27
|
+
map :article, '/article/:id'
|
28
|
+
|
29
|
+
get :article do
|
30
|
+
# get article bla bla ...
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
To generate urls in extends Sinatras built-in methods like `url` and `to` but it does not break them. They work like before except that now you can also pass the route name and paramters. The parameters have to be always passed as the last argument. Otherwise the `url` work the same.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# in your route or view you can write something like this
|
39
|
+
url :article, false, :id => 123 # /article/123
|
40
|
+
```
|
41
|
+
|
42
|
+
The `map` method supports the same routes as Sinatra does.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
# named parameters
|
46
|
+
map :article, '/article/:id'
|
47
|
+
|
48
|
+
url :article, false, :id => 123 # /article/123
|
49
|
+
|
50
|
+
# splats
|
51
|
+
map :article, '/article/*.*'
|
52
|
+
|
53
|
+
url :article, false, [123, 'json'] # /article/123.json
|
54
|
+
|
55
|
+
# regular expressions
|
56
|
+
map :article, %r{/article/([\w]+).([\w]+)}
|
57
|
+
|
58
|
+
url :article, false, [123, 'json'] # /article/123.json
|
59
|
+
|
60
|
+
# named captures
|
61
|
+
map :article, %r{/article/(?<slug>[^/?#]+)}
|
62
|
+
|
63
|
+
url :article, false, :slug => 'hello_world' # /article/hello_world
|
64
|
+
|
65
|
+
# optional named captures
|
66
|
+
map(:articles, %r{/articles(?<format>.[^/?#]+)?})
|
67
|
+
|
68
|
+
url :articles, false, :format => '.html' # /articles.html
|
69
|
+
```
|
data/lib/sinatra/named_routes.rb
CHANGED
@@ -5,7 +5,9 @@ module Sinatra
|
|
5
5
|
module NamedRoutes
|
6
6
|
module Helpers
|
7
7
|
|
8
|
-
#
|
8
|
+
# Generates the absolute URI for a given path in the app.
|
9
|
+
# Takes Rack routers and reverse proxies into account.
|
10
|
+
# Extended support passing in named route and parameters.
|
9
11
|
def uri(*args)
|
10
12
|
path = args.shift if args.first.is_a? Symbol
|
11
13
|
params = args.pop if args.last.is_a? Array or args.last.is_a? Hash
|
@@ -19,13 +21,17 @@ module Sinatra
|
|
19
21
|
end
|
20
22
|
|
21
23
|
end
|
24
|
+
|
22
25
|
alias :to :uri
|
23
26
|
alias :url :uri
|
24
27
|
end
|
25
28
|
|
29
|
+
# Maps a path to name.
|
26
30
|
def map(name, path)
|
27
31
|
NamedRoutes.routes[name] = Route.new path
|
28
32
|
end
|
33
|
+
|
34
|
+
alias :bind :map
|
29
35
|
|
30
36
|
private
|
31
37
|
|
@@ -38,6 +44,7 @@ module Sinatra
|
|
38
44
|
end
|
39
45
|
|
40
46
|
def self.get_path(name, params = {})
|
47
|
+
raise ArgumentError, "No route with the name #{name} exists." if NamedRoutes.routes.nil?
|
41
48
|
NamedRoutes.routes[name].build params
|
42
49
|
end
|
43
50
|
|
data/lib/sinatra/route_parser.rb
CHANGED
@@ -1,164 +1,164 @@
|
|
1
|
-
class Route
|
2
|
-
attr_reader :source
|
3
1
|
|
4
|
-
|
5
|
-
|
2
|
+
module Sinatra
|
3
|
+
module NamedRoutes
|
4
|
+
class Route
|
5
|
+
attr_reader :source
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
@output = []
|
7
|
+
def initialize(route)
|
8
|
+
route = route.source if route.is_a? Regexp
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# Public: Build URL with parameters.
|
15
|
-
#
|
16
|
-
# params - The hash or array of parameters to pass to URL.
|
17
|
-
#
|
18
|
-
# Returns the URL as a String.
|
19
|
-
def build(params = {})
|
20
|
-
path = []
|
21
|
-
params = {} if params.nil?
|
22
|
-
|
23
|
-
@output.each_index do |index|
|
24
|
-
item = @output[index]
|
25
|
-
next_item = @output.fetch(index + 1, nil)
|
26
|
-
|
27
|
-
case item[:token]
|
28
|
-
when :slash
|
29
|
-
@trailing_slash = item[:optional]
|
30
|
-
path << '/'
|
31
|
-
when :dot
|
32
|
-
@trailing_dot = item[:optional]
|
33
|
-
path << '.'
|
34
|
-
when :splat
|
35
|
-
if params.is_a? Hash
|
36
|
-
raise ArgumentError, 'No parameters passed.' if params[:splat].empty?
|
37
|
-
path << params[:splat].shift
|
38
|
-
else
|
39
|
-
raise ArgumentError, 'No enough parameters passed.' if params.empty?
|
40
|
-
path << params.shift
|
41
|
-
end
|
42
|
-
when :path
|
43
|
-
path << item[:value]
|
44
|
-
when :named_param
|
45
|
-
item_key = item[:value]
|
46
|
-
|
47
|
-
if params.has_key? item_key
|
48
|
-
path << params.delete(item_key)
|
49
|
-
else
|
50
|
-
raise ArgumentError, "No value passed for '#{item_key.to_s}'" unless item[:optional]
|
51
|
-
end
|
52
|
-
when :regexp
|
53
|
-
name = /#{item[:value]}/.names
|
10
|
+
@source = route
|
11
|
+
@input = StringScanner.new(route)
|
12
|
+
@output = []
|
54
13
|
|
55
|
-
|
56
|
-
|
14
|
+
parse
|
15
|
+
end
|
57
16
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
path <<
|
17
|
+
def build(params = {})
|
18
|
+
path = []
|
19
|
+
params = {} if params.nil?
|
20
|
+
|
21
|
+
@output.each_index do |index|
|
22
|
+
item = @output[index]
|
23
|
+
next_item = @output.fetch(index + 1, nil)
|
24
|
+
|
25
|
+
case item[:token]
|
26
|
+
when :slash
|
27
|
+
@trailing_slash = item[:optional]
|
28
|
+
path << '/'
|
29
|
+
when :dot
|
30
|
+
@trailing_dot = item[:optional]
|
31
|
+
path << '.'
|
32
|
+
when :splat
|
33
|
+
if params.is_a? Hash
|
34
|
+
raise ArgumentError, 'No parameters passed.' if params[:splat].empty?
|
35
|
+
path << params[:splat].shift
|
36
|
+
else
|
37
|
+
raise ArgumentError, 'No enough parameters passed.' if params.empty?
|
38
|
+
path << params.shift
|
39
|
+
end
|
40
|
+
when :path
|
41
|
+
path << item[:value]
|
42
|
+
when :named_param
|
43
|
+
item_key = item[:value]
|
44
|
+
|
45
|
+
if params.has_key? item_key
|
46
|
+
path << params.delete(item_key)
|
47
|
+
else
|
48
|
+
raise ArgumentError, "No value passed for '#{item_key.to_s}'" unless item[:optional]
|
49
|
+
end
|
50
|
+
when :regexp
|
51
|
+
name = /#{item[:value]}/.names
|
52
|
+
|
53
|
+
if name.any?
|
54
|
+
name = name.first.to_sym
|
55
|
+
|
56
|
+
if params.has_key? name
|
57
|
+
path << params.delete(name)
|
58
|
+
else
|
59
|
+
raise ArgumentError, "No value passed for '#{name.to_s}'" unless item[:optional]
|
60
|
+
end
|
61
|
+
else
|
62
|
+
if params.is_a? Hash
|
63
|
+
raise ArgumentError, 'No enough parameters passed.' if params[:captures].empty? and !item[:optional]
|
64
|
+
path << params[:captures].shift
|
65
|
+
else
|
66
|
+
raise ArgumentError, 'No enough parameters passed.' if params.empty?
|
67
|
+
path << params.shift
|
68
|
+
end
|
69
|
+
end
|
70
70
|
end
|
71
71
|
end
|
72
|
-
end
|
73
|
-
end
|
74
72
|
|
75
|
-
|
73
|
+
path = path.join
|
76
74
|
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
if @trailing_dot
|
76
|
+
path = path.chomp '.'
|
77
|
+
end
|
80
78
|
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
if @trailing_slash
|
80
|
+
path = path.chomp '/'
|
81
|
+
end
|
84
82
|
|
85
|
-
|
86
|
-
|
83
|
+
path
|
84
|
+
end
|
87
85
|
|
88
|
-
|
86
|
+
private
|
89
87
|
|
90
|
-
|
91
|
-
|
92
|
-
|
88
|
+
def is_optional?
|
89
|
+
@output.last[:optional] = @input.scan(/\?/) ? true : false
|
90
|
+
end
|
93
91
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
92
|
+
def parse
|
93
|
+
while token = parse_slash || parse_path || parse_named_param ||
|
94
|
+
parse_dot || parse_splat || parse_regexp
|
95
|
+
@output << token
|
96
|
+
is_optional?
|
97
|
+
end
|
98
|
+
end
|
101
99
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
100
|
+
def parse_slash
|
101
|
+
if @input.scan(/\//)
|
102
|
+
{
|
103
|
+
:token => :slash
|
104
|
+
}
|
105
|
+
else
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
111
109
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
110
|
+
def parse_dot
|
111
|
+
if @input.scan(/\./)
|
112
|
+
{
|
113
|
+
:token => :dot
|
114
|
+
}
|
115
|
+
else
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
121
119
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
120
|
+
def parse_splat
|
121
|
+
if @input.scan(/\*/)
|
122
|
+
{
|
123
|
+
:token => :splat
|
124
|
+
}
|
125
|
+
else
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
end
|
131
129
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
130
|
+
def parse_path
|
131
|
+
if @input.scan(/\w+/)
|
132
|
+
{
|
133
|
+
:token => :path,
|
134
|
+
:value => @input.matched
|
135
|
+
}
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
142
140
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
141
|
+
def parse_named_param
|
142
|
+
if @input.scan(/:[^\W]*/)
|
143
|
+
{
|
144
|
+
:token => :named_param,
|
145
|
+
:value => @input.matched.sub(':', '').to_sym
|
146
|
+
}
|
147
|
+
else
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
end
|
153
151
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
152
|
+
def parse_regexp
|
153
|
+
if @input.scan(/\([^\)]*\)/)
|
154
|
+
{
|
155
|
+
:token => :regexp,
|
156
|
+
:value => @input.matched
|
157
|
+
}
|
158
|
+
else
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
end
|
162
162
|
end
|
163
163
|
end
|
164
164
|
end
|
data/lib/sinatra/version.rb
CHANGED
data/spec/route_parser_spec.rb
CHANGED
@@ -1,46 +1,46 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
2
|
|
3
|
-
describe Route do
|
3
|
+
describe Sinatra::NamedRoutes::Route do
|
4
4
|
it 'supports named parameters' do
|
5
|
-
route = Route.new('/hello/:person.:format')
|
5
|
+
route = Sinatra::NamedRoutes::Route.new('/hello/:person.:format')
|
6
6
|
url = route.build :person => 'cristian', :format => 'json'
|
7
7
|
url.should eql '/hello/cristian.json'
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'throws exception if required named params are missing' do
|
11
11
|
expect do
|
12
|
-
route = Route.new('/hello/:person.:format')
|
12
|
+
route = Sinatra::NamedRoutes::Route.new('/hello/:person.:format')
|
13
13
|
url = route.build :person => 'cristian'
|
14
14
|
end.to raise_exception ArgumentError
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'supports splats' do
|
18
|
-
route = Route.new('/hello/*.*')
|
18
|
+
route = Sinatra::NamedRoutes::Route.new('/hello/*.*')
|
19
19
|
url = route.build ['cristian', 'json']
|
20
20
|
url.should eql '/hello/cristian.json'
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'throws exception if required splats are missing' do
|
24
24
|
expect do
|
25
|
-
route = Route.new('/hello/*.*')
|
25
|
+
route = Sinatra::NamedRoutes::Route.new('/hello/*.*')
|
26
26
|
url = route.build ['cristian']
|
27
27
|
end.to raise_exception ArgumentError
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'supports splats mixed wih named parameters' do
|
31
|
-
route = Route.new('/hello/:person.*')
|
31
|
+
route = Sinatra::NamedRoutes::Route.new('/hello/:person.*')
|
32
32
|
url = route.build :person => 'cristian', :splat => ['json']
|
33
33
|
url.should eql '/hello/cristian.json'
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'supports regular expressions' do
|
37
|
-
route = Route.new(%r{/hello/([\w]+).([\w]+)})
|
37
|
+
route = Sinatra::NamedRoutes::Route.new(%r{/hello/([\w]+).([\w]+)})
|
38
38
|
url = route.build ['cristian', 'html']
|
39
39
|
url.should eql '/hello/cristian.html'
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'supports optional regular expressions mixed with named params' do
|
43
|
-
route = Route.new(%r{/hello/:lang/([\w]+).?([\w]+)?})
|
43
|
+
route = Sinatra::NamedRoutes::Route.new(%r{/hello/:lang/([\w]+).?([\w]+)?})
|
44
44
|
url = route.build :lang => 'en', :captures => ['cristian', 'html']
|
45
45
|
url.should eql '/hello/en/cristian.html'
|
46
46
|
|
@@ -51,7 +51,7 @@ describe Route do
|
|
51
51
|
it 'supports named capture groups' do
|
52
52
|
next if RUBY_VERSION < '1.9'
|
53
53
|
|
54
|
-
route = Route.new(%r{/hello/(?<person>[^/?#]+)})
|
54
|
+
route = Sinatra::NamedRoutes::Route.new(%r{/hello/(?<person>[^/?#]+)})
|
55
55
|
url = route.build :person => 'cristian'
|
56
56
|
url.should eql '/hello/cristian'
|
57
57
|
end
|
@@ -59,7 +59,7 @@ describe Route do
|
|
59
59
|
it 'supports optional named capture groups' do
|
60
60
|
next if RUBY_VERSION < '1.9'
|
61
61
|
|
62
|
-
route = Route.new(%r{/page(?<format>.[^/?#]+)?})
|
62
|
+
route = Sinatra::NamedRoutes::Route.new(%r{/page(?<format>.[^/?#]+)?})
|
63
63
|
url = route.build
|
64
64
|
url.should eql '/page'
|
65
65
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-named-routes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-10 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
16
|
-
requirement: &
|
16
|
+
requirement: &70101493598320 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70101493598320
|
25
25
|
description: Allows the use of named routes in Sinatra applications.
|
26
26
|
email:
|
27
27
|
- contact@cristianhampus.se
|