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.
This commit is contained in:
Nicolas Sanguinetti 2009-01-15 04:24:45 -08:00 committed by Ryan Tomayko
parent 4a75d9edfb
commit 1fa9807f4b
3 changed files with 79 additions and 4 deletions

View File

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

@ -331,8 +331,7 @@ module Sinatra
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 @@ module Sinatra
else
{}
end
@params = original_params.dup
@params.merge!(params)
@params = original_params.merge(params)
catch(:pass) {
conditions.each { |cond|
@ -366,6 +364,22 @@ module Sinatra
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 File

@ -119,6 +119,63 @@ describe "Routing" do
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