Skip to content

Commit

Permalink
Add nested attributes to trustable.
Browse files Browse the repository at this point in the history
  • Loading branch information
unders committed Mar 23, 2012
1 parent 2e10fa3 commit 00b5cd5
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 45 deletions.
35 changes: 24 additions & 11 deletions lib/trusted_keys/trustable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def initialize(options)
@scope = options.fetch(:scope)
@trusted_keys = options.fetch(:trusted_keys)
@untrusted = options.fetch(:untrusted)
@nested = options[:nested] || false

self.class.send("attr_accessible", *options.fetch(:keys))
end
Expand All @@ -18,18 +19,15 @@ def attributes(params)
params[key.to_sym] || params[key.to_s]
end

result = sanitize_for_mass_assignment(params)

keys = params.keys.map(&:to_s) - result.keys.map(&:to_s)

unless keys.empty?
untrusted = @untrusted.keys(:scope => @scope,
:key => nil,
:keys => keys)
raise untrusted if untrusted.present?
if @nested
{}.tap do |hash|
params.each do |key, nested_hash|
hash[key] = sanitize(nested_hash)
end
end
else
sanitize(params)
end

remove_untrusted_keys(result)
end

def on_scope(attributes)
Expand All @@ -44,6 +42,21 @@ def level; @scope.size; end

private

def sanitize(params)
result = sanitize_for_mass_assignment(params)

keys = params.keys.map(&:to_s) - result.keys.map(&:to_s)

unless keys.empty?
untrusted = @untrusted.keys(:scope => @scope,
:key => nil,
:keys => keys)
raise untrusted if untrusted.present?
end

remove_untrusted_keys(result)
end

def remove_untrusted_keys(attributes)
trusted_keys = @trusted_keys.select do |trusted|
trusted.level == (level + 1)
Expand Down
84 changes: 50 additions & 34 deletions spec/trusted_keys/trustable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
:controller => "events",
:events => {
:nested_attributes => { "0" => { "_destroy"=>"false",
"dangerous" => "remove me",
"start"=>"2012" },
"new_1331711737056" => { "_destroy"=>"false",
"start"=>"2012" } } },
Expand All @@ -41,41 +42,18 @@ def options(options_hash)
}.merge(options_hash)
end

describe "#on_scope" do
context "level 2" do
it "returns the hash for that level" do
t = klass.new(options(:scope => [:post, :comments],
:trusted_keys => [],
:keys => [:body]))
t.on_scope(params[:post]).must_equal(params[:post])
end
end

context "level 3" do
it "returns the hash for that level" do
t = klass.new(options(:scope => [:post, :comments, :author],
:trusted_keys => [],
:keys => [:name]))
t.on_scope(params[:post]).must_equal(params[:post][:comments])
end
end
describe "nested attributes" do
it "returns nested hash" do
post = klass.new(options(:scope => [:events, :nested_attributes],
:trusted_keys => [],
:nested => true,
:keys => [ "start", "_destroy" ]))

context "level 1" do
it "not applicable" do
t = klass.new(options(:scope => [:post],
:trusted_keys => [],
:keys => [:body]))
proc { t.on_scope(params) }.must_raise NoMethodError
end
end

context "level 0" do
it "not applicable" do
t = klass.new(options(:scope => [],
:trusted_keys => [],
:keys => [:body]))
proc { t.on_scope(params) }.must_raise NoMethodError
end
expected = { "0" => { "_destroy"=>"false",
"start"=>"2012" },
"new_1331711737056" => { "_destroy"=>"false",
"start"=>"2012" } }
post.attributes(params).must_equal(expected)
end
end

Expand Down Expand Up @@ -176,6 +154,44 @@ def options(options_hash)
end
end

describe "#on_scope" do
context "level 2" do
it "returns the hash for that level" do
t = klass.new(options(:scope => [:post, :comments],
:trusted_keys => [],
:keys => [:body]))
t.on_scope(params[:post]).must_equal(params[:post])
end
end

context "level 3" do
it "returns the hash for that level" do
t = klass.new(options(:scope => [:post, :comments, :author],
:trusted_keys => [],
:keys => [:name]))
t.on_scope(params[:post]).must_equal(params[:post][:comments])
end
end

context "level 1" do
it "not applicable" do
t = klass.new(options(:scope => [:post],
:trusted_keys => [],
:keys => [:body]))
proc { t.on_scope(params) }.must_raise NoMethodError
end
end

context "level 0" do
it "not applicable" do
t = klass.new(options(:scope => [],
:trusted_keys => [],
:keys => [:body]))
proc { t.on_scope(params) }.must_raise NoMethodError
end
end
end

describe "#key" do
context "scope is empty" do
it "returns nil" do
Expand Down

0 comments on commit 00b5cd5

Please sign in to comment.