howl-router 0.1.1 → 0.2.0
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.
- checksums.yaml +4 -4
- data/lib/howl-router.rb +9 -4
- data/lib/howl-router/matcher.rb +30 -2
- data/lib/howl-router/padrino/ext/class_methods.rb +109 -108
- data/lib/howl-router/padrino/ext/instance_methods.rb +46 -46
- data/lib/howl-router/padrino/route.rb +6 -1
- data/lib/howl-router/request.rb +0 -4
- data/lib/howl-router/route.rb +36 -1
- data/lib/howl-router/router.rb +1 -1
- data/lib/howl-router/version.rb +1 -1
- data/test/padrino_test.rb +10 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7762704358bdeef51e547cbcf8fd5c82ed290850
|
4
|
+
data.tar.gz: 6be61141decbb09555cb5419c5b68e1db83369a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2456c6071cad44e55290fcf59db6460a163da793073d0acb76ff36eb14a1c49562efccbb54203d34b7343d9f414dd006590ffffc714b32b186e5bb9e736f535
|
7
|
+
data.tar.gz: 9bcbe7162ef0a76bfe25597233df1b25c753ce2c68435e7e8f324ea7a020be6cb4b755e723ba6a3480ac0ce123e5b7f6ae254eae77196caa0d95e4553869f3b8
|
data/lib/howl-router.rb
CHANGED
@@ -19,9 +19,9 @@ class Howl
|
|
19
19
|
# Generate a route, and add to routes.
|
20
20
|
#
|
21
21
|
# @param [String, Symbol] verb The verb decide a acceptable request method.
|
22
|
-
# @param [String] path The path associate to route.
|
22
|
+
# @param [String, Regexp] path The path associate to route.
|
23
23
|
# @option options [String] :path_for_generation Accept path_for_generation.
|
24
|
-
# @yield The block
|
24
|
+
# @yield The block associate to route.
|
25
25
|
#
|
26
26
|
# @example
|
27
27
|
# howl = Howl.new
|
@@ -60,7 +60,8 @@ class Howl
|
|
60
60
|
|
61
61
|
# Determines whether the compiled.
|
62
62
|
#
|
63
|
-
# @return [
|
63
|
+
# @return [Boolean]
|
64
|
+
#
|
64
65
|
def compiled?
|
65
66
|
@compiled
|
66
67
|
end
|
@@ -68,6 +69,7 @@ class Howl
|
|
68
69
|
# Compile routes.
|
69
70
|
#
|
70
71
|
# @return [Array] Return a compiled routes.
|
72
|
+
#
|
71
73
|
def compile
|
72
74
|
@compiled = true
|
73
75
|
router.compile
|
@@ -78,6 +80,7 @@ class Howl
|
|
78
80
|
# @param [Rack::Request] request The request is a Rack::Request or instance that inherited it.
|
79
81
|
#
|
80
82
|
# @return [Array] Return a routes that match the path_info.
|
83
|
+
#
|
81
84
|
def recognize(request)
|
82
85
|
router.recognize(request)
|
83
86
|
end
|
@@ -86,7 +89,7 @@ class Howl
|
|
86
89
|
#
|
87
90
|
# @param [String] path_info
|
88
91
|
#
|
89
|
-
# @return [Array] Return
|
92
|
+
# @return [Array] Return an Array that likes [name, params].
|
90
93
|
def recognize_path(path_info)
|
91
94
|
response = router.recognize(Rack::MockRequest.env_for(path_info))
|
92
95
|
route, params = response.first
|
@@ -102,6 +105,7 @@ class Howl
|
|
102
105
|
# Return a Router instance.
|
103
106
|
#
|
104
107
|
# @return [Howl::Router]
|
108
|
+
#
|
105
109
|
def router
|
106
110
|
@router ||= Router.new
|
107
111
|
end
|
@@ -109,6 +113,7 @@ class Howl
|
|
109
113
|
# Return a added routes.
|
110
114
|
#
|
111
115
|
# @return [Array]
|
116
|
+
#
|
112
117
|
def routes
|
113
118
|
router.routes
|
114
119
|
end
|
data/lib/howl-router/matcher.rb
CHANGED
@@ -2,16 +2,39 @@ require 'mustermann'
|
|
2
2
|
|
3
3
|
class Howl
|
4
4
|
class Matcher
|
5
|
+
# @param [String] path The path is string or regexp.
|
6
|
+
# @option options [Hash] :capture Set capture for path pattern.
|
7
|
+
# @option options [Hash] :default_values Set default_values for path pattern.
|
8
|
+
#
|
9
|
+
# @return [Howl::Matcher]
|
10
|
+
#
|
5
11
|
def initialize(path, options = {})
|
6
12
|
@path = path.is_a?(String) && path.empty? ? "/" : path
|
7
13
|
@capture = options.delete(:capture)
|
8
14
|
@default_values = options.delete(:default_values)
|
9
15
|
end
|
10
16
|
|
17
|
+
# Do the matching.
|
18
|
+
#
|
19
|
+
# @param [String] pattern The pattern is actual path (path_info etc).
|
20
|
+
#
|
21
|
+
# @return [MatchData] If the pattern matched this route, return a MatchData.
|
22
|
+
# @return [Nil] If the pattern doesn't matched this route, return a nil.
|
23
|
+
#
|
11
24
|
def match(pattern)
|
12
25
|
handler.match(pattern)
|
13
26
|
end
|
14
27
|
|
28
|
+
# Expand the path with params.
|
29
|
+
#
|
30
|
+
# @param [Hash] params The params for path pattern.
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# matcher = Howl::Matcher.new("/foo/:bar")
|
34
|
+
# matcher.expand(:bar => 123) #=> "/foo/123"
|
35
|
+
# matcher.expand(:bar => "bar", :baz => "test") #=> "/foo/bar?baz=test"
|
36
|
+
#
|
37
|
+
# @return [String] A expaneded path.
|
15
38
|
def expand(params)
|
16
39
|
params = params.dup
|
17
40
|
query = params.keys.inject({}) do |result, key|
|
@@ -24,25 +47,30 @@ class Howl
|
|
24
47
|
expanded_path
|
25
48
|
end
|
26
49
|
|
50
|
+
# @return [Boolean] This matcher's handler is mustermann ?
|
27
51
|
def mustermann?
|
28
52
|
handler.class == Mustermann::Rails
|
29
53
|
end
|
30
54
|
|
55
|
+
# @return [Mustermann::Rails] Return a Mustermann::Rails when @path is string.
|
56
|
+
# @return [Regexp] Return a regexp when @path is regexp.
|
31
57
|
def handler
|
32
58
|
@handler ||= case @path
|
33
59
|
when String
|
34
|
-
Mustermann.new(@path, :type => :rails, :capture => @capture
|
60
|
+
Mustermann.new(@path, :type => :rails, :capture => @capture)
|
35
61
|
when Regexp
|
36
62
|
/^(?:#{@path})$/
|
37
63
|
end
|
38
64
|
end
|
39
65
|
|
66
|
+
# @return [String] Return a converted handler.
|
40
67
|
def to_s
|
41
68
|
handler.to_s
|
42
69
|
end
|
43
70
|
|
71
|
+
# @return [Array] Return a named captures.
|
44
72
|
def names
|
45
|
-
|
73
|
+
handler.names.map(&:to_sym)
|
46
74
|
end
|
47
75
|
end
|
48
76
|
end
|
@@ -51,129 +51,130 @@ class Howl
|
|
51
51
|
end
|
52
52
|
|
53
53
|
private
|
54
|
-
def route(verb, path, *args, &block)
|
55
|
-
options = case args.size
|
56
|
-
when 2
|
57
|
-
args.last.merge(:map => args.first)
|
58
|
-
when 1
|
59
|
-
map = args.shift if args.first.is_a?(String)
|
60
|
-
if args.first.is_a?(Hash)
|
61
|
-
map ? args.first.merge(:map => map) : args.first
|
62
|
-
else
|
63
|
-
{:map => map || args.first}
|
64
|
-
end
|
65
|
-
when 0
|
66
|
-
{}
|
67
|
-
else raise
|
68
|
-
end
|
69
|
-
|
70
|
-
route_options = options.dup
|
71
|
-
route_options[:provides] = @_provides if @_provides
|
72
|
-
|
73
|
-
if allow_disabled_csrf
|
74
|
-
unless route_options[:csrf_protection] == false
|
75
|
-
route_options[:csrf_protection] = true
|
76
|
-
end
|
77
|
-
end
|
78
54
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
proc {|a,p| unbound_method.bind(a).call }
|
90
|
-
|
91
|
-
invoke_hook(:route_added, verb, path, block)
|
92
|
-
|
93
|
-
# Howl route construction
|
94
|
-
path[0, 0] = "/" if path == "(.:format)"
|
95
|
-
route = router.add(verb.downcase.to_sym, path, route_options)
|
96
|
-
route.name = name if name
|
97
|
-
route.action = action
|
98
|
-
priority_name = options.delete(:priority) || :normal
|
99
|
-
priority = ROUTE_PRIORITY[priority_name] or raise("Priority #{priority_name} not recognized, try #{ROUTE_PRIORITY.keys.join(', ')}")
|
100
|
-
route.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
|
101
|
-
route.parent = route_parents ? (route_parents.count == 1 ? route_parents.first : route_parents) : route_parents
|
102
|
-
route.host = options.delete(:host) if options.key?(:host)
|
103
|
-
route.user_agent = options.delete(:agent) if options.key?(:agent)
|
104
|
-
if options.key?(:default_values)
|
105
|
-
defaults = options.delete(:default_values)
|
106
|
-
route.default_values = defaults if defaults
|
107
|
-
end
|
108
|
-
options.delete_if do |option, captures|
|
109
|
-
if route.significant_variable_names.include?(option)
|
110
|
-
route.capture[option] = Array(captures).first
|
111
|
-
true
|
55
|
+
def route(verb, path, *args, &block)
|
56
|
+
options = case args.size
|
57
|
+
when 2
|
58
|
+
args.last.merge(:map => args.first)
|
59
|
+
when 1
|
60
|
+
map = args.shift if args.first.is_a?(String)
|
61
|
+
if args.first.is_a?(Hash)
|
62
|
+
map ? args.first.merge(:map => map) : args.first
|
63
|
+
else
|
64
|
+
{:map => map || args.first}
|
112
65
|
end
|
113
|
-
|
66
|
+
when 0
|
67
|
+
{}
|
68
|
+
else raise
|
69
|
+
end
|
114
70
|
|
115
|
-
|
116
|
-
|
117
|
-
conditions, @conditions = @conditions, []
|
118
|
-
route.custom_conditions.concat(conditions)
|
71
|
+
route_options = options.dup
|
72
|
+
route_options[:provides] = @_provides if @_provides
|
119
73
|
|
120
|
-
|
74
|
+
if allow_disabled_csrf
|
75
|
+
unless route_options[:csrf_protection] == false
|
76
|
+
route_options[:csrf_protection] = true
|
77
|
+
end
|
78
|
+
end
|
121
79
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
80
|
+
path, *route_options[:with] = path if path.is_a?(Array)
|
81
|
+
action = path
|
82
|
+
path, name, route_parents, options, route_options = *parse_route(path, route_options, verb)
|
83
|
+
options.reverse_merge!(@_conditions) if @_conditions
|
84
|
+
|
85
|
+
method_name = "#{verb} #{path}"
|
86
|
+
unbound_method = generate_method(method_name, &block)
|
87
|
+
|
88
|
+
block = block.arity != 0 ?
|
89
|
+
proc {|a,p| unbound_method.bind(a).call(*p) } :
|
90
|
+
proc {|a,p| unbound_method.bind(a).call }
|
91
|
+
|
92
|
+
invoke_hook(:route_added, verb, path, block)
|
93
|
+
|
94
|
+
# Howl route construction
|
95
|
+
path[0, 0] = "/" if path == "(.:format)"
|
96
|
+
route = router.add(verb.downcase.to_sym, path, route_options)
|
97
|
+
route.name = name if name
|
98
|
+
route.action = action
|
99
|
+
priority_name = options.delete(:priority) || :normal
|
100
|
+
priority = ROUTE_PRIORITY[priority_name] or raise("Priority #{priority_name} not recognized, try #{ROUTE_PRIORITY.keys.join(', ')}")
|
101
|
+
route.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
|
102
|
+
route.parent = route_parents ? (route_parents.count == 1 ? route_parents.first : route_parents) : route_parents
|
103
|
+
route.host = options.delete(:host) if options.key?(:host)
|
104
|
+
route.user_agent = options.delete(:agent) if options.key?(:agent)
|
105
|
+
if options.key?(:default_values)
|
106
|
+
defaults = options.delete(:default_values)
|
107
|
+
route.default_values = defaults if defaults
|
108
|
+
end
|
109
|
+
options.delete_if do |option, captures|
|
110
|
+
if route.significant_variable_names.include?(option)
|
111
|
+
route.capture[option] = Array(captures).first
|
112
|
+
true
|
128
113
|
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Add Sinatra conditions
|
117
|
+
options.each {|o, a| route.respond_to?("#{o}=") ? route.send("#{o}=", a) : send(o, *a) }
|
118
|
+
conditions, @conditions = @conditions, []
|
119
|
+
route.custom_conditions.concat(conditions)
|
129
120
|
|
130
|
-
|
121
|
+
invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
|
131
122
|
|
132
|
-
|
123
|
+
# Add Application defaults
|
124
|
+
route.before_filters.concat(@filters[:before])
|
125
|
+
route.after_filters.concat(@filters[:after])
|
126
|
+
if @_controller
|
127
|
+
route.use_layout = @layout
|
128
|
+
route.controller = Array(@_controller)[0].to_s
|
133
129
|
end
|
134
130
|
|
135
|
-
|
136
|
-
@_use_format = true
|
137
|
-
condition do
|
138
|
-
mime_types = types.map {|t| mime_type(t) }.compact
|
139
|
-
url_format = params[:format].to_sym if params[:format]
|
140
|
-
accepts = request.accept.map {|a| a.to_str }
|
141
|
-
|
142
|
-
# per rfc2616-sec14:
|
143
|
-
# Assume */* if no ACCEPT header is given.
|
144
|
-
catch_all = (accepts.delete "*/*" || accepts.empty?)
|
145
|
-
matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types)
|
146
|
-
if matching_types.empty? && types.include?(:any)
|
147
|
-
matching_types = accepts
|
148
|
-
end
|
131
|
+
deferred_routes[priority] << [route, block]
|
149
132
|
|
150
|
-
|
151
|
-
|
152
|
-
accept_format = CONTENT_TYPE_ALIASES[type] || type
|
153
|
-
elsif catch_all && !types.include?(:any)
|
154
|
-
type = types.first
|
155
|
-
accept_format = CONTENT_TYPE_ALIASES[type] || type
|
156
|
-
end
|
133
|
+
route
|
134
|
+
end
|
157
135
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
136
|
+
def provides(*types)
|
137
|
+
@_use_format = true
|
138
|
+
condition do
|
139
|
+
mime_types = types.map {|t| mime_type(t) }.compact
|
140
|
+
url_format = params[:format].to_sym if params[:format]
|
141
|
+
accepts = request.accept.map {|a| a.to_str }
|
142
|
+
|
143
|
+
# per rfc2616-sec14:
|
144
|
+
# Assume */* if no ACCEPT header is given.
|
145
|
+
catch_all = (accepts.delete "*/*" || accepts.empty?)
|
146
|
+
matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types)
|
147
|
+
if matching_types.empty? && types.include?(:any)
|
148
|
+
matching_types = accepts
|
149
|
+
end
|
150
|
+
|
151
|
+
if !url_format && matching_types.first
|
152
|
+
type = ::Rack::Mime::MIME_TYPES.find {|k, v| v == matching_types.first }[0].sub(/\./,'').to_sym
|
153
|
+
accept_format = CONTENT_TYPE_ALIASES[type] || type
|
154
|
+
elsif catch_all && !types.include?(:any)
|
155
|
+
type = types.first
|
156
|
+
accept_format = CONTENT_TYPE_ALIASES[type] || type
|
157
|
+
end
|
173
158
|
|
174
|
-
|
159
|
+
matched_format = types.include?(:any) ||
|
160
|
+
types.include?(accept_format) ||
|
161
|
+
types.include?(url_format) ||
|
162
|
+
((!url_format) && request.accept.empty? && types.include?(:html))
|
163
|
+
# per rfc2616-sec14:
|
164
|
+
# answer with 406 if accept is given but types to not match any
|
165
|
+
# provided type
|
166
|
+
halt 406 if
|
167
|
+
(!url_format && !accepts.empty? && !matched_format) ||
|
168
|
+
(settings.respond_to?(:treat_format_as_accept) && settings.treat_format_as_accept && url_format && !matched_format)
|
169
|
+
|
170
|
+
if matched_format
|
171
|
+
@_content_type = url_format || accept_format || :html
|
172
|
+
content_type(@_content_type, :charset => 'utf-8')
|
175
173
|
end
|
174
|
+
|
175
|
+
matched_format
|
176
176
|
end
|
177
|
+
end
|
177
178
|
end
|
178
179
|
end
|
179
180
|
end
|
@@ -3,63 +3,63 @@ class Howl
|
|
3
3
|
module Padrino
|
4
4
|
module InstanceMethods
|
5
5
|
private
|
6
|
-
def invoke_route(route, params, options = {})
|
7
|
-
original_params, parent_layout, successful = @params.dup, @layout, false
|
8
|
-
captured_params = params[:captures].is_a?(Array) ? params.delete(:captures) :
|
9
|
-
params.values_at(*route.matcher.names.dup)
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@block_params = params
|
7
|
+
def invoke_route(route, params, options = {})
|
8
|
+
original_params, parent_layout, successful = @params.dup, @layout, false
|
9
|
+
captured_params = params[:captures].is_a?(Array) ? params.delete(:captures) :
|
10
|
+
params.values_at(*route.matcher.names.dup)
|
16
11
|
|
17
|
-
|
12
|
+
@_response_buffer = nil
|
13
|
+
@route = request.route_obj = route
|
14
|
+
@params.merge!(params) if params.is_a?(Hash)
|
15
|
+
#@params.merge!(:captures => captured_params) unless captured_params.empty?
|
16
|
+
@params.merge!(:captures => captured_params) if !captured_params.empty? && route.path.is_a?(Regexp)
|
17
|
+
@block_params = params
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
19
|
+
filter! :before if options[:first]
|
20
|
+
|
21
|
+
catch(:pass) do
|
22
|
+
begin
|
23
|
+
(route.before_filters - settings.filters[:before]).each{|block| instance_eval(&block) }
|
24
|
+
@layout = route.use_layout if route.use_layout
|
25
|
+
route.custom_conditions.each {|block| pass if block.bind(self).call == false }
|
26
|
+
halt_response = catch(:halt){ route_eval{ route.block[self, captured_params] }}
|
27
|
+
@_response_buffer = halt_response.is_a?(Array) ? halt_response.last : halt_response
|
28
|
+
successful = true
|
29
|
+
halt(halt_response)
|
30
|
+
ensure
|
31
|
+
(route.after_filters - settings.filters[:after]).each {|block| instance_eval(&block) } if successful
|
32
|
+
@layout, @params = parent_layout, original_params
|
34
33
|
end
|
35
34
|
end
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
def route!(base = settings, pass_block = nil)
|
38
|
+
Thread.current['padrino.instance'] = self
|
39
|
+
code, headers, routes = base.compiled_router.call(@request.env)
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
else
|
48
|
-
route_eval do
|
49
|
-
headers.each{|k, v| response[k] = v } unless headers.empty?
|
50
|
-
route_missing if code == 404
|
51
|
-
route_missing if allow = response['Allow'] and allow.include?(request.env['REQUEST_METHOD'])
|
52
|
-
end
|
41
|
+
status(code)
|
42
|
+
if code == 200
|
43
|
+
routes.each_with_index do |(route, howl_params), index|
|
44
|
+
next if route.user_agent && !(route.user_agent =~ @request.user_agent)
|
45
|
+
invoke_route(route, howl_params, :first => index.zero?)
|
53
46
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
47
|
+
else
|
48
|
+
route_eval do
|
49
|
+
headers.each{|k, v| response[k] = v } unless headers.empty?
|
50
|
+
route_missing if code == 404
|
51
|
+
route_missing if allow = response['Allow'] and allow.include?(request.env['REQUEST_METHOD'])
|
58
52
|
end
|
53
|
+
end
|
59
54
|
|
60
|
-
|
61
|
-
|
55
|
+
if base.superclass.respond_to?(:router)
|
56
|
+
route!(base.superclass, pass_block)
|
57
|
+
return
|
62
58
|
end
|
59
|
+
|
60
|
+
route_eval(&pass_block) if pass_block
|
61
|
+
route_missing
|
62
|
+
end
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
@@ -3,7 +3,8 @@ require 'howl-router/route'
|
|
3
3
|
class Howl
|
4
4
|
module Padrino
|
5
5
|
class Route < ::Howl::Route
|
6
|
-
attr_accessor :action, :cache, :
|
6
|
+
attr_accessor :action, :cache, :cache_key, :cache_expires_in,
|
7
|
+
:parent, :use_layout, :controller, :user_agent
|
7
8
|
|
8
9
|
def before_filters(&block)
|
9
10
|
@_before_filters ||= []
|
@@ -31,6 +32,10 @@ class Howl
|
|
31
32
|
[verb.to_s.upcase]
|
32
33
|
end
|
33
34
|
|
35
|
+
def original_path
|
36
|
+
@path
|
37
|
+
end
|
38
|
+
|
34
39
|
def significant_variable_names
|
35
40
|
@significant_variable_names ||= if @path.is_a?(String)
|
36
41
|
@path.scan(/(^|[^\\])[:\*]([a-zA-Z0-9_]+)/).map{|p| p.last.to_sym}
|
data/lib/howl-router/request.rb
CHANGED
data/lib/howl-router/route.rb
CHANGED
@@ -3,7 +3,17 @@ class Howl
|
|
3
3
|
attr_accessor :block, :capture, :router, :params, :name,
|
4
4
|
:order, :default_values, :path_for_generation, :verb
|
5
5
|
|
6
|
-
|
6
|
+
# @param [String, Regexp] path The path associate to this route.
|
7
|
+
# @yield The block associate to this route.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# howl = Howl.new
|
12
|
+
# index = howl.add(:get "/") # returns Howl::Route
|
13
|
+
# index.name = :index # Naming
|
14
|
+
# index.verb = :get # Define a http verb.
|
15
|
+
#
|
16
|
+
def initialize(path, &block)
|
7
17
|
@path = path
|
8
18
|
@params = {}
|
9
19
|
@capture = {}
|
@@ -11,11 +21,19 @@ class Howl
|
|
11
21
|
@block = block if block_given?
|
12
22
|
end
|
13
23
|
|
24
|
+
# Return a matcher which is wrapper of Mustermann or Regexp.
|
25
|
+
#
|
26
|
+
# @return [Howl::Matcher]
|
27
|
+
#
|
14
28
|
def matcher
|
15
29
|
@matcher ||= Matcher.new(@path, :capture => @capture,
|
16
30
|
:default_values => @default_values)
|
17
31
|
end
|
18
32
|
|
33
|
+
# Return a block's arity.
|
34
|
+
#
|
35
|
+
# @return [Fixnum]
|
36
|
+
#
|
19
37
|
def arity
|
20
38
|
@block.arity
|
21
39
|
end
|
@@ -24,12 +42,29 @@ class Howl
|
|
24
42
|
@block.call(*args)
|
25
43
|
end
|
26
44
|
|
45
|
+
# Add a block later, and this method define a priority for routing.
|
46
|
+
#
|
47
|
+
# @yield The block associate to this route later.
|
48
|
+
#
|
27
49
|
def to(&block)
|
28
50
|
@block = block if block_given?
|
29
51
|
@order = @router.current_order
|
30
52
|
@router.increment_order
|
31
53
|
end
|
32
54
|
|
55
|
+
# Return a set path.
|
56
|
+
#
|
57
|
+
# @param [Hash] args[0] The hash for route's params.
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
#
|
61
|
+
# howl = Howl.new
|
62
|
+
# foo = howl.add(:get, "/foo/:id")
|
63
|
+
# foo.path #=> "/foo/:id"
|
64
|
+
# foo.path(:id => 1) #=> "/foo/1"
|
65
|
+
#
|
66
|
+
# @return [String] path pattern or expanded path.
|
67
|
+
#
|
33
68
|
def path(*args)
|
34
69
|
return @path if args.empty?
|
35
70
|
params = args[0]
|
data/lib/howl-router/router.rb
CHANGED
@@ -34,7 +34,7 @@ class Howl
|
|
34
34
|
params[:captures] = match_data.captures
|
35
35
|
else
|
36
36
|
params.merge!(route.params).merge!(match_data.names.inject({}){|result, name|
|
37
|
-
result[name.to_sym] = match_data[name]
|
37
|
+
result[name.to_sym] = match_data[name] ? Rack::Utils.unescape(match_data[name]) : nil
|
38
38
|
result
|
39
39
|
}).merge!(request_params){|key, self_value, new_value| self_value || new_value }
|
40
40
|
end
|
data/lib/howl-router/version.rb
CHANGED
data/test/padrino_test.rb
CHANGED
@@ -110,6 +110,16 @@ describe "Howl::Padrino" do
|
|
110
110
|
assert_equal 'success!', body
|
111
111
|
end
|
112
112
|
|
113
|
+
should 'parse routes that include encoded slash' do
|
114
|
+
mock_app do
|
115
|
+
get('/:drive_alias/:path', :path => /.*/){
|
116
|
+
"Show #{params[:drive_alias]} and #{params[:path]}"
|
117
|
+
}
|
118
|
+
end
|
119
|
+
get("/drive%2Ffoo/some/path")
|
120
|
+
assert_equal "Show drive/foo and some/path", body
|
121
|
+
end
|
122
|
+
|
113
123
|
should 'parse route that contains encoded param.' do
|
114
124
|
mock_app do
|
115
125
|
get('/foo/:name'){ params[:name] }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: howl-router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- namusyaka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|