aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib')
-rw-r--r--activemodel/lib/active_model.rb7
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb12
-rw-r--r--activemodel/lib/active_model/configuration.rb6
-rw-r--r--activemodel/lib/active_model/dirty.rb5
-rw-r--r--activemodel/lib/active_model/errors.rb20
-rw-r--r--activemodel/lib/active_model/locale/en.yml2
-rw-r--r--activemodel/lib/active_model/model.rb16
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activemodel/lib/active_model/observing.rb68
-rw-r--r--activemodel/lib/active_model/secure_password.rb11
-rw-r--r--activemodel/lib/active_model/serialization.rb23
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb4
-rw-r--r--activemodel/lib/active_model/translation.rb17
-rw-r--r--activemodel/lib/active_model/validations.rb17
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb3
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb3
-rw-r--r--activemodel/lib/active_model/validations/with.rb6
17 files changed, 134 insertions, 88 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 2586147a20..ded1b752df 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -21,8 +21,6 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support'
require 'active_model/version'
@@ -59,5 +57,6 @@ module ActiveModel
end
end
-require 'active_support/i18n'
-I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
+ActiveSupport.on_load(:i18n) do
+ I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
+end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index d211122945..24575438c0 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -180,6 +180,18 @@ module ActiveModel
undefine_attribute_methods
end
+
+ # Allows you to make aliases for attributes.
+ #
+ # class Person
+ # attr_accessor :name
+ # alias_attribute :nickname, :name
+ # end
+ #
+ # person = Person.new
+ # person.nickname = "Bob"
+ # person.nickname # => "Bob"
+ # person.name # => "Bob"
def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
matcher_new = matcher.method_name(new_name).to_s
diff --git a/activemodel/lib/active_model/configuration.rb b/activemodel/lib/active_model/configuration.rb
index 1757c12ebf..ba5a6a2075 100644
--- a/activemodel/lib/active_model/configuration.rb
+++ b/activemodel/lib/active_model/configuration.rb
@@ -95,7 +95,7 @@ module ActiveModel
end
def define
- host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__
+ host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
attr_accessor :#{name}
def #{name}?; !!#{name}; end
CODE
@@ -107,7 +107,7 @@ module ActiveModel
define_method("#{name}?") { !!send(name) }
end
- host.class_eval <<-CODE
+ host.class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}; defined?(@#{name}) ? @#{name} : self.class.#{name}; end
def #{name}?; !!#{name}; end
CODE
@@ -117,7 +117,7 @@ module ActiveModel
define_method("#{name}=") { |val| host.send("#{name}=", val) }
end
else
- class_methods.class_eval <<-CODE, __FILE__, __LINE__
+ class_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}=(val)
singleton_class.class_eval do
remove_possible_method(:#{name})
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index d327913824..3d35b5bb6f 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -83,6 +83,7 @@ module ActiveModel
# in-place attributes.
#
# person.name_will_change!
+ # person.name_change # => ['Bill', 'Bill']
# person.name << 'y'
# person.name_change # => ['Bill', 'Billy']
module Dirty
@@ -151,13 +152,15 @@ module ActiveModel
# Handle <tt>*_will_change!</tt> for +method_missing+.
def attribute_will_change!(attr)
+ return if attribute_changed?(attr)
+
begin
value = __send__(attr)
value = value.duplicable? ? value.clone : value
rescue TypeError, NoMethodError
end
- changed_attributes[attr] = value unless changed_attributes.include?(attr)
+ changed_attributes[attr] = value
end
# Handle <tt>reset_*!</tt> for +method_missing+.
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 042f9cd7e2..aba6618b56 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -3,7 +3,6 @@
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/hash/reverse_merge'
module ActiveModel
# == Active Model Errors
@@ -130,12 +129,12 @@ module ActiveModel
# has more than one error message, yields once for each error message.
#
# p.errors.add(:name, "can't be blank")
- # p.errors.each do |attribute, errors_array|
+ # p.errors.each do |attribute, error|
# # Will yield :name and "can't be blank"
# end
#
# p.errors.add(:name, "must be specified")
- # p.errors.each do |attribute, errors_array|
+ # p.errors.each do |attribute, error|
# # Will yield :name and "can't be blank"
# # then yield :name and "must be specified"
# end
@@ -202,12 +201,12 @@ module ActiveModel
# # <error>name must be specified</error>
# # </errors>
def to_xml(options={})
- to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
+ to_a.to_xml({ :root => "errors", :skip_types => true }.merge!(options))
end
# Returns an Hash that can be used as the JSON representation for this object.
# Options:
- # * <tt>:full_messages</tt> - determines if json object should contain
+ # * <tt>:full_messages</tt> - determines if json object should contain
# full messages or not. Default: <tt>false</tt>.
def as_json(options=nil)
to_hash(options && options[:full_messages])
@@ -217,7 +216,7 @@ module ActiveModel
if full_messages
messages = {}
self.messages.each do |attribute, array|
- messages[attribute] = array.map{|message| full_message(attribute, message) }
+ messages[attribute] = array.map { |message| full_message(attribute, message) }
end
messages
else
@@ -286,7 +285,7 @@ module ActiveModel
# "Name is invalid"
def full_message(attribute, message)
return message if attribute == :base
- attr_name = attribute.to_s.gsub('.', '_').humanize
+ attr_name = attribute.to_s.tr('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
I18n.t(:"errors.format", {
:default => "%{attribute} %{message}",
@@ -347,7 +346,7 @@ module ActiveModel
:model => @base.class.model_name.human,
:attribute => @base.class.human_attribute_name(attribute),
:value => value
- }.merge(options)
+ }.merge!(options)
I18n.translate(key, options)
end
@@ -356,9 +355,10 @@ module ActiveModel
def normalize_message(attribute, message, options)
message ||= :invalid
- if message.is_a?(Symbol)
+ case message
+ when Symbol
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
- elsif message.is_a?(Proc)
+ when Proc
message.call
else
message
diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml
index ba49c6beaa..d17848c861 100644
--- a/activemodel/lib/active_model/locale/en.yml
+++ b/activemodel/lib/active_model/locale/en.yml
@@ -9,7 +9,7 @@ en:
inclusion: "is not included in the list"
exclusion: "is reserved"
invalid: "is invalid"
- confirmation: "doesn't match confirmation"
+ confirmation: "doesn't match %{attribute}"
accepted: "must be accepted"
empty: "can't be empty"
blank: "can't be blank"
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
index 6825fdc653..3af95b09b0 100644
--- a/activemodel/lib/active_model/model.rb
+++ b/activemodel/lib/active_model/model.rb
@@ -2,11 +2,11 @@ module ActiveModel
# == Active Model Basic Model
#
- # Includes the required interface for an object to interact with +ActionPack+,
- # using different +ActiveModel+ modules. It includes model name introspections,
+ # Includes the required interface for an object to interact with <tt>ActionPack</tt>,
+ # using different <tt>ActiveModel</tt> modules. It includes model name introspections,
# conversions, translations and validations. Besides that, it allows you to
# initialize the object with a hash of attributes, pretty much like
- # +ActiveRecord+ does.
+ # <tt>ActiveRecord</tt> does.
#
# A minimal implementation could be:
#
@@ -19,8 +19,8 @@ module ActiveModel
# person.name # => 'bob'
# person.age # => 18
#
- # Note that, by default, +ActiveModel::Model+ implements +persisted?+ to
- # return +false+, which is the most common case. You may want to override it
+ # Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt> to
+ # return <tt>false</tt>, which is the most common case. You may want to override it
# in your class to simulate a different scenario:
#
# class Person
@@ -35,14 +35,14 @@ module ActiveModel
# person = Person.new(:id => 1, :name => 'bob')
# person.persisted? # => true
#
- # Also, if for some reason you need to run code on +initialize+, make sure you
+ # Also, if for some reason you need to run code on <tt>initialize</tt>, make sure you
# call super if you want the attributes hash initialization to happen.
#
# class Person
# include ActiveModel::Model
# attr_accessor :id, :name, :omg
#
- # def initialize(attributes)
+ # def initialize(attributes={})
# super
# @omg ||= true
# end
@@ -52,7 +52,7 @@ module ActiveModel
# person.omg # => true
#
# For more detailed information on other functionalities available, please refer
- # to the specific modules included in +ActiveModel::Model+ (see below).
+ # to the specific modules included in <tt>ActiveModel::Model</tt> (see below).
module Model
def self.included(base)
base.class_eval do
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 5665e10002..2b5fc57a3a 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -54,7 +54,7 @@ module ActiveModel
defaults << options[:default] if options[:default]
defaults << @human
- options = {:scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults}.merge(options.except(:default))
+ options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
I18n.translate(defaults.shift, options)
end
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index 32f2aa46bd..f5ea285ccb 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -4,6 +4,8 @@ require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/enumerable'
+require 'active_support/deprecation'
+require 'active_support/core_ext/object/try'
require 'active_support/descendants_tracker'
module ActiveModel
@@ -69,27 +71,34 @@ module ActiveModel
end
# Notify list of observers of a change.
- def notify_observers(*arg)
- observer_instances.each { |observer| observer.update(*arg) }
+ def notify_observers(*args)
+ observer_instances.each { |observer| observer.update(*args) }
end
# Total number of observers.
- def count_observers
+ def observers_count
observer_instances.size
end
+ def count_observers
+ msg = "count_observers is deprecated in favor of observers_count"
+ ActiveSupport::Deprecation.warn(msg)
+ observers_count
+ end
+
protected
def instantiate_observer(observer) #:nodoc:
# string/symbol
if observer.respond_to?(:to_sym)
- observer.to_s.camelize.constantize.instance
- elsif observer.respond_to?(:instance)
+ observer = observer.to_s.camelize.constantize
+ end
+ if observer.respond_to?(:instance)
observer.instance
else
raise ArgumentError,
- "#{observer} must be a lowercase, underscored class name (or an " +
- "instance of the class itself) responding to the instance " +
- "method. Example: Person.observers = :big_brother # calls " +
+ "#{observer} must be a lowercase, underscored class name (or " +
+ "the class itself) responding to the method :instance. " +
+ "Example: Person.observers = :big_brother # calls " +
"BigBrother.instance"
end
end
@@ -101,17 +110,24 @@ module ActiveModel
end
end
- private
- # Fires notifications to model's observers
- #
- # def save
- # notify_observers(:before_save)
- # ...
- # notify_observers(:after_save)
- # end
- def notify_observers(method)
- self.class.notify_observers(method, self)
- end
+ # Fires notifications to model's observers
+ #
+ # def save
+ # notify_observers(:before_save)
+ # ...
+ # notify_observers(:after_save)
+ # end
+ #
+ # Custom notifications can be sent in a similar fashion:
+ #
+ # notify_observers(:custom_notification, :foo)
+ #
+ # This will call +custom_notification+, passing as arguments
+ # the current object and :foo.
+ #
+ def notify_observers(method, *extra_args)
+ self.class.notify_observers(method, self, *extra_args)
+ end
end
# == Active Model Observers
@@ -186,7 +202,7 @@ module ActiveModel
def observe(*models)
models.flatten!
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
- redefine_method(:observed_classes) { models }
+ singleton_class.redefine_method(:observed_classes) { models }
end
# Returns an array of Classes to observe.
@@ -205,15 +221,12 @@ module ActiveModel
# The class observed by default is inferred from the observer's class name:
# assert_equal Person, PersonObserver.observed_class
def observed_class
- if observed_class_name = name[/(.*)Observer/, 1]
- observed_class_name.constantize
- else
- nil
- end
+ name[/(.*)Observer/, 1].try :constantize
end
end
# Start observing the declared classes and their subclasses.
+ # Called automatically by the instance method.
def initialize
observed_classes.each { |klass| add_observer!(klass) }
end
@@ -224,10 +237,10 @@ module ActiveModel
# Send observed_method(object) if the method exists and
# the observer is enabled for the given object's class.
- def update(observed_method, object, &block) #:nodoc:
+ def update(observed_method, object, *extra_args, &block) #:nodoc:
return unless respond_to?(observed_method)
return if disabled_for?(object)
- send(observed_method, object, &block)
+ send(observed_method, object, *extra_args, &block)
end
# Special method sent by the observed class when it is inherited.
@@ -242,6 +255,7 @@ module ActiveModel
klass.add_observer(self)
end
+ # Returns true if notifications are disabled for this object.
def disabled_for?(object)
klass = object.class
return false unless klass.respond_to?(:observers)
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index e7a57cf691..8711b24124 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -55,17 +55,14 @@ module ActiveModel
module InstanceMethodsOnActivation
# Returns self if the password is correct, otherwise false.
def authenticate(unencrypted_password)
- if BCrypt::Password.new(password_digest) == unencrypted_password
- self
- else
- false
- end
+ BCrypt::Password.new(password_digest) == unencrypted_password && self
end
- # Encrypts the password into the password_digest attribute.
+ # Encrypts the password into the password_digest attribute, only if the
+ # new password is not blank.
def password=(unencrypted_password)
- @password = unencrypted_password
unless unencrypted_password.blank?
+ @password = unencrypted_password
self.password_digest = BCrypt::Password.create(unencrypted_password)
end
end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index 4323ee1e09..00cd05b7ef 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -26,17 +26,18 @@ module ActiveModel
# person.serializable_hash # => {"name"=>"Bob"}
#
# You need to declare an attributes hash which contains the attributes
- # you want to serialize. When called, serializable hash will use
+ # you want to serialize. Attributes must be strings, not symbols.
+ # When called, serializable hash will use
# instance methods that match the name of the attributes hash's keys.
# In order to override this behavior, take a look at the private
- # method read_attribute_for_serialization.
+ # method +read_attribute_for_serialization+.
#
# Most of the time though, you will want to include the JSON or XML
# serializations. Both of these modules automatically include the
- # ActiveModel::Serialization module, so there is no need to explicitly
+ # <tt>ActiveModel::Serialization</tt> module, so there is no need to explicitly
# include it.
#
- # So a minimal implementation including XML and JSON would be:
+ # A minimal implementation including XML and JSON would be:
#
# class Person
# include ActiveModel::Serializers::JSON
@@ -63,7 +64,12 @@ module ActiveModel
# person.to_json # => "{\"name\":\"Bob\"}"
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
#
- # Valid options are <tt>:only</tt>, <tt>:except</tt> and <tt>:methods</tt> .
+ # Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and <tt>include</tt>.
+ # The following are all valid examples:
+ #
+ # person.serializable_hash(:only => 'name')
+ # person.serializable_hash(:include => :address)
+ # person.serializable_hash(:include => { :address => { :only => 'city' }})
module Serialization
def serializable_hash(options = nil)
options ||= {}
@@ -78,12 +84,11 @@ module ActiveModel
hash = {}
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
- method_names = Array(options[:methods]).select { |n| respond_to?(n) }
- method_names.each { |n| hash[n.to_s] = send(n) }
+ Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) }
serializable_add_includes(options) do |association, records, opts|
- hash[association.to_s] = if records.is_a?(Enumerable)
- records.map { |a| a.serializable_hash(opts) }
+ hash[association.to_s] = if records.respond_to?(:to_ary)
+ records.to_ary.map { |a| a.serializable_hash(opts) }
else
records.serializable_hash(opts)
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 5084298210..b78f1ff3f3 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -115,7 +115,9 @@ module ActiveModel
merged_options = opts.merge(options.slice(:builder, :indent))
merged_options[:skip_instruct] = true
- if records.is_a?(Enumerable)
+ if records.respond_to?(:to_ary)
+ records = records.to_ary
+
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
type = options[:skip_types] ? { } : {:type => "array"}
association_name = association.to_s.singularize
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 02b7c54d61..6f0ca92e2a 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/hash/reverse_merge'
-
module ActiveModel
# == Active Model Translation
@@ -43,19 +41,20 @@ module ActiveModel
#
# Specify +options+ with additional translating options.
def human_attribute_name(attribute, options = {})
- defaults = []
+ options = { :count => 1 }.merge!(options)
parts = attribute.to_s.split(".", 2)
attribute = parts.pop
namespace = parts.pop
+ attributes_scope = "#{self.i18n_scope}.attributes"
if namespace
- lookup_ancestors.each do |klass|
- defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
+ defaults = lookup_ancestors.map do |klass|
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
end
- defaults << :"#{self.i18n_scope}.attributes.#{namespace}.#{attribute}"
+ defaults << :"#{attributes_scope}.#{namespace}.#{attribute}"
else
- lookup_ancestors.each do |klass|
- defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
+ defaults = lookup_ancestors.map do |klass|
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}"
end
end
@@ -63,7 +62,7 @@ module ActiveModel
defaults << options.delete(:default) if options[:default]
defaults << attribute.humanize
- options.reverse_merge! :count => 1, :default => defaults
+ options[:default] = defaults
I18n.translate(defaults.shift, options)
end
end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 0e15155b85..3ed72bae3b 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -65,7 +65,7 @@ module ActiveModel
#
# attr_accessor :first_name, :last_name
#
- # validates_each :first_name, :last_name do |record, attr, value|
+ # validates_each :first_name, :last_name, :allow_blank => true do |record, attr, value|
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
# end
# end
@@ -128,6 +128,19 @@ module ActiveModel
# end
# end
#
+ # Options:
+ # * <tt>:on</tt> - Specifies the context where this validation is active
+ # (e.g. <tt>:on => :create</tt> or <tt>:on => :custom_validation_context</tt>)
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
+ # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
+ # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or
+ # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
+ # method, proc or string should return or evaluate to a true or false value.
def validate(*args, &block)
options = args.extract_options!
if options.key?(:on)
@@ -145,7 +158,7 @@ module ActiveModel
_validators.values.flatten.uniq
end
- # List all validators that being used to validate a specific attribute.
+ # List all validators that are being used to validate a specific attribute.
def validators_on(*attributes)
attributes.map do |attribute|
_validators[attribute.to_sym]
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index c39c85e1af..dbafd0bd1a 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -8,7 +8,8 @@ module ActiveModel
# Provides an interface for any class to have <tt>before_validation</tt> and
# <tt>after_validation</tt> callbacks.
#
- # First, extend ActiveModel::Callbacks from the class you are creating:
+ # First, include ActiveModel::Validations::Callbacks from the class you are
+ # creating:
#
# class MyModel
# include ActiveModel::Validations::Callbacks
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index e8526303e2..69ab74734d 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -5,7 +5,8 @@ module ActiveModel
class ConfirmationValidator < EachValidator
def validate_each(record, attribute, value)
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
- record.errors.add(attribute, :confirmation, options)
+ human_attribute_name = record.class.human_attribute_name(attribute)
+ record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(:attribute => human_attribute_name))
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 72b8562b93..991c5f7b82 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -126,12 +126,12 @@ module ActiveModel
# end
#
# Standard configuration options (:on, :if and :unless), which are
- # available on the class version of validates_with, should instead be
- # placed on the <tt>validates</tt> method as these are applied and tested
+ # available on the class version of +validates_with+, should instead be
+ # placed on the +validates+ method as these are applied and tested
# in the callback
#
# If you pass any additional configuration options, they will be passed
- # to the class and available as <tt>options</tt>, please refer to the
+ # to the class and available as +options+, please refer to the
# class version of this method for more information
#
def validates_with(*args, &block)