Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Nested params (e.g., "post[title]=Hello") [#70]

This is based largely on manveru's example implementation:

http://paste.linuxhelp.tv/pastes/view/15309

NOTE: we should plan on ripping this out once nested params
makes it into Rack.
  • Loading branch information...
commit 1fa9807f4b99e6b61e6111cf9e99b8255fb0d217 1 parent 4a75d9e
@foca foca authored rtomayko committed
Showing with 79 additions and 4 deletions.
  1. +4 −0 CHANGES
  2. +18 −4 lib/sinatra/base.rb
  3. +57 −0 test/routing_test.rb
View
4 CHANGES
@@ -8,6 +8,10 @@
Documentation on using these features is forth-coming; the following
provides the basic gist: http://gist.github.com/38605
+ * Parameters with subscripts are now parsed into a nested/recursive
+ Hash structure. e.g., "post[title]=Hello&post[body]=World" yields
+ params: {'post' => {'title' => 'Hello', 'body' => 'World'}}.
+
* Regular expressions may now be used in route pattens; captures are
available at "params[:captures]".
View
22 lib/sinatra/base.rb
@@ -331,8 +331,7 @@ def dispatch!
self.class.filters.each {|block| instance_eval(&block)}
if routes = self.class.routes[@request.request_method]
path = @request.path_info
- original_params = Hash.new{ |hash,k| hash[k.to_s] if Symbol === k }
- original_params.merge! @request.params
+ original_params = nested_params(@request.params)
routes.each do |pattern, keys, conditions, method_name|
if pattern =~ path
@@ -352,8 +351,7 @@ def dispatch!
else
{}
end
- @params = original_params.dup
- @params.merge!(params)
+ @params = original_params.merge(params)
catch(:pass) {
conditions.each { |cond|
@@ -366,6 +364,22 @@ def dispatch!
raise NotFound
end
+ def nested_params(params)
+ return indifferent_hash.merge(params) if !params.keys.join.include?('[')
+ params.inject indifferent_hash do |res, (key,val)|
+ if key =~ /\[.*\]/
+ splat = key.scan(/(^[^\[]+)|\[([^\]]+)\]/).flatten.compact
+ head, last = splat[0..-2], splat[-1]
+ head.inject(res){ |s,v| s[v] ||= indifferent_hash }[last] = val
+ end
+ res
+ end
+ end
+
+ def indifferent_hash
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
+ end
+
def invoke(handler)
res = catch(:halt) {
if handler.respond_to?(:call)
View
57 test/routing_test.rb
@@ -119,6 +119,63 @@
assert ok?
end
+ it "supports basic nested params" do
+ mock_app {
+ get '/hi' do
+ params["person"]["name"]
+ end
+ }
+
+ get "/hi?person[name]=John+Doe"
+ assert ok?
+ assert_equal "John Doe", body
+ end
+
+ it "exposes nested params with indifferent hash" do
+ mock_app {
+ get '/testme' do
+ assert_equal 'baz', params['bar']['foo']
+ assert_equal 'baz', params['bar'][:foo]
+ 'well, alright'
+ end
+ }
+ get '/testme?bar[foo]=baz'
+ assert_equal 'well, alright', body
+ end
+
+ it "supports deeply nested params" do
+ input = {
+ 'browser[chrome][engine][name]' => 'V8',
+ 'browser[chrome][engine][version]' => '1.0',
+ 'browser[firefox][engine][name]' => 'spidermonkey',
+ 'browser[firefox][engine][version]' => '1.7.0',
+ 'emacs[map][goto-line]' => 'M-g g',
+ 'emacs[version]' => '22.3.1',
+ 'paste[name]' => 'hello world',
+ 'paste[syntax]' => 'ruby'
+ }
+ expected = {
+ "emacs" => {
+ "map" => { "goto-line" => "M-g g" },
+ "version" => "22.3.1"
+ },
+ "browser" => {
+ "firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
+ "chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
+ },
+ "paste" => {"name"=>"hello world", "syntax"=>"ruby"}
+ }
+ mock_app {
+ get '/foo' do
+ assert_equal expected, params
+ 'looks good'
+ end
+ }
+ get "/foo?#{param_string(input)}"
+ assert ok?
+ assert_equal 'looks good', body
+ end
+
it "supports paths that include spaces" do
mock_app {
get '/path with spaces' do
Please sign in to comment.
Something went wrong with that request. Please try again.