From 7c5ae0a88fc9406857ee362c827c57eb23fd5f95 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Sun, 1 May 2011 20:20:22 +0200 Subject: Added mass-assignment security :as and :without_protection support to AR.new and AR.create --- activerecord/lib/active_record/base.rb | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 04c12f86b6..0ba401584d 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -475,10 +475,19 @@ module ActiveRecord #:nodoc: # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the # attributes on the objects that are to be created. # + # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options + # in the +options+ parameter. + # # ==== Examples # # Create a single new object # User.create(:first_name => 'Jamie') # + # # Create a single new object using the :admin mass-assignment security scope + # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin) + # + # # Create a single new object bypassing mass-assignment security + # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true) + # # # Create an Array of new objects # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) # @@ -491,11 +500,11 @@ module ActiveRecord #:nodoc: # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u| # u.is_admin = false # end - def create(attributes = nil, &block) + def create(attributes = nil, options = {}, &block) if attributes.is_a?(Array) - attributes.collect { |attr| create(attr, &block) } + attributes.collect { |attr| create(attr, options, &block) } else - object = new(attributes) + object = new(attributes, options) yield(object) if block_given? object.save object @@ -1484,7 +1493,20 @@ end # attributes but not yet saved (pass a hash with key names matching the associated table column names). # In both instances, valid attribute keys are determined by the column names of the associated table -- # hence you can't have attributes that aren't part of the table columns. - def initialize(attributes = nil) + # + # +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options + # in the +options+ parameter. + # + # ==== Examples + # # Instantiates a single new object + # User.new(:first_name => 'Jamie') + # + # # Instantiates a single new object using the :admin mass-assignment security scope + # User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin) + # + # # Instantiates a single new object bypassing mass-assignment security + # User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true) + def initialize(attributes = nil, options = {}) @attributes = attributes_from_column_definition @association_cache = {} @aggregation_cache = {} @@ -1500,7 +1522,8 @@ end set_serialized_attributes populate_with_current_scope_attributes - self.attributes = attributes unless attributes.nil? + + assign_attributes(attributes, options) if attributes result = yield self if block_given? run_callbacks :initialize -- cgit v1.2.3 From 86d7ed33754f80690395309dd307c6d9ecc0022f Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Sun, 1 May 2011 23:30:07 +0200 Subject: singular and collection relations in AR can now specify mass-assignment security options (:as and :without_protection) in build, create and create! methods. --- .../associations/collection_association.rb | 20 ++++++++++---------- .../associations/has_many_through_association.rb | 4 ++-- .../associations/singular_association.rb | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 33a184d48d..6cdec8c487 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -93,20 +93,20 @@ module ActiveRecord first_or_last(:last, *args) end - def build(attributes = {}, &block) - build_or_create(attributes, :build, &block) + def build(attributes = {}, options = {}, &block) + build_or_create(:build, attributes, options, &block) end - def create(attributes = {}, &block) + def create(attributes = {}, options = {}, &block) unless owner.persisted? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" end - build_or_create(attributes, :create, &block) + build_or_create(:create, attributes, options, &block) end - def create!(attrs = {}, &block) - record = create(attrs, &block) + def create!(attrs = {}, options = {}, &block) + record = create(attrs, options, &block) Array.wrap(record).each(&:save!) record end @@ -403,9 +403,9 @@ module ActiveRecord end + existing end - def build_or_create(attributes, method) + def build_or_create(method, attributes, options) records = Array.wrap(attributes).map do |attrs| - record = build_record(attrs) + record = build_record(attrs, options) add_to_target(record) do yield(record) if block_given? @@ -421,8 +421,8 @@ module ActiveRecord raise NotImplementedError end - def build_record(attributes) - reflection.build_association(scoped.scope_for_create.merge(attributes)) + def build_record(attributes, options) + reflection.build_association(scoped.scope_for_create.merge(attributes), options) end def delete_or_destroy(records, method) diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 9d2b29685b..7708228d23 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -60,10 +60,10 @@ module ActiveRecord through_record end - def build_record(attributes) + def build_record(attributes, options = {}) ensure_not_nested - record = super(attributes) + record = super(attributes, options) inverse = source_reflection.inverse_of if inverse diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index 4edbe216be..ea4d73d414 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -17,16 +17,16 @@ module ActiveRecord replace(record) end - def create(attributes = {}) - new_record(:create, attributes) + def create(attributes = {}, options = {}) + new_record(:create, attributes, options) end - def create!(attributes = {}) - build(attributes).tap { |record| record.save! } + def create!(attributes = {}, options = {}) + build(attributes, options).tap { |record| record.save! } end - def build(attributes = {}) - new_record(:build, attributes) + def build(attributes = {}, options = {}) + new_record(:build, attributes, options) end private @@ -44,9 +44,9 @@ module ActiveRecord replace(record) end - def new_record(method, attributes) + def new_record(method, attributes, options) attributes = scoped.scope_for_create.merge(attributes || {}) - record = reflection.send("#{method}_association", attributes) + record = reflection.send("#{method}_association", attributes, options) set_new_record(record) record end -- cgit v1.2.3