Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added support for Rails style nested attributes

  • Loading branch information...
commit 9e965b13f8e16e13b6540f9fa2f621274971c3e3 1 parent c36497c
Ivan Navarrete and Jonas Nicklas authored
View
13 README.md
@@ -116,13 +116,14 @@ Using Rails' skip_before_filter to make a controller insecure again
end
-## TODO
+## Nested attributes
-* Mark a parameter that expects an array as its value:
- * param_accessible :user => ["group_ids[]"]
- * would accept [10,12] or {"0" => 10, "1" => 12}
- * param_accessible :user => {"groups[]" => [:name]}
- * would accept [{:name => "Admins"}, {:name => "Users"}]
+Arrays of attributes like the ones used with Rails' nested forms feature can be specified using
+the following syntax:
+
+ class DemoController < ApplicationController
+ param_accessible :"foo[]" => [:bar, :baz]
+ end
## Contributing
View
29 lib/param_accessible/rule.rb
@@ -47,7 +47,9 @@ def accessible_params_for controller, dest
def accessible_hash_for params, attributes, dest
attributes.each do |key, value|
- if value.is_a?(Hash)
+ if key.to_s =~ /\[\]$/
+ accessible_array_for key, params, value, dest
+ elsif value.is_a?(Hash)
attrs = dest[key]
if attrs.nil?
attrs = {}
@@ -75,7 +77,30 @@ def accessible_params_for_regex regex, params, dest
dest
end
-
+
+ def accessible_array_for key, params, value, dest
+ key = key.to_s.chomp('[]')
+
+ if params and params[key].is_a? Hash
+ params[key].each do |index, nested_params|
+ dest[key] ||= {}
+ attrs = dest[key][index] = {}
+ accessible_hash_for nested_params, value, attrs if value
+ end
+ elsif params and params[key].is_a? Array
+ params[key].each do |nested_params|
+ if nested_params.is_a? Hash
+ dest[key] ||= []
+ attrs = {}
+ accessible_hash_for nested_params, value, attrs if value
+ dest[key].push(attrs)
+ else
+ dest[key] = nil
+ end
+ end
+ end
+ end
+
# When specifying params to protect, we allow a combination of arrays and hashes much like how
# ActiveRecord::Base#find's :include options works. This method normalizes that into just nested hashes,
# stringifying the keys and setting all values to nil. This format is easier/faster to work with when
View
5 lib/param_accessible/rules.rb
@@ -33,10 +33,9 @@ def detect_inaccessible_hash hash, accessible, errors, prefix = nil
detect_inaccessible_hash value, nested, errors, prefix_for(prefix, key)
elsif value.is_a?(Array)
- nested = accessible[key] || {}
- value.each do |v|
+ value.each_with_index do |v, i|
if v.is_a?(Hash)
- detect_inaccessible_hash v, nested, errors, prefix_for(prefix, key)
+ detect_inaccessible_hash v, accessible[key][i], errors, prefix_for(prefix_for(prefix, key), "")
end
end
end
View
4 spec/app_root/app/controllers/nested_controller.rb
@@ -0,0 +1,4 @@
+class NestedController < ApplicationController
+ param_accessible :a => [{:"b[]" => [:d, :e, :q]}, :c]
+ param_accessible :"o[]", :p
+end
View
88 spec/lib/nested_spec.rb
@@ -0,0 +1,88 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe NestedController do
+ include RSpec::Rails::ControllerExampleGroup
+
+ it "should allow arrays given as rails style pseudo hashes" do
+ post :create, :a => { :b => {"0" => {"d"=>"foo", "e"=>"bar"}, "1" => {"d"=>"foo", "e"=>"bar"}}}
+ response.code.should == "200"
+ end
+
+ it "should allow arrays given as arrays hashes" do
+ post :create, :a => { :b => [{"d"=>"foo", "e"=>"bar"}, {"d"=>"foo", "e"=>"bar"}]}
+ response.code.should == "200"
+ end
+
+ it "should allow arrays given as arrays hashes" do
+ post :create, :o => ["foo", "bar"]
+ response.code.should == "200"
+ end
+
+ it "should allow arrays given as rails style hash arrays when only arrays of strings are allowed" do
+ post :create, :o => { "0" => "foo", "1" => "bar" }
+ response.code.should == "200"
+ end
+
+ it "should not allow options which are not in the list" do
+ begin
+ post :create, :a => { :b => {"0" => {"x"=>"foo" }}}
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(a[b][0][x])
+ end
+ end
+
+ it "should not allow strings for an array" do
+ begin
+ post :create, :a => { :b => "foo" }
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(a[b])
+ end
+ end
+
+ it "should not allow arrays with invalid values" do
+ begin
+ post :create, :a => { :b => [{"x"=>"foo"}]}
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(a[b][][x])
+ end
+ end
+
+ it "should not allow arrays with invalid values at other index" do
+ begin
+ post :create, :a => { :b => [{"d" => "foo"}, {"x"=>"foo"}]}
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(a[b][][x])
+ end
+ end
+
+ it "should not allow hashes when only arrays of strings are allowed" do
+ begin
+ post :create, :o => [{ :foo => "bar" }]
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(o[][foo])
+ end
+ end
+
+ it "should not allow string when only arrays are allowed" do
+ begin
+ post :create, :o => "foo"
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(o)
+ end
+ end
+
+ it "should not allow arrays given as rails style hash arrays containing hashes when only arrays of strings are allowed" do
+ begin
+ post :create, :o => { "0" => { "x" => "foo" } }
+ raise "should fail"
+ rescue ParamAccessible::Error => e
+ e.inaccessible_params.should == %w(o[0][x])
+ end
+ end
+end
View
7 spec/lib/simple_spec.rb
@@ -45,12 +45,7 @@
e.inaccessible_params.should == %w(bar[unknown])
end
end
-
- it "should handle nested arrays" do
- post :create, :bar => [:baz => 'hi']
- response.code.should == '200'
- end
-
+
it "should be fine when the ONLY value is a value but not an options hash" do
SimpleController.param_accessible :bar => []
end
Please sign in to comment.
Something went wrong with that request. Please try again.