howl-router 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a4ebbc66f3c3f654d674622767d7edf84254af76
4
- data.tar.gz: a4f0e1a5b59fab741073bae7cf03fa985ada4ab9
3
+ metadata.gz: 7762704358bdeef51e547cbcf8fd5c82ed290850
4
+ data.tar.gz: 6be61141decbb09555cb5419c5b68e1db83369a9
5
5
  SHA512:
6
- metadata.gz: 85c9892dd1461ab74ac61faceb3d5a92555793a5f4810d3c724008765aec3758dda65e478a6d22e534076bfce3b743a3d463920a2d1ed768870b3e301b1f8459
7
- data.tar.gz: 08f15a1b94df198e78e195ea4a1e547b2552a3b2ef1d041f405fabfa3e56f2f771192471f09ad9b23ee456defad79e77e60be5ccaf9a8a384830049de3d1b90d
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 assosiate to route.
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 [Bool]
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 a Array that likes [name, params].
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
@@ -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, :uri_decode => nil)
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
- mustermann? ? handler.names.map(&:to_sym) : []
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
- path, *route_options[:with] = path if path.is_a?(Array)
80
- action = path
81
- path, name, route_parents, options, route_options = *parse_route(path, route_options, verb)
82
- options.reverse_merge!(@_conditions) if @_conditions
83
-
84
- method_name = "#{verb} #{path}"
85
- unbound_method = generate_method(method_name, &block)
86
-
87
- block = block.arity != 0 ?
88
- proc {|a,p| unbound_method.bind(a).call(*p) } :
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
- end
66
+ when 0
67
+ {}
68
+ else raise
69
+ end
114
70
 
115
- # Add Sinatra conditions
116
- options.each {|o, a| route.respond_to?("#{o}=") ? route.send("#{o}=", a) : send(o, *a) }
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
- invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
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
- # Add Application defaults
123
- route.before_filters.concat(@filters[:before])
124
- route.after_filters.concat(@filters[:after])
125
- if @_controller
126
- route.use_layout = @layout
127
- route.controller = Array(@_controller)[0].to_s
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
- deferred_routes[priority] << [route, block]
121
+ invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
131
122
 
132
- route
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
- def provides(*types)
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
- if !url_format && matching_types.first
151
- type = ::Rack::Mime::MIME_TYPES.find {|k, v| v == matching_types.first }[0].sub(/\./,'').to_sym
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
- matched_format = types.include?(:any) ||
159
- types.include?(accept_format) ||
160
- types.include?(url_format) ||
161
- ((!url_format) && request.accept.empty? && types.include?(:html))
162
- # per rfc2616-sec14:
163
- # answer with 406 if accept is given but types to not match any
164
- # provided type
165
- halt 406 if
166
- (!url_format && !accepts.empty? && !matched_format) ||
167
- (settings.respond_to?(:treat_format_as_accept) && settings.treat_format_as_accept && url_format && !matched_format)
168
-
169
- if matched_format
170
- @_content_type = url_format || accept_format || :html
171
- content_type(@_content_type, :charset => 'utf-8')
172
- end
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
- matched_format
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
- @_response_buffer = nil
12
- @route = request.route_obj = route
13
- @params.merge!(params) if params.is_a?(Hash)
14
- @params.merge!(:captures => captured_params) unless captured_params.empty?
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
- filter! :before if options[:first]
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
- catch(:pass) do
20
- begin
21
- (route.before_filters - settings.filters[:before]).each{|block| instance_eval(&block) }
22
- @layout = route.use_layout if route.use_layout
23
- route.custom_conditions.each {|block|
24
- pass if block.bind(self).call == false
25
- } unless route.custom_conditions.empty?
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
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
- def route!(base = settings, pass_block = nil)
38
- Thread.current['padrino.instance'] = self
39
- code, headers, routes = base.compiled_router.call(@request.env)
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
- 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?)
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
- if base.superclass.respond_to?(:router)
56
- route!(base.superclass, pass_block)
57
- return
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
- route_eval(&pass_block) if pass_block
61
- route_missing
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, :parent, :use_layout, :controller, :user_agent
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}
@@ -3,9 +3,5 @@ require 'rack'
3
3
  class Howl
4
4
  class Request < Rack::Request
5
5
  attr_accessor :acceptable_methods
6
-
7
- def path_info
8
- Rack::Utils.unescape super
9
- end
10
6
  end
11
7
  end
@@ -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
- def initialize(path, options = {}, &block)
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]
@@ -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
@@ -1,3 +1,3 @@
1
1
  class Howl
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.0'
3
3
  end
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.1.1
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-08-25 00:00:00.000000000 Z
11
+ date: 2013-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack