diff options
Diffstat (limited to 'activemodel/lib')
26 files changed, 336 insertions, 218 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 97a83e58af..99918fdb96 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -28,7 +28,7 @@ module ActiveModel # attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!' # attribute_method_suffix '_contrived?' # attribute_method_prefix 'clear_' - # define_attribute_methods ['name'] + # define_attribute_methods 'name' # # attr_accessor :name # @@ -86,7 +86,7 @@ module ActiveModel # include ActiveModel::AttributeMethods # attr_accessor :name # attribute_method_prefix 'clear_' - # define_attribute_methods [:name] + # define_attribute_methods :name # # private # @@ -124,7 +124,7 @@ module ActiveModel # include ActiveModel::AttributeMethods # attr_accessor :name # attribute_method_suffix '_short?' - # define_attribute_methods [:name] + # define_attribute_methods :name # # private # @@ -162,7 +162,7 @@ module ActiveModel # include ActiveModel::AttributeMethods # attr_accessor :name # attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!' - # define_attribute_methods [:name] + # define_attribute_methods :name # # private # @@ -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 @@ -204,7 +216,7 @@ module ActiveModel # # Call to define_attribute_methods must appear after the # # attribute_method_prefix, attribute_method_suffix or # # attribute_method_affix declares. - # define_attribute_methods [:name, :age, :address] + # define_attribute_methods :name, :age, :address # # private # @@ -212,8 +224,8 @@ module ActiveModel # ... # end # end - def define_attribute_methods(attr_names) - attr_names.each { |attr_name| define_attribute_method(attr_name) } + def define_attribute_methods(*attr_names) + attr_names.flatten.each { |attr_name| define_attribute_method(attr_name) } end def define_attribute_method(attr_name) @@ -243,11 +255,7 @@ module ActiveModel # Returns true if the attribute methods defined have been generated. def generated_attribute_methods #:nodoc: - @generated_attribute_methods ||= begin - mod = Module.new - include mod - mod - end + @generated_attribute_methods ||= Module.new.tap { |mod| include mod } end protected 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..3f2fd12db7 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -30,7 +30,7 @@ module ActiveModel # # include ActiveModel::Dirty # - # define_attribute_methods [:name] + # define_attribute_methods :name # # def name # @name @@ -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/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb index 5e5405fe27..893fbf92c3 100644 --- a/activemodel/lib/active_model/mass_assignment_security.rb +++ b/activemodel/lib/active_model/mass_assignment_security.rb @@ -229,7 +229,7 @@ module ActiveModel protected def sanitize_for_mass_assignment(attributes, role = nil) - _mass_assignment_sanitizer.sanitize(attributes, mass_assignment_authorizer(role)) + _mass_assignment_sanitizer.sanitize(self.class, attributes, mass_assignment_authorizer(role)) end def mass_assignment_authorizer(role) diff --git a/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb b/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb index 4491e07a72..44ce5a489d 100644 --- a/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb +++ b/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb @@ -2,18 +2,18 @@ module ActiveModel module MassAssignmentSecurity class Sanitizer # Returns all attributes not denied by the authorizer. - def sanitize(attributes, authorizer) + def sanitize(klass, attributes, authorizer) rejected = [] sanitized_attributes = attributes.reject do |key, value| rejected << key if authorizer.deny?(key) end - process_removed_attributes(rejected) unless rejected.empty? + process_removed_attributes(klass, rejected) unless rejected.empty? sanitized_attributes end protected - def process_removed_attributes(attrs) + def process_removed_attributes(klass, attrs) raise NotImplementedError, "#process_removed_attributes(attrs) suppose to be overwritten" end end @@ -32,8 +32,21 @@ module ActiveModel @target.respond_to?(:logger) && @target.logger end - def process_removed_attributes(attrs) - logger.warn "Can't mass-assign protected attributes: #{attrs.join(', ')}" if logger? + 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 @@ -42,9 +55,9 @@ module ActiveModel super() end - def process_removed_attributes(attrs) + def process_removed_attributes(klass, attrs) return if (attrs - insensitive_attributes).empty? - raise ActiveModel::MassAssignmentSecurity::Error.new(attrs) + raise ActiveModel::MassAssignmentSecurity::Error.new(klass, attrs) end def insensitive_attributes @@ -53,8 +66,8 @@ module ActiveModel end class Error < StandardError - def initialize(attrs) - super("Can't mass-assign protected attributes: #{attrs.join(', ')}") + def initialize(klass, attrs) + super("Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}") end end end 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 adf000e53c..2b5fc57a3a 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -1,7 +1,6 @@ require 'active_support/inflector' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/module/introspection' -require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' @@ -55,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..3eab745c89 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -6,8 +6,9 @@ module ActiveModel # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. # - # Validations for presence of password, confirmation of password (using + # Validations for presence of password on create, confirmation of password (using # a "password_confirmation" attribute) are automatically added. + # If you wish to turn off validations, pass 'validations: false' as an argument. # You can add more validations by hand if need be. # # You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use has_secure_password: @@ -31,16 +32,20 @@ module ActiveModel # user.authenticate("mUc3m00RsqyRe") # => user # User.find_by_name("david").try(:authenticate, "notright") # => false # User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user - def has_secure_password + def has_secure_password(options = {}) # Load bcrypt-ruby only when has_secure_password is used. # This is to avoid ActiveModel (and by extension the entire framework) being dependent on a binary library. gem 'bcrypt-ruby', '~> 3.0.0' require 'bcrypt' attr_reader :password - - validates_confirmation_of :password - validates_presence_of :password_digest + + if options.fetch(:validations, true) + validates_confirmation_of :password + validates_presence_of :password, :on => :create + end + + before_create { raise "Password digest missing on new record" if password_digest.blank? } include InstanceMethodsOnActivation @@ -55,17 +60,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 e9e80c4d2a..6d8fd21814 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..611e9ffd55 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] @@ -165,6 +178,12 @@ module ActiveModel end end + # Clean the +Errors+ object if instance is duped + def initialize_dup(other) # :nodoc: + @errors = nil + super + end + # Returns the +Errors+ object that holds all information about attribute error messages. def errors @errors ||= Errors.new(self) diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index e628c6f306..38abd0c1fa 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -23,7 +23,7 @@ module ActiveModel module HelperMethods # Encapsulates the pattern of wanting to validate the acceptance of a - # terms of service check box (or similar agreement). Example: + # terms of service check box (or similar agreement). # # class Person < ActiveRecord::Base # validates_acceptance_of :terms_of_service @@ -59,7 +59,7 @@ module ActiveModel # The method, proc or string should return or evaluate to a true or # false value. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_acceptance_of(*attr_names) validates_with AcceptanceValidator, _merge_attributes(attr_names) end 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..ede34d15bc 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 @@ -18,7 +19,7 @@ module ActiveModel module HelperMethods # Encapsulates the pattern of wanting to validate a password or email - # address field with a confirmation. For example: + # address field with a confirmation. # # Model: # class Person < ActiveRecord::Base @@ -59,7 +60,7 @@ module ActiveModel # <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_confirmation_of(*attr_names) validates_with ConfirmationValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index 5fedb1978b..4f09679541 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -15,34 +15,42 @@ module ActiveModel end module HelperMethods - # Validates that the value of the specified attribute is not in a particular enumerable object. + # Validates that the value of the specified attribute is not in a + # particular enumerable object. # # class Person < ActiveRecord::Base # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here" # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60" # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed" - # validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] }, :message => "should not be the same as your username or first name" + # validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] }, + # :message => "should not be the same as your username or first name" # 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 enumerable. If the enumerable - # is a range the test is performed with <tt>Range#cover?</tt> - # (backported in Active Support for 1.8), otherwise with <tt>include?</tt>. - # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved"). - # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+). - # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+). + # * <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 + # enumerable. If the enumerable is a range the test is performed with + # <tt>Range#cover?</tt> (backported in Active Support for 1.8), otherwise + # with <tt>include?</tt>. + # * <tt>:message</tt> - Specifies a custom error message (default is: "is + # reserved"). + # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute + # is +nil+ (default is +false+). + # * <tt>:allow_blank</tt> - If set to true, skips this validation if the + # attribute is blank(default is +false+). # * <tt>:on</tt> - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are <tt>:create</tt> # and <tt>:update</tt>. - # * <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 + # * <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_exclusion_of(*attr_names) validates_with ExclusionValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb index d3faa8c6a6..dd87e312f9 100644 --- a/activemodel/lib/active_model/validations/format.rb +++ b/activemodel/lib/active_model/validations/format.rb @@ -42,50 +42,62 @@ module ActiveModel end module HelperMethods - # Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided. - # You can require that the attribute matches the regular expression: + # Validates whether the value of the specified attribute is of the correct + # form, going by the regular expression provided.You can require that the + # attribute matches the regular expression: # # class Person < ActiveRecord::Base # validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create # end # - # Alternatively, you can require that the specified attribute does _not_ match the regular expression: + # Alternatively, you can require that the specified attribute does _not_ + # match the regular expression: # # class Person < ActiveRecord::Base # validates_format_of :email, :without => /NOSPAM/ # end # - # You can also provide a proc or lambda which will determine the regular expression that will be used to validate the attribute + # You can also provide a proc or lambda which will determine the regular + # expression that will be used to validate the attribute. # # class Person < ActiveRecord::Base # # Admin can have number as a first letter in their screen name - # validates_format_of :screen_name, :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\Z/i : /\A[a-z][a-z0-9_\-]*\Z/i } + # validates_format_of :screen_name, + # :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\Z/i : /\A[a-z][a-z0-9_\-]*\Z/i } # end # - # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string, <tt>^</tt> and <tt>$</tt> match the start/end of a line. + # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the + # string, <tt>^</tt> and <tt>$</tt> match the start/end of a line. # - # You must pass either <tt>:with</tt> or <tt>:without</tt> as an option. In addition, both must be a regular expression - # or a proc or lambda, or else an exception will be raised. + # You must pass either <tt>:with</tt> or <tt>:without</tt> as an option. + # In addition, both must be a regular expression or a proc or lambda, or + # else an exception will be raised. # # Configuration options: # * <tt>:message</tt> - A custom error message (default is: "is invalid"). - # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+). - # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+). - # * <tt>:with</tt> - Regular expression that if the attribute matches will result in a successful validation. - # This can be provided as a proc or lambda returning regular expression which will be called at runtime. - # * <tt>:without</tt> - Regular expression that if the attribute does not match will result in a successful validation. - # This can be provided as a proc or lambda returning regular expression which will be called at runtime. + # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute + # is +nil+ (default is +false+). + # * <tt>:allow_blank</tt> - If set to true, skips this validation if the + # attribute is blank (default is +false+). + # * <tt>:with</tt> - Regular expression that if the attribute matches will + # result in a successful validation. This can be provided as a proc or lambda + # returning regular expression which will be called at runtime. + # * <tt>:without</tt> - Regular expression that if the attribute does not match + # will result in a successful validation. This can be provided as a proc or + # lambda returning regular expression which will be called at runtime. # * <tt>:on</tt> - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are <tt>:create</tt> # and <tt>:update</tt>. - # * <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 + # * <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_format_of(*attr_names) validates_with FormatValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index 15ae7b1959..ffdbed0fc1 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -15,7 +15,8 @@ module ActiveModel end module HelperMethods - # Validates whether the value of the specified attribute is available in a particular enumerable object. + # Validates whether the value of the specified attribute is available in a + # particular enumerable object. # # class Person < ActiveRecord::Base # validates_inclusion_of :gender, :in => %w( m f ) @@ -29,20 +30,25 @@ module ActiveModel # supplied as a proc or lambda which returns an enumerable. If the enumerable # is a range the test is performed with <tt>Range#cover?</tt> # (backported in Active Support for 1.8), otherwise with <tt>include?</tt>. - # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list"). - # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+). - # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+). + # * <tt>:message</tt> - Specifies a custom error message (default is: "is not + # included in the list"). + # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute + # is +nil+ (default is +false+). + # * <tt>:allow_blank</tt> - If set to true, skips this validation if the + # attribute is blank (default is +false+). # * <tt>:on</tt> - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are <tt>:create</tt> # and <tt>:update</tt>. - # * <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 + # * <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_inclusion_of(*attr_names) validates_with InclusionValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 037f8c2db8..64b4fe2d74 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -72,35 +72,46 @@ module ActiveModel # validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name" # validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters" # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me." - # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.", :tokenizer => lambda { |str| str.scan(/\w+/) } + # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.", + # :tokenizer => lambda { |str| str.scan(/\w+/) } # end # # Configuration options: # * <tt>:minimum</tt> - The minimum size of the attribute. # * <tt>:maximum</tt> - The maximum size of the attribute. # * <tt>:is</tt> - The exact size of the attribute. - # * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute. + # * <tt>:within</tt> - A range specifying the minimum and maximum size of the + # attribute. # * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>. # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation. # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation. - # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %{count} characters)"). - # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %{count} characters)"). - # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %{count} characters)"). - # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message. + # * <tt>:too_long</tt> - The error message if the attribute goes over the + # maximum (default is: "is too long (maximum is %{count} characters)"). + # * <tt>:too_short</tt> - The error message if the attribute goes under the + # minimum (default is: "is too short (min is %{count} characters)"). + # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method + # and the attribute is the wrong size (default is: "is the wrong length + # (should be %{count} characters)"). + # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, + # <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate + # <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message. # * <tt>:on</tt> - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are <tt>:create</tt> # and <tt>:update</tt>. - # * <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. - # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to - # count words as in above example.) - # Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters. + # * <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. + # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. + # (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to count words + # as in above example). Defaults to <tt>lambda{ |value| value.split(//) }</tt> + # which counts individual characters. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_length_of(*attr_names) validates_with LengthValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index bb9f9679fc..40b5b92b84 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -79,9 +79,10 @@ module ActiveModel end module HelperMethods - # Validates whether the value of the specified attribute is numeric by trying to convert it to - # a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression - # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true). + # Validates whether the value of the specified attribute is numeric by trying + # to convert it to a float with Kernel.Float (if <tt>only_integer</tt> is false) + # or applying it to the regular expression <tt>/\A[\+\-]?\d+\Z/</tt> (if + # <tt>only_integer</tt> is set to true). # # class Person < ActiveRecord::Base # validates_numericality_of :value, :on => :create @@ -92,37 +93,50 @@ module ActiveModel # * <tt>:on</tt> - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are <tt>:create</tt> # and <tt>:update</tt>. - # * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+). - # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+. - # * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value. - # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value. + # * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, + # e.g. an integral value (default is +false+). + # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is + # +false+). Notice that for fixnum and float columns empty strings are + # converted to +nil+. + # * <tt>:greater_than</tt> - Specifies the value must be greater than the + # supplied value. + # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater + # than or equal the supplied value. # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value. - # * <tt>:less_than</tt> - Specifies the value must be less than the supplied value. - # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value. - # * <tt>:other_than</tt> - Specifies the value must be other than the supplied value. + # * <tt>:less_than</tt> - Specifies the value must be less than the supplied + # value. + # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or + # equal the supplied value. + # * <tt>:other_than</tt> - Specifies the value must be other than the supplied + # value. # * <tt>:odd</tt> - Specifies the value must be an odd number. # * <tt>:even</tt> - Specifies the value must be an even number. - # * <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 + # * <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information + # See <tt>ActiveModel::Validation#validates!</tt> for more information. + # + # The following checks can also be supplied with a proc or a symbol which + # corresponds to a method: # - # The following checks can also be supplied with a proc or a symbol which corresponds to a method: # * <tt>:greater_than</tt> # * <tt>:greater_than_or_equal_to</tt> # * <tt>:equal_to</tt> # * <tt>:less_than</tt> # * <tt>:less_than_or_equal_to</tt> # + # For example: + # # class Person < ActiveRecord::Base # validates_numericality_of :width, :less_than => Proc.new { |person| person.height } # validates_numericality_of :width, :greater_than => :minimum_weight # end - # def validates_numericality_of(*attr_names) validates_with NumericalityValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb index 9a643a6f5c..018ef1e733 100644 --- a/activemodel/lib/active_model/validations/presence.rb +++ b/activemodel/lib/active_model/validations/presence.rb @@ -11,7 +11,8 @@ module ActiveModel end module HelperMethods - # Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example: + # Validates that the specified attributes are not blank (as defined by + # Object#blank?). Happens by default on save. # # class Person < ActiveRecord::Base # validates_presence_of :first_name @@ -19,25 +20,28 @@ module ActiveModel # # The first_name attribute must be in the object and it cannot be blank. # - # If you want to validate the presence of a boolean field (where the real values are true and false), - # you will want to use <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>. + # If you want to validate the presence of a boolean field (where the real values + # are true and false), you will want to use + # <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>. # - # This is due to the way Object#blank? handles boolean values: <tt>false.blank? # => true</tt>. + # This is due to the way Object#blank? handles boolean values: + # <tt>false.blank? # => true</tt>. # # Configuration options: # * <tt>:message</tt> - A custom error message (default is: "can't be blank"). # * <tt>:on</tt> - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are <tt>:create</tt> # and <tt>:update</tt>. - # * <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. + # * <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information - # + # See <tt>ActiveModel::Validation#validates!</tt> for more information. def validates_presence_of(*attr_names) validates_with PresenceValidator, _merge_attributes(attr_names) end diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 72b8562b93..66cc9daa2c 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -62,8 +62,8 @@ module ActiveModel # <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. # * <tt>:strict</tt> - Specifies whether validation should be strict. - # See <tt>ActiveModel::Validation#validates!</tt> for more information - + # See <tt>ActiveModel::Validation#validates!</tt> for more information. + # # If you pass any additional configuration options, they will be passed # to the class and available as <tt>options</tt>: # @@ -77,7 +77,6 @@ module ActiveModel # options[:my_custom_key] # => "my custom value" # end # end - # def validates_with(*args, &block) options = args.extract_options! args.each do |klass| @@ -126,14 +125,13 @@ 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 - # in the callback + # 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 - # class version of this method for more information - # + # to the class and available as +options+, please refer to the + # class version of this method for more information. def validates_with(*args, &block) options = args.extract_options! args.each do |klass| |