diff --git a/README.md b/README.md
index 2b67cf6..3e4a6c4 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
# Protected Attributes Continued
+
-This is the community continued version of `protected_attributes`. It works with Rails 5 only and I recommend you only use it to support legacy portions of your application that you do not want to upgrade. Note that this feature was dropped by the Rails team and switched to strong_parameters because of security issues, just so you understand your risks. This is in use successfully in some of my Rails 5 apps in which security like this is a non-issue. For people who would like to continue using this feature in their Rails 5 apps lets continue the work here.
+This is the community continued version of `protected_attributes`. It works with Rails 5+ only and I recommend you only use it to support legacy portions of your application that you do not want to upgrade. Note that this feature was dropped by the Rails team and switched to strong_parameters because of security issues, just so you understand your risks. This is in use successfully in some of my Rails 5 apps in which security like this is a non-issue. For people who would like to continue using this feature in their Rails 5 apps lets continue the work here. If you are looking for a similar approach see my [recommended alternative](https://github.com/westonganger/protected_attributes_continued#a-better-alternative)
Protect attributes from mass-assignment in Active Record models.
@@ -98,10 +99,49 @@ config.active_record.mass_assignment_sanitizer = :strict
Any protected attributes violation raises `ActiveModel::MassAssignmentSecurity::Error` then.
-# Credits
+## Contributing
-Created and Maintained by [Weston Ganger - @westonganger](https://github.com/westonganger)
+We use the `appraisal` gem for testing multiple versions of `Rails`. Please use the following steps to test using `appraisal`.
+
+1. `bundle exec appraisal install`
+2. `bundle exec appraisal rake test`
+
+## Credits
+
+Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
Originally forked from the dead/unmaintained `protected_attributes` gem by the Rails team.
-
+## A Better Alternative
+
+While I do utilize this gem in some legacy projects I have adopted an alternative approach that is similar to this gem but only utilizes Rails built-in `strong_params` which is a much more future proof way of doing things. See the following example for how to implement.
+
+```ruby
+class Post < ActiveRecord::Base
+
+ def self.strong_params(params)
+ params.permit(:post).permit(:name, :content, :published_at)
+ end
+
+end
+
+class PostsController < ApplicationController
+
+ def create
+ @post = Post.new
+
+ @post.assign_attributes(Post.strong_params(params))
+
+ respond_with @post
+ end
+
+ def update
+ @post = Post.find(params[:id])
+
+ @post.update(Post.strong_params(params))
+
+ respond_with @post
+ end
+
+end
+```
diff --git a/lib/active_record/mass_assignment_security/inheritance.rb b/lib/active_record/mass_assignment_security/inheritance.rb
index c431ad6..1ff2a28 100644
--- a/lib/active_record/mass_assignment_security/inheritance.rb
+++ b/lib/active_record/mass_assignment_security/inheritance.rb
@@ -4,7 +4,7 @@ module Inheritance
extend ActiveSupport::Concern
module ClassMethods
-
+
private
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
@@ -16,3 +16,31 @@ def subclass_from_attributes(attrs)
end
end
end
+
+module ActiveRecord
+ module Inheritance
+ module ClassMethods
+ undef :new
+
+ def new(attributes = nil, options = {}, &block)
+ if abstract_class? || self == Base
+ raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
+ end
+
+ if has_attribute?(inheritance_column)
+ subclass = subclass_from_attributes(attributes)
+
+ if respond_to?(:column_defaults) && subclass.nil? && base_class == self
+ subclass = subclass_from_attributes(column_defaults)
+ end
+ end
+
+ if subclass && subclass != self
+ subclass.new(attributes, options, &block)
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/lib/active_record/mass_assignment_security/relation.rb b/lib/active_record/mass_assignment_security/relation.rb
index 5bede2e..809beec 100644
--- a/lib/active_record/mass_assignment_security/relation.rb
+++ b/lib/active_record/mass_assignment_security/relation.rb
@@ -1,5 +1,8 @@
module ActiveRecord
class Relation
+ undef :new
+ undef :create
+ undef :create!
undef :first_or_create
undef :first_or_create!
undef :first_or_initialize
@@ -7,6 +10,32 @@ class Relation
undef :find_or_create_by
undef :find_or_create_by!
+ def new(attributes = nil, options = {}, &block)
+ attrs = respond_to?(:values_for_create) ? values_for_create(attributes) : attributes
+
+ scoping { klass.new(attrs, options, &block) }
+ end
+
+ def create(attributes = nil, options = {}, &block)
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create(attr, options, &block) }
+ else
+ attrs = respond_to?(:values_for_create) ? values_for_create(attributes) : attributes
+
+ scoping { klass.create(attrs, options, &block) }
+ end
+ end
+
+ def create!(attributes = nil, options = {}, &block)
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create!(attr, options, &block) }
+ else
+ attrs = respond_to?(:values_for_create) ? values_for_create(attributes) : attributes
+
+ scoping { klass.create!(attrs, options, &block) }
+ end
+ end
+
# Tries to load the first record; if it fails, then create is called with the same arguments as this method.
#
# Expects arguments in the same format as +Base.create+.