Permalink
Browse files

Add mass_assignment_options support to ActiveRecord

  • Loading branch information...
1 parent a11bc9a commit 9a9f697fd7f00cd9f7752f8007838f68f0ef979a @guilleiguaran committed Sep 19, 2012
View
104 lib/active_record/attribute_sanitization.rb
@@ -1,104 +0,0 @@
-require 'active_model/mass_assignment_security'
-require 'active_record'
-
-module ActiveRecord
- ActiveSupport.on_load(:active_record_config) do
- mattr_accessor :whitelist_attributes, instance_accessor: false
- mattr_accessor :mass_assignment_sanitizer, instance_accessor: false
- end
-
- module AttributeSanitization
- extend ActiveSupport::Concern
- include ActiveModel::MassAssignmentSecurity
-
- included do
- initialize_mass_assignment_sanitizer
- end
-
- module ClassMethods
- def inherited(child) # :nodoc:
- child.send :initialize_mass_assignment_sanitizer if self == Base
- super
- end
-
- private
-
- # The primary key and inheritance column can never be set by mass-assignment for security reasons.
- def attributes_protected_by_default
- default = [ primary_key, inheritance_column ]
- default << 'id' unless primary_key.eql? 'id'
- default
- end
-
- def initialize_mass_assignment_sanitizer
- attr_accessible(nil) if Model.whitelist_attributes
- self.mass_assignment_sanitizer = Model.mass_assignment_sanitizer if Model.mass_assignment_sanitizer
- end
- end
-
- # Allows you to set all the attributes for a particular mass-assignment
- # security role by passing in a hash of attributes with keys matching
- # the attribute names (which again matches the column names) and the role
- # name using the :as option.
- #
- # To bypass mass-assignment security you can use the :without_protection => true
- # option.
- #
- # class User < ActiveRecord::Base
- # attr_accessible :name
- # attr_accessible :name, :is_admin, :as => :admin
- # end
- #
- # user = User.new
- # user.assign_attributes({ :name => 'Josh', :is_admin => true })
- # user.name # => "Josh"
- # user.is_admin? # => false
- #
- # user = User.new
- # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
- # user.name # => "Josh"
- # user.is_admin? # => true
- #
- # user = User.new
- # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
- # user.name # => "Josh"
- # user.is_admin? # => true
- def assign_attributes(new_attributes, options = {})
- return if new_attributes.blank?
-
- attributes = new_attributes.stringify_keys
- multi_parameter_attributes = []
- nested_parameter_attributes = []
- previous_options = @mass_assignment_options
- @mass_assignment_options = options
-
- unless options[:without_protection]
- attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
- end
-
- attributes.each do |k, v|
- if k.include?("(")
- multi_parameter_attributes << [ k, v ]
- elsif v.is_a?(Hash)
- nested_parameter_attributes << [ k, v ]
- else
- _assign_attribute(k, v)
- end
- end
-
- assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
- assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
- ensure
- @mass_assignment_options = previous_options
- end
-
- protected
-
- def mass_assignment_role
- mass_assignment_options[:as] || :default
- end
- end
-end
-
-ActiveRecord::Base.send :include, ActiveRecord::AttributeSanitization
-ActiveRecord::SchemaMigration.attr_accessible(:version)
View
47 lib/active_record/mass_assignment_security.rb
@@ -0,0 +1,47 @@
+require "active_record"
+require "active_record/mass_assignment_security/associations"
+require "active_record/mass_assignment_security/attribute_assignment"
+require "active_record/mass_assignment_security/core"
+require "active_record/mass_assignment_security/nested_attributes"
+require "active_record/mass_assignment_security/persistence"
+require "active_record/mass_assignment_security/reflection"
+require "active_record/mass_assignment_security/relation"
+require "active_record/mass_assignment_security/validations"
+require "active_record/mass_assignment_security/associations"
+
+class ActiveRecord::Base
+ include ActiveRecord::MassAssignmentSecurity::Core
+ include ActiveRecord::MassAssignmentSecurity::AttributeAssignment
+ include ActiveRecord::MassAssignmentSecurity::Persistence
+ include ActiveRecord::MassAssignmentSecurity::Relation
+ include ActiveRecord::MassAssignmentSecurity::Validations
+ include ActiveRecord::MassAssignmentSecurity::NestedAttributes
+end
+
+class ActiveRecord::Reflection::AssociationReflection
+ include ActiveRecord::MassAssignmentSecurity::Reflection::AssociationReflection
+end
+
+module ActiveRecord::Associations
+ class Association
+ include ActiveRecord::MassAssignmentSecurity::Associations::Association
+ end
+
+ class CollectionAssociation
+ include ActiveRecord::MassAssignmentSecurity::Associations::CollectionAssociation
+ end
+
+ class CollectionProxy
+ include ActiveRecord::MassAssignmentSecurity::Associations::CollectionProxy
+ end
+
+ class HasManyThroughAssociation
+ include ActiveRecord::MassAssignmentSecurity::Associations::HasManyThroughAssociation
+ end
+
+ class SingularAssociation
+ include ActiveRecord::MassAssignmentSecurity::Associations::SingularAssociation
+ end
+end
+
+ActiveRecord::SchemaMigration.attr_accessible(:version)
View
118 lib/active_record/mass_assignment_security/associations.rb
@@ -0,0 +1,118 @@
+module ActiveRecord
+ module MassAssignmentSecurity
+ module Associations
+ module Association
+ def build_record(attributes, options)
+ reflection.build_association(attributes, options) do |record|
+ attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
+ record.assign_attributes(attributes, without_protection: true)
+ end
+ end
+
+ private :build_record
+ end
+
+ module CollectionAssociation
+ def build(attributes = {}, options = {}, &block)
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| build(attr, options, &block) }
+ else
+ add_to_target(build_record(attributes, options)) do |record|
+ yield(record) if block_given?
+ end
+ end
+ end
+
+ def create(attributes = {}, options = {}, &block)
+ create_record(attributes, options, &block)
+ end
+
+ def create!(attributes = {}, options = {}, &block)
+ create_record(attributes, options, true, &block)
+ end
+
+ def create_record(attributes, options, raise = false, &block)
+ unless owner.persisted?
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
+ end
+
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create_record(attr, options, raise, &block) }
+ else
+ transaction do
+ add_to_target(build_record(attributes, options)) do |record|
+ yield(record) if block_given?
+ insert_record(record, true, raise)
+ end
+ end
+ end
+ end
+
+ private :create_record
+ end
+
+ module CollectionProxy
+ def build(attributes = {}, options = {}, &block)
+ @association.build(attributes, options, &block)
+ end
+
+ def create(attributes = {}, options = {}, &block)
+ @association.create(attributes, options, &block)
+ end
+
+ def create!(attributes = {}, options = {}, &block)
+ @association.create!(attributes, options, &block)
+ end
+ end
+
+ module HasManyThroughAssociation
+ def build_record(attributes, options = {})
+ ensure_not_nested
+
+ record = super(attributes, options)
+
+ inverse = source_reflection.inverse_of
+ if inverse
+ if inverse.macro == :has_many
+ record.send(inverse.name) << build_through_record(record)
+ elsif inverse.macro == :has_one
+ record.send("#{inverse.name}=", build_through_record(record))
+ end
+ end
+
+ record
+ end
+
+ private :build_record
+ end
+
+ module SingularAssociation
+ def create(attributes = {}, options = {}, &block)
+ create_record(attributes, options, &block)
+ end
+
+ def create!(attributes = {}, options = {}, &block)
+ create_record(attributes, options, true, &block)
+ end
+
+ def build(attributes = {}, options = {})
+ record = build_record(attributes, options)
+ yield(record) if block_given?
+ set_new_record(record)
+ record
+ end
+
+ def create_record(attributes, options = {}, raise_error = false)
+ record = build_record(attributes, options)
+ yield(record) if block_given?
+ saved = record.save
+ set_new_record(record)
+ raise RecordInvalid.new(record) if !saved && raise_error
+ record
+ end
+
+ private :create_record
+ end
+ end
+ end
+end
View
107 lib/active_record/mass_assignment_security/attribute_assignment.rb
@@ -0,0 +1,107 @@
+require 'active_model/mass_assignment_security'
+require 'active_record'
+
+module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :whitelist_attributes, instance_accessor: false
+ mattr_accessor :mass_assignment_sanitizer, instance_accessor: false
+ end
+
+ module MassAssignmentSecurity
+ module AttributeAssignment
+ extend ActiveSupport::Concern
+ include ActiveModel::MassAssignmentSecurity
+
+ included do
+ initialize_mass_assignment_sanitizer
+ end
+
+ module ClassMethods
+ def inherited(child) # :nodoc:
+ child.send :initialize_mass_assignment_sanitizer if self == Base
+ super
+ end
+
+ private
+
+ # The primary key and inheritance column can never be set by mass-assignment for security reasons.
+ def attributes_protected_by_default
+ default = [ primary_key, inheritance_column ]
+ default << 'id' unless primary_key.eql? 'id'
+ default
+ end
+
+ def initialize_mass_assignment_sanitizer
+ attr_accessible(nil) if Model.whitelist_attributes
+ self.mass_assignment_sanitizer = Model.mass_assignment_sanitizer if Model.mass_assignment_sanitizer
+ end
+ end
+
+ # Allows you to set all the attributes for a particular mass-assignment
+ # security role by passing in a hash of attributes with keys matching
+ # the attribute names (which again matches the column names) and the role
+ # name using the :as option.
+ #
+ # To bypass mass-assignment security you can use the :without_protection => true
+ # option.
+ #
+ # class User < ActiveRecord::Base
+ # attr_accessible :name
+ # attr_accessible :name, :is_admin, :as => :admin
+ # end
+ #
+ # user = User.new
+ # user.assign_attributes({ :name => 'Josh', :is_admin => true })
+ # user.name # => "Josh"
+ # user.is_admin? # => false
+ #
+ # user = User.new
+ # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
+ # user.name # => "Josh"
+ # user.is_admin? # => true
+ #
+ # user = User.new
+ # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
+ # user.name # => "Josh"
+ # user.is_admin? # => true
+ def assign_attributes(new_attributes, options = {})
+ return if new_attributes.blank?
+
+ attributes = new_attributes.stringify_keys
+ multi_parameter_attributes = []
+ nested_parameter_attributes = []
+ previous_options = @mass_assignment_options
+ @mass_assignment_options = options
+
+ unless options[:without_protection]
+ attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
+ end
+
+ attributes.each do |k, v|
+ if k.include?("(")
+ multi_parameter_attributes << [ k, v ]
+ elsif v.is_a?(Hash)
+ nested_parameter_attributes << [ k, v ]
+ else
+ _assign_attribute(k, v)
+ end
+ end
+
+ assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
+ assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
+ ensure
+ @mass_assignment_options = previous_options
+ end
+
+ protected
+
+ def mass_assignment_options
+ @mass_assignment_options ||= {}
+ end
+
+ def mass_assignment_role
+ mass_assignment_options[:as] || :default
+ end
+ end
+ end
+end
View
27 lib/active_record/mass_assignment_security/core.rb
@@ -0,0 +1,27 @@
+module ActiveRecord
+ module MassAssignmentSecurity
+ module Core
+ def initialize(attributes = nil, options = {})
+ defaults = self.class.column_defaults.dup
+ defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
+
+ @attributes = self.class.initialize_attributes(defaults)
+ @columns_hash = self.class.column_types.dup
+
+ init_internals
+ ensure_proper_type
+ populate_with_current_scope_attributes
+
+ assign_attributes(attributes, options) if attributes
+
+ yield self if block_given?
+ run_callbacks :initialize unless _initialize_callbacks.empty?
+ end
+
+ def init_internals
+ super
+ @mass_assignment_options = nil
+ end
+ end
+ end
+end
View
148 lib/active_record/mass_assignment_security/nested_attributes.rb
@@ -0,0 +1,148 @@
+module ActiveRecord
+ module MassAssignmentSecurity
+ module NestedAttributes
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def accepts_nested_attributes_for(*attr_names)
+ options = { :allow_destroy => false, :update_only => false }
+ options.update(attr_names.extract_options!)
+ options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
+ options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
+
+ attr_names.each do |association_name|
+ if reflection = reflect_on_association(association_name)
+ reflection.options[:autosave] = true
+ add_autosave_association_callbacks(reflection)
+
+ nested_attributes_options = self.nested_attributes_options.dup
+ nested_attributes_options[association_name.to_sym] = options
+ self.nested_attributes_options = nested_attributes_options
+
+ type = (reflection.collection? ? :collection : :one_to_one)
+
+ # def pirate_attributes=(attributes)
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
+ # end
+ generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
+ if method_defined?(:#{association_name}_attributes=)
+ remove_method(:#{association_name}_attributes=)
+ end
+ def #{association_name}_attributes=(attributes)
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
+ end
+ eoruby
+ else
+ raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
+ end
+ end
+ end
+ end
+
+ private
+
+ UNASSIGNABLE_KEYS = %w( id _destroy )
+
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {})
+ options = self.nested_attributes_options[association_name]
+ attributes = attributes.with_indifferent_access
+
+ if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
+ (options[:update_only] || record.id.to_s == attributes['id'].to_s)
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes)
+
+ elsif attributes['id'].present? && !assignment_opts[:without_protection]
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
+
+ elsif !reject_new_record?(association_name, attributes)
+ method = "build_#{association_name}"
+ if respond_to?(method)
+ send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
+ else
+ raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
+ end
+ end
+ end
+
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
+ options = self.nested_attributes_options[association_name]
+
+ unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
+ raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
+ end
+
+ if limit = options[:limit]
+ limit = case limit
+ when Symbol
+ send(limit)
+ when Proc
+ limit.call
+ else
+ limit
+ end
+
+ if limit && attributes_collection.size > limit
+ raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
+ end
+ end
+
+ if attributes_collection.is_a? Hash
+ keys = attributes_collection.keys
+ attributes_collection = if keys.include?('id') || keys.include?(:id)
+ [attributes_collection]
+ else
+ attributes_collection.values
+ end
+ end
+
+ association = association(association_name)
+
+ existing_records = if association.loaded?
+ association.target
+ else
+ attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
+ attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
+ end
+
+ attributes_collection.each do |attributes|
+ attributes = attributes.with_indifferent_access
+
+ if attributes['id'].blank?
+ unless reject_new_record?(association_name, attributes)
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
+ end
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
+ unless association.loaded? || call_reject_if(association_name, attributes)
+ # Make sure we are operating on the actual object which is in the association's
+ # proxy_target array (either by finding it, or adding it if not found)
+ target_record = association.target.detect { |record| record == existing_record }
+
+ if target_record
+ existing_record = target_record
+ else
+ association.add_to_target(existing_record)
+ end
+ end
+
+ if !call_reject_if(association_name, attributes)
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts)
+ end
+ elsif assignment_opts[:without_protection]
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
+ else
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
+ end
+ end
+ end
+
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts)
+ record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
+ record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
+ end
+
+ def unassignable_keys(assignment_opts)
+ assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS
+ end
+ end
+ end
+end
View
81 lib/active_record/mass_assignment_security/persistence.rb
@@ -0,0 +1,81 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module MassAssignmentSecurity
+ # = Active Record Persistence
+ module Persistence
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
+ # The resulting object is returned whether the object was saved successfully to the database or not.
+ #
+ # The +attributes+ parameter can be either 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 role
+ # 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' }])
+ #
+ # # Create a single object and pass it into a block to set other attributes.
+ # User.create(:first_name => 'Jamie') do |u|
+ # u.is_admin = false
+ # end
+ #
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
+ # u.is_admin = false
+ # end
+ def create(attributes = nil, options = {}, &block)
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create(attr, options, &block) }
+ else
+ object = new(attributes, options, &block)
+ object.save
+ object
+ end
+ end
+ end
+
+ # Updates the attributes of the model from the passed-in hash and saves the
+ # record, all wrapped in a transaction. If the object is invalid, the saving
+ # will fail and false will be returned.
+ #
+ # When updating model attributes, mass-assignment security protection is respected.
+ # If no +:as+ option is supplied then the +:default+ role will be used.
+ # If you want to bypass the forbidden attributes protection then you can do so using
+ # the +:without_protection+ option.
+ def update_attributes(attributes, options = {})
+ # The following transaction covers any possible database side-effects of the
+ # attributes assignment. For example, setting the IDs of a child collection.
+ with_transaction_returning_status do
+ assign_attributes(attributes, options)
+ save
+ end
+ end
+
+ # Updates its receiver just like +update_attributes+ but calls <tt>save!</tt> instead
+ # of +save+, so an exception is raised if the record is invalid.
+ def update_attributes!(attributes, options = {})
+ # The following transaction covers any possible database side-effects of the
+ # attributes assignment. For example, setting the IDs of a child collection.
+ with_transaction_returning_status do
+ assign_attributes(attributes, options)
+ save!
+ end
+ end
+ end
+ end
+end
View
11 lib/active_record/mass_assignment_security/reflection.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module MassAssignmentSecurity
+ module Reflection
+ module AssociationReflection
+ def build_association(*options, &block)
+ klass.new(*options, &block)
+ end
+ end
+ end
+ end
+end
View
47 lib/active_record/mass_assignment_security/relation.rb
@@ -0,0 +1,47 @@
+module ActiveRecord
+ module MassAssignmentSecurity
+ module Relation
+ # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
+ #
+ # Expects arguments in the same format as +Base.create+.
+ #
+ # ==== Examples
+ # # Find the first user named Penélope or create a new one.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Penélope or create a new one.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Scarlett or create a new one with a particular last name.
+ # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ #
+ # # Find the first user named Scarlett or create a new one with a different last name.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Scarlett').first_or_create do |user|
+ # user.last_name = "O'Hara"
+ # end
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ def first_or_create(attributes = nil, options = {}, &block)
+ first || create(attributes, options, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
+ #
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
+ def first_or_create!(attributes = nil, options = {}, &block)
+ first || create!(attributes, options, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
+ #
+ # Expects arguments in the same format as <tt>Base.new</tt>.
+ def first_or_initialize(attributes = nil, &block)
+ first || new(attributes, options, &block)
+ end
+ end
+ end
+end
View
24 lib/active_record/mass_assignment_security/validations.rb
@@ -0,0 +1,24 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module MassAssignmentSecurity
+ module Validations
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
+ # so an exception is raised if the record is invalid.
+ def create!(attributes = nil, options = {}, &block)
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create!(attr, options, &block) }
+ else
+ object = new(attributes, options)
+ yield(object) if block_given?
+ object.save!
+ object
+ end
+ end
+ end
+ end
+ end
+end
View
2 lib/protected_attributes.rb
@@ -1,6 +1,6 @@
require "active_model/mass_assignment_security"
require "action_controller/accessible_params_wrapper"
-require "active_record/attribute_sanitization"
+require "active_record/mass_assignment_security"
require "protected_attributes/railtie"
require "protected_attributes/version"
View
2 test/attribute_sanitization_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
require 'ar_helper'
-require 'active_record/attribute_sanitization'
+require 'active_record/mass_assignment_security'
require 'models/company'
require 'models/subscriber'
require 'models/keyboard'

0 comments on commit 9a9f697

Please sign in to comment.