Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 9173

Show
Ignore:
Timestamp:
04/01/08 00:05:48 (2 months ago)
Author:
david
Message:

Splitting them up first

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activemodel/lib/active_model/validations.rb

    r9172 r9173  
    3131    module ClassMethods 
    3232      DEFAULT_VALIDATION_OPTIONS = { 
    33         :on => :save, 
    34         :allow_nil => false, 
     33        :on          => :save, 
     34        :allow_nil   => false, 
    3535        :allow_blank => false, 
    36         :message => nil 
     36        :message     => nil 
    3737      }.freeze 
    38  
    39       ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze 
    40       ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=', 
    41                                   :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=', 
    42                                   :odd => 'odd?', :even => 'even?' }.freeze 
    43  
    44       # Adds a validation method or block to the class. This is useful when 
    45       # overriding the #validate instance method becomes too unwieldly and 
    46       # you're looking for more descriptive declaration of your validations. 
    47       # 
    48       # This can be done with a symbol pointing to a method: 
    49       # 
    50       #   class Comment < ActiveRecord::Base 
    51       #     validate :must_be_friends 
    52       # 
    53       #     def must_be_friends 
    54       #       errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee) 
    55       #     end 
    56       #   end 
    57       # 
    58       # Or with a block which is passed the current record to be validated: 
    59       # 
    60       #   class Comment < ActiveRecord::Base 
    61       #     validate do |comment| 
    62       #       comment.must_be_friends 
    63       #     end 
    64       # 
    65       #     def must_be_friends 
    66       #       errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee) 
    67       #     end 
    68       #   end 
    69       # 
    70       # This usage applies to #validate_on_create and #validate_on_update as well. 
    71  
    72       # Validates each attribute against a block. 
    73       # 
    74       #   class Person < ActiveRecord::Base 
    75       #     validates_each :first_name, :last_name do |record, attr, value| 
    76       #       record.errors.add attr, 'starts with z.' if value[0] == ?z 
    77       #     end 
    78       #   end 
    79       # 
    80       # Options: 
    81       # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) 
    82       # * <tt>allow_nil</tt> - Skip validation if attribute is nil. 
    83       # * <tt>allow_blank</tt> - Skip validation if attribute is blank. 
    84       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    85       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    86       #   method, proc or string should return or evaluate to a true or false value. 
    87       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    88       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    89       #   method, proc or string should return or evaluate to a true or false value. 
    90       def validates_each(*attrs) 
    91         options = attrs.extract_options!.symbolize_keys 
    92         attrs   = attrs.flatten 
    93  
    94         # Declare the validation. 
    95         send(validation_method(options[:on] || :save), options) do |record| 
    96           attrs.each do |attr| 
    97             value = record.send(attr) 
    98             next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) 
    99             yield record, attr, value 
    100           end 
    101         end 
    102       end 
    103  
    104       # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example: 
    105       # 
    106       #   Model: 
    107       #     class Person < ActiveRecord::Base 
    108       #       validates_confirmation_of :user_name, :password 
    109       #       validates_confirmation_of :email_address, :message => "should match confirmation" 
    110       #     end 
    111       # 
    112       #   View: 
    113       #     <%= password_field "person", "password" %> 
    114       #     <%= password_field "person", "password_confirmation" %> 
    115       # 
    116       # The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password. 
    117       # To achieve this, the validation adds accessors to the model for the confirmation attribute. NOTE: This check is performed 
    118       # only if +password_confirmation+ is not nil, and by default only on save. To require confirmation, make sure to add a presence 
    119       # check for the confirmation attribute: 
    120       # 
    121       #   validates_presence_of :password_confirmation, :if => :password_changed? 
    122       # 
    123       # Configuration options: 
    124       # * <tt>message</tt> - A custom error message (default is: "doesn't match confirmation") 
    125       # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) 
    126       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    127       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    128       #   method, proc or string should return or evaluate to a true or false value. 
    129       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    130       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    131       #   method, proc or string should return or evaluate to a true or false value.       
    132       def validates_confirmation_of(*attr_names) 
    133         configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save } 
    134         configuration.update(attr_names.extract_options!) 
    135  
    136         attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" })) 
    137  
    138         validates_each(attr_names, configuration) do |record, attr_name, value| 
    139           record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation") 
    140         end 
    141       end 
    142  
    143       # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example: 
    144       # 
    145       #   class Person < ActiveRecord::Base 
    146       #     validates_acceptance_of :terms_of_service 
    147       #     validates_acceptance_of :eula, :message => "must be abided" 
    148       #   end 
    149       # 
    150       # If the database column does not exist, the terms_of_service attribute is entirely virtual. This check is 
    151       # performed only if terms_of_service is not nil and by default on save. 
    152       # 
    153       # Configuration options: 
    154       # * <tt>message</tt> - A custom error message (default is: "must be accepted") 
    155       # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) 
    156       # * <tt>allow_nil</tt> - Skip validation if attribute is nil. (default is true) 
    157       # * <tt>accept</tt> - Specifies value that is considered accepted.  The default value is a string "1", which 
    158       #   makes it easy to relate to an HTML checkbox. This should be set to 'true' if you are validating a database 
    159       #   column, since the attribute is typecast from "1" to <tt>true</tt> before validation. 
    160       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    161       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    162       #   method, proc or string should return or evaluate to a true or false value. 
    163       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    164       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    165       #   method, proc or string should return or evaluate to a true or false value.       
    166       def validates_acceptance_of(*attr_names) 
    167         configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" } 
    168         configuration.update(attr_names.extract_options!) 
    169  
    170         db_cols = begin 
    171           column_names 
    172         rescue ActiveRecord::StatementInvalid 
    173           [] 
    174         end 
    175         names = attr_names.reject { |name| db_cols.include?(name.to_s) } 
    176         attr_accessor(*names) 
    177  
    178         validates_each(attr_names,configuration) do |record, attr_name, value| 
    179           record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept] 
    180         end 
    181       end 
    182  
    183       # Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example: 
    184       # 
    185       #   class Person < ActiveRecord::Base 
    186       #     validates_presence_of :first_name 
    187       #   end 
    188       # 
    189       # The first_name attribute must be in the object and it cannot be blank. 
    190       # 
    191       # If you want to validate the presence of a boolean field (where the real values are true and false), 
    192       # you will want to use validates_inclusion_of :field_name, :in => [true, false] 
    193       # This is due to the way Object#blank? handles boolean values. false.blank? # => true 
    194       # 
    195       # Configuration options: 
    196       # * <tt>message</tt> - A custom error message (default is: "can't be blank") 
    197       # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) 
    198       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    199       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    200       #   method, proc or string should return or evaluate to a true or false value. 
    201       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    202       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    203       #   method, proc or string should return or evaluate to a true or false value. 
    204       # 
    205       def validates_presence_of(*attr_names) 
    206         configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save } 
    207         configuration.update(attr_names.extract_options!) 
    208  
    209         # can't use validates_each here, because it cannot cope with nonexistent attributes, 
    210         # while errors.add_on_empty can 
    211         send(validation_method(configuration[:on]), configuration) do |record| 
    212           record.errors.add_on_blank(attr_names, configuration[:message]) 
    213         end 
    214       end 
    215  
    216       # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time: 
    217       # 
    218       #   class Person < ActiveRecord::Base 
    219       #     validates_length_of :first_name, :maximum=>30 
    220       #     validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind" 
    221       #     validates_length_of :fax, :in => 7..32, :allow_nil => true 
    222       #     validates_length_of :phone, :in => 7..32, :allow_blank => true 
    223       #     validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name" 
    224       #     validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character" 
    225       #     validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me." 
    226       #   end 
    227       # 
    228       # Configuration options: 
    229       # * <tt>minimum</tt> - The minimum size of the attribute 
    230       # * <tt>maximum</tt> - The maximum size of the attribute 
    231       # * <tt>is</tt> - The exact size of the attribute 
    232       # * <tt>within</tt> - A range specifying the minimum and maximum size of the attribute 
    233       # * <tt>in</tt> - A synonym(or alias) for :within 
    234       # * <tt>allow_nil</tt> - Attribute may be nil; skip validation. 
    235       # * <tt>allow_blank</tt> - Attribute may be blank; skip validation. 
    236       # 
    237       # * <tt>too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)") 
    238       # * <tt>too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)") 
    239       # * <tt>wrong_length</tt> - The error message if using the :is method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)") 
    240       # * <tt>message</tt> - The error message to use for a :minimum, :maximum, or :is violation.  An alias of the appropriate too_long/too_short/wrong_length message 
    241       # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) 
    242       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    243       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    244       #   method, proc or string should return or evaluate to a true or false value. 
    245       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    246       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    247       #   method, proc or string should return or evaluate to a true or false value.       
    248       def validates_length_of(*attrs) 
    249         # Merge given options with defaults. 
    250         options = { 
    251           :too_long     => ActiveRecord::Errors.default_error_messages[:too_long], 
    252           :too_short    => ActiveRecord::Errors.default_error_messages[:too_short], 
    253           :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length] 
    254         }.merge(DEFAULT_VALIDATION_OPTIONS) 
    255         options.update(attrs.extract_options!.symbolize_keys) 
    256  
    257         # Ensure that one and only one range option is specified. 
    258         range_options = ALL_RANGE_OPTIONS & options.keys 
    259         case range_options.size 
    260           when 0 
    261             raise ArgumentError, 'Range unspecified.  Specify the :within, :maximum, :minimum, or :is option.' 
    262           when 1 
    263             # Valid number of options; do nothing. 
    264           else 
    265             raise ArgumentError, 'Too many range options specified.  Choose only one.' 
    266         end 
    267  
    268         # Get range option and value. 
    269         option = range_options.first 
    270         option_value = options[range_options.first] 
    271  
    272         case option 
    273           when :within, :in 
    274             raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range) 
    275  
    276             too_short = options[:too_short] % option_value.begin 
    277             too_long  = options[:too_long]  % option_value.end 
    278  
    279             validates_each(attrs, options) do |record, attr, value| 
    280               value = value.split(//) if value.kind_of?(String) 
    281               if value.nil? or value.size < option_value.begin 
    282                 record.errors.add(attr, too_short) 
    283               elsif value.size > option_value.end 
    284                 record.errors.add(attr, too_long) 
    285               end 
    286             end 
    287           when :is, :minimum, :maximum 
    288             raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0 
    289  
    290             # Declare different validations per option. 
    291             validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" } 
    292             message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long } 
    293  
    294             message = (options[:message] || options[message_options[option]]) % option_value 
    295  
    296             validates_each(attrs, options) do |record, attr, value| 
    297               value = value.split(//) if value.kind_of?(String) 
    298               record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value] 
    299             end 
    300         end 
    301       end 
    302  
    303       alias_method :validates_size_of, :validates_length_of 
    304  
    305  
    306       # Validates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user 
    307       # can be named "davidhh". 
    308       # 
    309       #   class Person < ActiveRecord::Base 
    310       #     validates_uniqueness_of :user_name, :scope => :account_id 
    311       #   end 
    312       # 
    313       # It can also validate whether the value of the specified attributes are unique based on multiple scope parameters.  For example, 
    314       # making sure that a teacher can only be on the schedule once per semester for a particular class. 
    315       # 
    316       #   class TeacherSchedule < ActiveRecord::Base 
    317       #     validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id] 
    318       #   end 
    319       # 
    320       # When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified 
    321       # attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself. 
    322       # 
    323       # Because this check is performed outside the database there is still a chance that duplicate values 
    324       # will be inserted in two parallel transactions.  To guarantee against this you should create a  
    325       # unique index on the field. See +add_index+ for more information. 
    326       # 
    327       # Configuration options: 
    328       # * <tt>message</tt> - Specifies a custom error message (default is: "has already been taken") 
    329       # * <tt>scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint. 
    330       # * <tt>case_sensitive</tt> - Looks for an exact match.  Ignored by non-text columns (false by default). 
    331       # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) 
    332       # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false) 
    333       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    334       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    335       #   method, proc or string should return or evaluate to a true or false value. 
    336       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    337       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    338       #   method, proc or string should return or evaluate to a true or false value. 
    339       def validates_uniqueness_of(*attr_names) 
    340         configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] } 
    341         configuration.update(attr_names.extract_options!) 
    342  
    343         validates_each(attr_names,configuration) do |record, attr_name, value| 
    344           # The check for an existing value should be run from a class that 
    345           # isn't abstract. This means working down from the current class 
    346           # (self), to the first non-abstract class. Since classes don't know 
    347           # their subclasses, we have to build the hierarchy between self and 
    348           # the record's class. 
    349           class_hierarchy = [record.class] 
    350           while class_hierarchy.first != self 
    351             class_hierarchy.insert(0, class_hierarchy.first.superclass) 
    352           end 
    353  
    354           # Now we can work our way down the tree to the first non-abstract 
    355           # class (which has a database table to query from). 
    356           finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? } 
    357  
    358           if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?) 
    359             condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}" 
    360             condition_params = [value] 
    361           else 
    362             # sqlite has case sensitive SELECT query, while MySQL/Postgresql don't. 
    363             # Hence, this is needed only for sqlite. 
    364             condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}" 
    365             condition_params = [value.downcase] 
    366           end 
    367  
    368           if scope = configuration[:scope] 
    369             Array(scope).map do |scope_item| 
    370               scope_value = record.send(scope_item) 
    371               condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}" 
    372               condition_params << scope_value 
    373             end 
    374           end 
    375  
    376           unless record.new_record? 
    377             condition_sql << " AND #{record.class.quoted_table_name}.#{record.class.primary_key} <> ?" 
    378             condition_params << record.send(:id) 
    379           end 
    380  
    381           results = finder_class.with_exclusive_scope do 
    382             connection.select_all( 
    383               construct_finder_sql( 
    384                 :select     => "#{attr_name}", 
    385                 :from       => "#{finder_class.quoted_table_name}", 
    386                 :conditions => [condition_sql, *condition_params] 
    387               ) 
    388             ) 
    389           end 
    390  
    391           unless results.length.zero? 
    392             found = true 
    393  
    394             # As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate 
    395             # column in ruby when case sensitive option 
    396             if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text? 
    397               found = results.any? { |a| a[attr_name.to_s] == value } 
    398             end 
    399  
    400             record.errors.add(attr_name, configuration[:message]) if found 
    401           end 
    402         end 
    403       end 
    404  
    405  
    406       # Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression 
    407       # provided. 
    408       # 
    409       #   class Person < ActiveRecord::Base 
    410       #     validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create 
    411       #   end 
    412       # 
    413       # Note: use \A and \Z to match the start and end of the string, ^ and $ match the start/end of a line. 
    414       # 
    415       # A regular expression must be provided or else an exception will be raised. 
    416       # 
    417       # Configuration options: 
    418       # * <tt>message</tt> - A custom error message (default is: "is invalid") 
    419       # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) 
    420       # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false) 
    421       # * <tt>with</tt> - The regular expression used to validate the format with (note: must be supplied!) 
    422       # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update) 
    423       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    424       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    425       #   method, proc or string should return or evaluate to a true or false value. 
    426       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    427       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    428       #   method, proc or string should return or evaluate to a true or false value. 
    429       def validates_format_of(*attr_names) 
    430         configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil } 
    431         configuration.update(attr_names.extract_options!) 
    432  
    433         raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp) 
    434  
    435         validates_each(attr_names, configuration) do |record, attr_name, value| 
    436           record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with] 
    437         end 
    438       end 
    439  
    440       # Validates whether the value of the specified attribute is available in a particular enumerable object. 
    441       # 
    442       #   class Person < ActiveRecord::Base 
    443       #     validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!" 
    444       #     validates_inclusion_of :age, :in => 0..99 
    445       #     validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list" 
    446       #   end 
    447       # 
    448       # Configuration options: 
    449       # * <tt>in</tt> - An enumerable object of available items 
    450       # * <tt>message</tt> - Specifies a customer error message (default is: "is not included in the list") 
    451       # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) 
    452       # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false) 
    453       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    454       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    455       #   method, proc or string should return or evaluate to a true or false value. 
    456       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    457       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    458       #   method, proc or string should return or evaluate to a true or false value. 
    459       def validates_inclusion_of(*attr_names) 
    460         configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save } 
    461         configuration.update(attr_names.extract_options!) 
    462  
    463         enum = configuration[:in] || configuration[:within] 
    464  
    465         raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?") 
    466  
    467         validates_each(attr_names, configuration) do |record, attr_name, value| 
    468           record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value) 
    469         end 
    470       end 
    471  
    472       # Validates that the value of the specified attribute is not in a particular enumerable object. 
    473       # 
    474       #   class Person < ActiveRecord::Base 
    475       #     validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here" 
    476       #     validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60" 
    477       #     validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed" 
    478       #   end 
    479       # 
    480       # Configuration options: 
    481       # * <tt>in</tt> - An enumerable object of items that the value shouldn't be part of 
    482       # * <tt>message</tt> - Specifies a customer error message (default is: "is reserved") 
    483       # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) 
    484       # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false) 
    485       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    486       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    487       #   method, proc or string should return or evaluate to a true or false value. 
    488       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    489       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    490       #   method, proc or string should return or evaluate to a true or false value. 
    491       def validates_exclusion_of(*attr_names) 
    492         configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save } 
    493         configuration.update(attr_names.extract_options!) 
    494  
    495         enum = configuration[:in] || configuration[:within] 
    496  
    497         raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?") 
    498  
    499         validates_each(attr_names, configuration) do |record, attr_name, value| 
    500           record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value) 
    501         end 
    502       end 
    503  
    504       # Validates whether the associated object or objects are all valid themselves. Works with any kind of association. 
    505       # 
    506       #   class Book < ActiveRecord::Base 
    507       #     has_many :pages 
    508       #     belongs_to :library 
    509       # 
    510       #     validates_associated :pages, :library 
    511       #   end 
    512       # 
    513       # Warning: If, after the above definition, you then wrote: 
    514       # 
    515       #   class Page < ActiveRecord::Base 
    516       #     belongs_to :book 
    517       # 
    518       #     validates_associated :book 
    519       #   end 
    520       # 
    521       # ...this would specify a circular dependency and cause infinite recursion. 
    522       # 
    523       # NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association 
    524       # is both present and guaranteed to be valid, you also need to use validates_presence_of. 
    525       # 
    526       # Configuration options: 
    527       # * <tt>message</tt> - A custom error message (default is: "is invalid") 
    528       # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update) 
    529       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    530       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    531       #   method, proc or string should return or evaluate to a true or false value. 
    532       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    533       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    534       #   method, proc or string should return or evaluate to a true or false value. 
    535       def validates_associated(*attr_names) 
    536         configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save } 
    537         configuration.update(attr_names.extract_options!) 
    538  
    539         validates_each(attr_names, configuration) do |record, attr_name, value| 
    540           record.errors.add(attr_name, configuration[:message]) unless 
    541             (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v } 
    542         end 
    543       end 
    544  
    545       # Validates whether the value of the specified attribute is numeric by trying to convert it to 
    546       # a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression 
    547       # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>integer</tt> is set to true). 
    548       # 
    549       #   class Person < ActiveRecord::Base 
    550       #     validates_numericality_of :value, :on => :create 
    551       #   end 
    552       # 
    553       # Configuration options: 
    554       # * <tt>message</tt> - A custom error message (default is: "is not a number") 
    555       # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update) 
    556       # * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false) 
    557       # * <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 
    558       # * <tt>greater_than</tt> Specifies the value must be greater than the supplied value 
    559       # * <tt>greater_than_or_equal_to</tt> Specifies the value must be greater than or equal the supplied value 
    560       # * <tt>equal_to</tt> Specifies the value must be equal to the supplied value 
    561       # * <tt>less_than</tt> Specifies the value must be less than the supplied value 
    562       # * <tt>less_than_or_equal_to</tt> Specifies the value must be less than or equal the supplied value 
    563       # * <tt>odd</tt> Specifies the value must be an odd number 
    564       # * <tt>even</tt> Specifies the value must be an even number 
    565       # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should 
    566       #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    567       #   method, proc or string should return or evaluate to a true or false value. 
    568       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 
    569       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The 
    570       #   method, proc or string should return or evaluate to a true or false value. 
    571       def validates_numericality_of(*attr_names) 
    572         configuration = { :on => :save, :only_integer => false, :allow_nil => false } 
    573         configuration.update(attr_names.extract_options!) 
    574  
    575  
    576         numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys 
    577  
    578         (numericality_options - [ :odd, :even ]).each do |option| 
    579           raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric) 
    580         end 
    581  
    582         validates_each(attr_names,configuration) do |record, attr_name, value| 
    583           raw_value = record.send("#{attr_name}_before_type_cast") || value 
    584  
    585           next if configuration[:allow_nil] and raw_value.nil? 
    586  
    587           if configuration[:only_integer] 
    588             unless raw_value.to_s =~ /\A[+-]?\d+\Z/ 
    589               record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number]) 
    590               next 
    591             end 
    592             raw_value = raw_value.to_i 
    593           else 
    594            begin 
    595               raw_value = Kernel.Float(raw_value.to_s) 
    596             rescue ArgumentError, TypeError 
    597               record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number]) 
    598               next 
    599             end 
    600           end 
    601  
    602           numericality_options.each do |option| 
    603             case option 
    604               when :odd, :even 
    605                 record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[] 
    606               else 
    607                 message = configuration[:message] || ActiveRecord::Errors.default_error_messages[option] 
    608                 message = message % configuration[option] if configuration[option] 
    609                 record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] 
    610             end 
    611           end 
    612         end 
    613       end 
    61438 
    61539      private 
     
    62347    end 
    62448 
     49    # Returns the Errors object that holds all information about attribute error messages. 
     50    def errors 
     51      @errors ||= Errors.new 
     52    end 
    62553 
    626     # Runs validate and validate_on_create or validate_on_update and returns true if no errors were added otherwise false. 
     54    # Runs all the specified validations and returns true if no errors were added otherwise false. 
    62755    def valid? 
    62856      errors.clear 
     
    65583      errors.empty? 
    65684    end 
    657  
    658     # Returns the Errors object that holds all information about attribute error messages. 
    659     def errors 
    660       @errors ||= Errors.new 
    661     end 
    66285  end 
    66386end 
     87 
     88Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path| 
     89  filename = File.basename(path) 
     90  require "active_model/validations/#{filename}" 
     91end