aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib')
-rw-r--r--activemodel/lib/active_model.rb19
-rw-r--r--activemodel/lib/active_model/deprecated_mass_assignment_security.rb19
-rw-r--r--activemodel/lib/active_model/forbidden_attributes_protection.rb27
-rw-r--r--activemodel/lib/active_model/mass_assignment_security.rb350
-rw-r--r--activemodel/lib/active_model/mass_assignment_security/permission_set.rb40
-rw-r--r--activemodel/lib/active_model/mass_assignment_security/sanitizer.rb74
-rw-r--r--activemodel/lib/active_model/railtie.rb8
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb8
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb15
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb3
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb3
-rw-r--r--activemodel/lib/active_model/validations/validates.rb6
12 files changed, 92 insertions, 480 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index ec2d734647..f757ba9843 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -34,10 +34,10 @@ module ActiveModel
autoload :Conversion
autoload :Dirty
autoload :EachValidator, 'active_model/validator'
- autoload :Errors
+ autoload :ForbiddenAttributesProtection
autoload :Lint
- autoload :MassAssignmentSecurity
autoload :Model
+ autoload :DeprecatedMassAssignmentSecurity
autoload :Name, 'active_model/naming'
autoload :Naming
autoload :Observer, 'active_model/observing'
@@ -49,11 +49,22 @@ module ActiveModel
autoload :Validations
autoload :Validator
+ eager_autoload do
+ autoload :Errors
+ end
+
module Serializers
extend ActiveSupport::Autoload
- autoload :JSON
- autoload :Xml
+ eager_autoload do
+ autoload :JSON
+ autoload :Xml
+ end
+ end
+
+ def eager_load!
+ super
+ ActiveModel::Serializer.eager_load!
end
end
diff --git a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
new file mode 100644
index 0000000000..2ea69991fc
--- /dev/null
+++ b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
@@ -0,0 +1,19 @@
+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 " \
+ "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 " \
+ "or add `protected_attributes` to your Gemfile to use old one."
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb
new file mode 100644
index 0000000000..4c05b19cba
--- /dev/null
+++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb
@@ -0,0 +1,27 @@
+module ActiveModel
+ # Raised when forbidden attributes are used for mass assignment.
+ #
+ # class Person < ActiveRecord::Base
+ # end
+ #
+ # params = ActionController::Parameters.new(name: 'Bob')
+ # Person.new(params)
+ # # => ActiveModel::ForbiddenAttributesError
+ #
+ # params.permit!
+ # Person.new(params)
+ # # => #<Person id: nil, name: "Bob">
+ class ForbiddenAttributesError < StandardError
+ end
+
+ module ForbiddenAttributesProtection # :nodoc:
+ protected
+ def sanitize_for_mass_assignment(attributes, options = {})
+ if attributes.respond_to?(:permitted?) && !attributes.permitted?
+ raise ActiveModel::ForbiddenAttributesError
+ else
+ attributes
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb
deleted file mode 100644
index f9841abcb0..0000000000
--- a/activemodel/lib/active_model/mass_assignment_security.rb
+++ /dev/null
@@ -1,350 +0,0 @@
-require 'active_support/core_ext/string/inflections'
-require 'active_model/mass_assignment_security/permission_set'
-require 'active_model/mass_assignment_security/sanitizer'
-
-module ActiveModel
- # == Active Model Mass-Assignment Security
- #
- # Mass assignment security provides an interface for protecting attributes
- # from end-user assignment. For more complex permissions, mass assignment
- # security may be handled outside the model by extending a non-ActiveRecord
- # class, such as a controller, with this behavior.
- #
- # For example, a logged in user may need to assign additional attributes
- # depending on their role:
- #
- # class AccountsController < ApplicationController
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessible :first_name, :last_name
- # attr_accessible :first_name, :last_name, :plan_id, as: :admin
- #
- # def update
- # ...
- # @account.update_attributes(account_params)
- # ...
- # end
- #
- # protected
- #
- # def account_params
- # role = admin ? :admin : :default
- # sanitize_for_mass_assignment(params[:account], role)
- # end
- #
- # end
- #
- # === Configuration options
- #
- # * <tt>mass_assignment_sanitizer</tt> - Defines sanitize method. Possible
- # values are:
- # * <tt>:logger</tt> (default) - writes filtered attributes to logger
- # * <tt>:strict</tt> - raise <tt>ActiveModel::MassAssignmentSecurity::Error</tt>
- # on any protected attribute update.
- #
- # You can specify your own sanitizer object eg. <tt>MySanitizer.new</tt>.
- # See <tt>ActiveModel::MassAssignmentSecurity::LoggerSanitizer</tt> for
- # example implementation.
- module MassAssignmentSecurity
- extend ActiveSupport::Concern
-
- included do
- class_attribute :_accessible_attributes, instance_writer: false
- class_attribute :_protected_attributes, instance_writer: false
- class_attribute :_active_authorizer, instance_writer: false
-
- class_attribute :_mass_assignment_sanitizer, instance_writer: false
- self.mass_assignment_sanitizer = :logger
- end
-
- module ClassMethods
- # Attributes named in this macro are protected from mass-assignment
- # whenever attributes are sanitized before assignment. A role for the
- # attributes is optional, if no role is provided then <tt>:default</tt>
- # is used. A role can be defined by using the <tt>:as</tt> option with a
- # symbol or an array of symbols as the value.
- #
- # Mass-assignment to these attributes will simply be ignored, to assign
- # to them you can use direct writer methods. This is meant to protect
- # sensitive attributes from being overwritten by malicious users
- # tampering with URLs or forms.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessor :name, :email, :logins_count
- #
- # attr_protected :logins_count
- # # Suppose that admin can not change email for customer
- # attr_protected :logins_count, :email, as: :admin
- #
- # def assign_attributes(values, options = {})
- # sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
- # send("#{k}=", v)
- # end
- # end
- # end
- #
- # When using the <tt>:default</tt> role:
- #
- # customer = Customer.new
- # customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5 }, as: :default)
- # customer.name # => "David"
- # customer.email # => "a@b.com"
- # customer.logins_count # => nil
- #
- # And using the <tt>:admin</tt> role:
- #
- # customer = Customer.new
- # customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5}, as: :admin)
- # customer.name # => "David"
- # customer.email # => nil
- # customer.logins_count # => nil
- #
- # customer.email = 'c@d.com'
- # customer.email # => "c@d.com"
- #
- # To start from an all-closed default and enable attributes as needed,
- # have a look at +attr_accessible+.
- #
- # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of
- # +attr_protected+ to sanitize attributes provides basically the same
- # functionality, but it makes a bit tricky to deal with nested attributes.
- def attr_protected(*args)
- options = args.extract_options!
- role = options[:as] || :default
-
- self._protected_attributes = protected_attributes_configs.dup
-
- Array(role).each do |name|
- self._protected_attributes[name] = self.protected_attributes(name) + args
- end
-
- self._active_authorizer = self._protected_attributes
- end
-
- # Specifies a white list of model attributes that can be set via
- # mass-assignment.
- #
- # Like +attr_protected+, a role for the attributes is optional,
- # if no role is provided then <tt>:default</tt> is used. A role can be
- # defined by using the <tt>:as</tt> option with a symbol or an array of
- # symbols as the value.
- #
- # This is the opposite of the +attr_protected+ macro: Mass-assignment
- # will only set attributes in this list, to assign to the rest of
- # attributes you can use direct writer methods. This is meant to protect
- # sensitive attributes from being overwritten by malicious users
- # tampering with URLs or forms. If you'd rather start from an all-open
- # default and restrict attributes as needed, have a look at
- # +attr_protected+.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessor :name, :credit_rating
- #
- # # Both admin and default user can change name of a customer
- # attr_accessible :name, as: [:admin, :default]
- # # Only admin can change credit rating of a customer
- # attr_accessible :credit_rating, as: :admin
- #
- # def assign_attributes(values, options = {})
- # sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
- # send("#{k}=", v)
- # end
- # end
- # end
- #
- # When using the <tt>:default</tt> role:
- #
- # customer = Customer.new
- # customer.assign_attributes({ name: 'David', credit_rating: 'Excellent', last_login: 1.day.ago }, as: :default)
- # customer.name # => "David"
- # customer.credit_rating # => nil
- #
- # customer.credit_rating = 'Average'
- # customer.credit_rating # => "Average"
- #
- # And using the <tt>:admin</tt> role:
- #
- # customer = Customer.new
- # customer.assign_attributes({ name: 'David', credit_rating: 'Excellent', last_login: 1.day.ago }, as: :admin)
- # customer.name # => "David"
- # customer.credit_rating # => "Excellent"
- #
- # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of
- # +attr_accessible+ to sanitize attributes provides basically the same
- # functionality, but it makes a bit tricky to deal with nested attributes.
- def attr_accessible(*args)
- options = args.extract_options!
- role = options[:as] || :default
-
- self._accessible_attributes = accessible_attributes_configs.dup
-
- Array(role).each do |name|
- self._accessible_attributes[name] = self.accessible_attributes(name) + args
- end
-
- self._active_authorizer = self._accessible_attributes
- end
-
- # Returns an instance of <tt>ActiveModel::MassAssignmentSecurity::BlackList</tt>
- # with the attributes protected by #attr_protected method. If no +role+
- # is provided, then <tt>:default</tt> is used.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessor :name, :email, :logins_count
- #
- # attr_protected :logins_count
- # attr_protected :logins_count, :email, as: :admin
- # end
- #
- # Customer.protected_attributes
- # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count"}>
- #
- # Customer.protected_attributes(:default)
- # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count"}>
- #
- # Customer.protected_attributes(:admin)
- # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count", "email"}>
- def protected_attributes(role = :default)
- protected_attributes_configs[role]
- end
-
- # Returns an instance of <tt>ActiveModel::MassAssignmentSecurity::WhiteList</tt>
- # with the attributes protected by #attr_accessible method. If no +role+
- # is provided, then <tt>:default</tt> is used.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessor :name, :credit_rating
- #
- # attr_accessible :name, as: [:admin, :default]
- # attr_accessible :credit_rating, as: :admin
- # end
- #
- # Customer.accessible_attributes
- # # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
- #
- # Customer.accessible_attributes(:default)
- # # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
- #
- # Customer.accessible_attributes(:admin)
- # # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name", "credit_rating"}>
- def accessible_attributes(role = :default)
- accessible_attributes_configs[role]
- end
-
- # Returns a hash with the protected attributes (by #attr_accessible or
- # #attr_protected) per role.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessor :name, :credit_rating
- #
- # attr_accessible :name, as: [:admin, :default]
- # attr_accessible :credit_rating, as: :admin
- # end
- #
- # Customer.active_authorizers
- # # => {
- # # :admin=> #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name", "credit_rating"}>,
- # # :default=>#<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
- # #  }
- def active_authorizers
- self._active_authorizer ||= protected_attributes_configs
- end
- alias active_authorizer active_authorizers
-
- # Returns an empty array by default. You can still override this to define
- # the default attributes protected by #attr_protected method.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # def self.attributes_protected_by_default
- # [:name]
- # end
- # end
- #
- # Customer.protected_attributes
- # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {:name}>
- def attributes_protected_by_default
- []
- end
-
- # Defines sanitize method.
- #
- # class Customer
- # include ActiveModel::MassAssignmentSecurity
- #
- # attr_accessor :name
- #
- # attr_protected :name
- #
- # def assign_attributes(values)
- # sanitize_for_mass_assignment(values).each do |k, v|
- # send("#{k}=", v)
- # end
- # end
- # end
- #
- # # See ActiveModel::MassAssignmentSecurity::StrictSanitizer for more information.
- # Customer.mass_assignment_sanitizer = :strict
- #
- # customer = Customer.new
- # customer.assign_attributes(name: 'David')
- # # => ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes for Customer: name
- #
- # Also, you can specify your own sanitizer object.
- #
- # class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer
- # def process_removed_attributes(klass, attrs)
- # raise StandardError
- # end
- # end
- #
- # Customer.mass_assignment_sanitizer = CustomSanitizer.new
- #
- # customer = Customer.new
- # customer.assign_attributes(name: 'David')
- # # => StandardError: StandardError
- def mass_assignment_sanitizer=(value)
- self._mass_assignment_sanitizer = if value.is_a?(Symbol)
- const_get(:"#{value.to_s.camelize}Sanitizer").new(self)
- else
- value
- end
- end
-
- private
-
- def protected_attributes_configs
- self._protected_attributes ||= begin
- Hash.new { |h,k| h[k] = BlackList.new(attributes_protected_by_default) }
- end
- end
-
- def accessible_attributes_configs
- self._accessible_attributes ||= begin
- Hash.new { |h,k| h[k] = WhiteList.new }
- end
- end
- end
-
- protected
-
- def sanitize_for_mass_assignment(attributes, role = nil) #:nodoc:
- _mass_assignment_sanitizer.sanitize(self.class, attributes, mass_assignment_authorizer(role))
- end
-
- def mass_assignment_authorizer(role) #:nodoc:
- self.class.active_authorizer[role || :default]
- end
- end
-end
diff --git a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
deleted file mode 100644
index f104d0306c..0000000000
--- a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'set'
-
-module ActiveModel
- module MassAssignmentSecurity
- class PermissionSet < Set #:nodoc:
-
- def +(values)
- super(values.compact.map(&:to_s))
- end
-
- def include?(key)
- super(remove_multiparameter_id(key))
- end
-
- def deny?(key)
- raise NotImplementedError, "#deny?(key) supposed to be overwritten"
- end
-
- protected
-
- def remove_multiparameter_id(key)
- key.to_s.gsub(/\(.+/, '')
- end
- end
-
- class WhiteList < PermissionSet #:nodoc:
-
- def deny?(key)
- !include?(key)
- end
- end
-
- class BlackList < PermissionSet #:nodoc:
-
- def deny?(key)
- include?(key)
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb b/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
deleted file mode 100644
index dafb7cdff3..0000000000
--- a/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module ActiveModel
- module MassAssignmentSecurity
- class Sanitizer #:nodoc:
- # Returns all attributes not denied by the authorizer.
- def sanitize(klass, attributes, authorizer)
- rejected = []
- sanitized_attributes = attributes.reject do |key, value|
- rejected << key if authorizer.deny?(key)
- end
- process_removed_attributes(klass, rejected) unless rejected.empty?
- sanitized_attributes
- end
-
- protected
-
- def process_removed_attributes(klass, attrs)
- raise NotImplementedError, "#process_removed_attributes(attrs) suppose to be overwritten"
- end
- end
-
- class LoggerSanitizer < Sanitizer #:nodoc:
- def initialize(target)
- @target = target
- super()
- end
-
- def logger
- @target.logger
- end
-
- def logger?
- @target.respond_to?(:logger) && @target.logger
- end
-
- def backtrace
- if defined? Rails
- Rails.backtrace_cleaner.clean(caller)
- else
- caller
- end
- end
-
- def process_removed_attributes(klass, attrs)
- if logger?
- logger.warn do
- "WARNING: Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}\n" +
- backtrace.map { |trace| "\t#{trace}" }.join("\n")
- end
- end
- end
- end
-
- class StrictSanitizer < Sanitizer #:nodoc:
- def initialize(target = nil)
- super()
- end
-
- def process_removed_attributes(klass, attrs)
- return if (attrs - insensitive_attributes).empty?
- raise ActiveModel::MassAssignmentSecurity::Error.new(klass, attrs)
- end
-
- def insensitive_attributes
- ['id']
- end
- end
-
- class Error < StandardError #:nodoc:
- def initialize(klass, attrs)
- super("Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}")
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/railtie.rb b/activemodel/lib/active_model/railtie.rb
index 63ffe5db63..f239758b35 100644
--- a/activemodel/lib/active_model/railtie.rb
+++ b/activemodel/lib/active_model/railtie.rb
@@ -1,2 +1,8 @@
require "active_model"
-require "rails" \ No newline at end of file
+require "rails"
+
+module ActiveModel
+ class Railtie < Rails::Railtie
+ config.eager_load_namespaces << ActiveModel
+ end
+end \ No newline at end of file
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index bf3fe7ff04..c153ef4309 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -56,7 +56,8 @@ module ActiveModel
options = args.last
if options.is_a?(Hash) && options[:on]
options[:if] = Array(options[:if])
- options[:if].unshift("self.validation_context == :#{options[:on]}")
+ options[:on] = Array(options[:on])
+ options[:if].unshift("#{options[:on]}.include? self.validation_context")
end
set_callback(:validation, :before, *args, &block)
end
@@ -92,7 +93,10 @@ module ActiveModel
options = args.extract_options!
options[:prepend] = true
options[:if] = Array(options[:if])
- options[:if].unshift("self.validation_context == :#{options[:on]}") if options[:on]
+ if options[:on]
+ options[:on] = Array(options[:on])
+ options[:if].unshift("#{options[:on]}.include? self.validation_context")
+ end
set_callback(:validation, :after, *(args << options), &block)
end
end
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index 643a6f2b7c..3d7067fbcb 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -1,13 +1,13 @@
-require 'active_support/core_ext/range.rb'
+require 'active_support/core_ext/range'
module ActiveModel
module Validations
module Clusivity #:nodoc:
- ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
+ ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " <<
"and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
- unless [:include?, :call].any?{ |method| delimiter.respond_to?(method) }
+ unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
raise ArgumentError, ERROR_MESSAGE
end
end
@@ -15,7 +15,14 @@ module ActiveModel
private
def include?(record, value)
- exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
+ exclusions = 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)
end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index dc3368c569..3ec552c372 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -24,11 +24,12 @@ module ActiveModel
# validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
# validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
# message: 'should not be the same as your username or first name'
+ # validates_exclusion_of :karma, in: :reserved_karmas
# end
#
# Configuration options:
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
- # be part of. This can be supplied as a proc or lambda which returns an
+ # be part of. This can be supplied as a proc, lambda or symbol which returns an
# enumerable. If the enumerable is a range the test is performed with
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index c2835c550b..babc8982da 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -23,11 +23,12 @@ module ActiveModel
# validates_inclusion_of :age, in: 0..99
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
+ # validates_inclusion_of :karma, in: :available_karmas
# end
#
# Configuration options:
# * <tt>:in</tt> - An enumerable object of available items. This can be
- # supplied as a proc or lambda which returns an enumerable. If the
+ # supplied as a proc, lambda or symbol which returns an enumerable. If the
# enumerable is a range the test is performed with <tt>Range#cover?</tt>,
# otherwise with <tt>include?</tt>.
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index eb6e604851..03046a543a 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -94,10 +94,10 @@ module ActiveModel
# validates :token, uniqueness: true, strict: TokenGenerationException
#
#
- # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+
- # and +:strict+ can be given to one specific validator, as a hash:
+ # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+
+ # and +:message+ can be given to one specific validator, as a hash:
#
- # validates :password, presence: { if: :password_required? }, confirmation: true
+ # validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
def validates(*attributes)
defaults = attributes.extract_options!.dup
validations = defaults.slice!(*_validates_default_keys)