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 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