aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb24
-rw-r--r--activemodel/lib/active_model/deprecated_mass_assignment_security.rb21
-rw-r--r--activemodel/lib/active_model/dirty.rb38
-rw-r--r--activemodel/lib/active_model/errors.rb16
-rw-r--r--activemodel/lib/active_model/secure_password.rb20
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb2
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb4
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb16
8 files changed, 75 insertions, 66 deletions
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 98cde8ba59..f336c759d2 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -1,4 +1,5 @@
require 'thread_safe'
+require 'mutex_m'
module ActiveModel
# Raised when an attribute is not defined.
@@ -218,6 +219,16 @@ module ActiveModel
end
end
+ # Is +new_name+ an alias?
+ def attribute_alias?(new_name)
+ attribute_aliases.key? new_name.to_s
+ end
+
+ # Returns the original name for the alias +name+
+ def attribute_alias(name)
+ attribute_aliases[name.to_s]
+ end
+
# Declares the attributes that should be prefixed and suffixed by
# ActiveModel::AttributeMethods.
#
@@ -322,9 +333,10 @@ module ActiveModel
attribute_method_matchers_cache.clear
end
- # Returns true if the attribute methods defined have been generated.
def generated_attribute_methods #:nodoc:
- @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
+ @generated_attribute_methods ||= Module.new {
+ extend Mutex_m
+ }.tap { |mod| include mod }
end
protected
@@ -388,14 +400,6 @@ module ActiveModel
AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
def initialize(options = {})
- if options[:prefix] == '' || options[:suffix] == ''
- message = "Specifying an empty prefix/suffix for an attribute method is no longer " \
- "necessary. If the un-prefixed/suffixed version of the method has not been " \
- "defined when `define_attribute_methods` is called, it will be defined " \
- "automatically."
- ActiveSupport::Deprecation.warn message
- end
-
@prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '')
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
@method_missing_target = "#{@prefix}attribute#{@suffix}"
diff --git a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
deleted file mode 100644
index 1f409c87b9..0000000000
--- a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module ActiveModel
- module DeprecatedMassAssignmentSecurity # :nodoc:
- extend ActiveSupport::Concern
-
- module ClassMethods # :nodoc:
- def attr_protected(*args)
- raise "`attr_protected` is extracted out of Rails into a gem. " \
- "Please use new recommended protection model for params" \
- "(strong_parameters) or add `protected_attributes` to your " \
- "Gemfile to use old one."
- end
-
- def attr_accessible(*args)
- raise "`attr_accessible` is extracted out of Rails into a gem. " \
- "Please use new recommended protection model for params" \
- "(strong_parameters) or add `protected_attributes` to your " \
- "Gemfile to use old one."
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index ea5ddf71de..98e7d84608 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -14,13 +14,9 @@ module ActiveModel
# track.
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
# attribute.
- #
- # If you wish to also track previous changes on save or update, you need to
- # add:
- #
- # @previously_changed = changes
- #
- # inside of your save or update method.
+ # * Call <tt>changes_applied</tt> after the changes are persisted.
+ # * Call <tt>reset_changes</tt> when you want to reset the changes
+ # information.
#
# A minimal implementation could be:
#
@@ -39,8 +35,12 @@ module ActiveModel
# end
#
# def save
- # @previously_changed = changes
- # @changed_attributes.clear
+ # # do persistence work
+ # changes_applied
+ # end
+ #
+ # def reload!
+ # reset_changes
# end
# end
#
@@ -65,6 +65,12 @@ module ActiveModel
# person.changed? # => false
# person.name_changed? # => false
#
+ # Reset the changes:
+ #
+ # person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
+ # person.reload!
+ # person.previous_changes # => {}
+ #
# Assigning the same value leaves the attribute unchanged:
#
# person.name = 'Bill'
@@ -129,7 +135,7 @@ module ActiveModel
# person.save
# person.previous_changes # => {"name" => ["bob", "robert"]}
def previous_changes
- @previously_changed
+ @previously_changed ||= {}
end
# Returns a hash of the attributes with unsaved changes indicating their original
@@ -154,6 +160,18 @@ module ActiveModel
private
+ # Removes current changes and makes them accessible through +previous_changes+.
+ def changes_applied
+ @previously_changed = changes
+ @changed_attributes = {}
+ end
+
+ # Removes all dirty data: current changes and previous changes
+ def reset_changes
+ @previously_changed = {}
+ @changed_attributes = {}
+ end
+
# Handle <tt>*_change</tt> for +method_missing+.
def attribute_change(attr)
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 0d7efab04b..cf7551e4f4 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -50,7 +50,7 @@ module ActiveModel
#
# The above allows you to do:
#
- # p = Person.new
+ # person = Person.new
# person.validate! # => ["can not be nil"]
# person.errors.full_messages # => ["name can not be nil"]
# # etc..
@@ -238,8 +238,8 @@ module ActiveModel
# object. You can pass the <tt>:full_messages</tt> option. This determines
# if the json object should contain full messages or not (false by default).
#
- # person.as_json # => {:name=>["can not be nil"]}
- # person.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
+ # person.errors.as_json # => {:name=>["can not be nil"]}
+ # person.errors.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
def as_json(options=nil)
to_hash(options && options[:full_messages])
end
@@ -247,8 +247,8 @@ module ActiveModel
# Returns a Hash of attributes with their error messages. If +full_messages+
# is +true+, it will contain full messages (see +full_message+).
#
- # person.to_hash # => {:name=>["can not be nil"]}
- # person.to_hash(true) # => {:name=>["name can not be nil"]}
+ # person.errors.to_hash # => {:name=>["can not be nil"]}
+ # person.errors.to_hash(true) # => {:name=>["name can not be nil"]}
def to_hash(full_messages = false)
if full_messages
messages = {}
@@ -289,7 +289,7 @@ module ActiveModel
# # => NameIsInvalid: name is invalid
#
# person.errors.messages # => {}
- def add(attribute, message = nil, options = {})
+ def add(attribute, message = :invalid, options = {})
message = normalize_message(attribute, message, options)
if exception = options[:strict]
exception = ActiveModel::StrictValidationFailed if exception == true
@@ -331,7 +331,7 @@ module ActiveModel
#
# person.errors.add :name, :blank
# person.errors.added? :name, :blank # => true
- def added?(attribute, message = nil, options = {})
+ def added?(attribute, message = :invalid, options = {})
message = normalize_message(attribute, message, options)
self[attribute].include? message
end
@@ -437,8 +437,6 @@ module ActiveModel
private
def normalize_message(attribute, message, options)
- message ||= :invalid
-
case message
when Symbol
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 7156f1bb30..17fafe4be9 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -2,7 +2,9 @@ module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
- class << self; attr_accessor :min_cost; end
+ class << self
+ attr_accessor :min_cost # :nodoc:
+ end
self.min_cost = false
module ClassMethods
@@ -18,9 +20,9 @@ module ActiveModel
# value to the password_confirmation attribute and the validation
# will not be triggered.
#
- # You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:
+ # You need to add bcrypt-ruby (~> 3.1.2) to Gemfile to use #has_secure_password:
#
- # gem 'bcrypt-ruby', '~> 3.0.0'
+ # gem 'bcrypt-ruby', '~> 3.1.2'
#
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
#
@@ -44,7 +46,7 @@ module ActiveModel
# This is to avoid ActiveModel (and by extension the entire framework)
# being dependent on a binary library.
begin
- gem 'bcrypt-ruby', '~> 3.0.0'
+ gem 'bcrypt-ruby', '~> 3.1.2'
require 'bcrypt'
rescue LoadError
$stderr.puts "You don't have bcrypt-ruby installed in your application. Please add it to your Gemfile and run bundle install"
@@ -56,9 +58,9 @@ module ActiveModel
include InstanceMethodsOnActivation
if options.fetch(:validations, true)
- validates_confirmation_of :password, if: lambda { |m| m.password.present? }
+ validates_confirmation_of :password, if: :should_confirm_password?
validates_presence_of :password, on: :create
- validates_presence_of :password_confirmation, if: lambda { |m| m.password.present? }
+ validates_presence_of :password_confirmation, if: :should_confirm_password?
before_create { raise "Password digest missing on new record" if password_digest.blank? }
end
@@ -109,6 +111,12 @@ module ActiveModel
def password_confirmation=(unencrypted_password)
@password_confirmation = unencrypted_password
end
+
+ private
+
+ def should_confirm_password?
+ password_confirmation && password.present?
+ end
end
end
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 2803f69b6f..2864c2ba11 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -205,7 +205,7 @@ module ActiveModel
Serializer.new(self, options).serialize(&block)
end
- # Sets the model +attributes+ from a JSON string. Returns +self+.
+ # Sets the model +attributes+ from an XML string. Returns +self+.
#
# class Person
# include ActiveModel::Serializers::Xml
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index cabb9482f2..fde53b9f89 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -58,7 +58,9 @@ module ActiveModel
if options.is_a?(Hash) && options[:on]
options[:if] = Array(options[:if])
options[:on] = Array(options[:on])
- options[:if].unshift("#{options[:on]}.include? self.validation_context")
+ options[:if].unshift lambda { |o|
+ options[:on].include? o.validation_context
+ }
end
set_callback(:validation, :before, *args, &block)
end
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index c5aacb010a..1c35cb7c35 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -15,15 +15,15 @@ module ActiveModel
private
def include?(record, value)
- exclusions = if delimiter.respond_to?(:call)
- delimiter.call(record)
- elsif delimiter.respond_to?(:to_sym)
- record.send(delimiter)
- else
- delimiter
- end
+ members = if delimiter.respond_to?(:call)
+ delimiter.call(record)
+ elsif delimiter.respond_to?(:to_sym)
+ record.send(delimiter)
+ else
+ delimiter
+ end
- exclusions.send(inclusion_method(exclusions), value)
+ members.send(inclusion_method(members), value)
end
def delimiter