activerecord 1.15.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  # Active Record validation is reported to and from this object, which is used by Base#save to
18
- # determine whether the object in a valid state to be saved. See usage example in Validations.
18
+ # determine whether the object is in a valid state to be saved. See usage example in Validations.
19
19
  class Errors
20
20
  include Enumerable
21
21
 
@@ -35,10 +35,17 @@ module ActiveRecord
35
35
  :too_short => "is too short (minimum is %d characters)",
36
36
  :wrong_length => "is the wrong length (should be %d characters)",
37
37
  :taken => "has already been taken",
38
- :not_a_number => "is not a number"
38
+ :not_a_number => "is not a number",
39
+ :greater_than => "must be greater than %d",
40
+ :greater_than_or_equal_to => "must be greater than or equal to %d",
41
+ :equal_to => "must be equal to %d",
42
+ :less_than => "must be less than %d",
43
+ :less_than_or_equal_to => "must be less than or equal to %d",
44
+ :odd => "must be odd",
45
+ :even => "must be even"
39
46
  }
40
47
 
41
- # Holds a hash with all the default error messages, such that they can be replaced by your own copy or localizations.
48
+ # Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
42
49
  cattr_accessor :default_error_messages
43
50
 
44
51
 
@@ -76,27 +83,33 @@ module ActiveRecord
76
83
  end
77
84
  end
78
85
 
79
- # Will add an error message to each of the attributes in +attributes+ that has a length outside of the passed boundary +range+.
80
- # If the length is above the boundary, the too_long_msg message will be used. If below, the too_short_msg.
81
- def add_on_boundary_breaking(attributes, range, too_long_msg = @@default_error_messages[:too_long], too_short_msg = @@default_error_messages[:too_short])
82
- for attr in [attributes].flatten
83
- value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s]
84
- add(attr, too_short_msg % range.begin) if value && value.length < range.begin
85
- add(attr, too_long_msg % range.end) if value && value.length > range.end
86
- end
87
- end
88
-
89
- alias :add_on_boundry_breaking :add_on_boundary_breaking
90
- deprecate :add_on_boundary_breaking => :validates_length_of, :add_on_boundry_breaking => :validates_length_of
91
-
92
86
  # Returns true if the specified +attribute+ has errors associated with it.
87
+ #
88
+ # class Company < ActiveRecord::Base
89
+ # validates_presence_of :name, :address, :email
90
+ # validates_length_of :name, :in => 5..30
91
+ # end
92
+ #
93
+ # company = Company.create(:address => '123 First St.')
94
+ # company.errors.invalid?(:name) # => true
95
+ # company.errors.invalid?(:address) # => false
93
96
  def invalid?(attribute)
94
97
  !@errors[attribute.to_s].nil?
95
98
  end
96
99
 
97
- # * Returns nil, if no errors are associated with the specified +attribute+.
98
- # * Returns the error message, if one error is associated with the specified +attribute+.
99
- # * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
100
+ # Returns nil, if no errors are associated with the specified +attribute+.
101
+ # Returns the error message, if one error is associated with the specified +attribute+.
102
+ # Returns an array of error messages, if more than one error is associated with the specified +attribute+.
103
+ #
104
+ # class Company < ActiveRecord::Base
105
+ # validates_presence_of :name, :address, :email
106
+ # validates_length_of :name, :in => 5..30
107
+ # end
108
+ #
109
+ # company = Company.create(:address => '123 First St.')
110
+ # company.errors.on(:name) # => ["is too short (minimum is 5 characters)", "can't be blank"]
111
+ # company.errors.on(:email) # => "can't be blank"
112
+ # company.errors.on(:address) # => nil
100
113
  def on(attribute)
101
114
  errors = @errors[attribute.to_s]
102
115
  return nil if errors.nil?
@@ -105,23 +118,54 @@ module ActiveRecord
105
118
 
106
119
  alias :[] :on
107
120
 
108
- # Returns errors assigned to base object through add_to_base according to the normal rules of on(attribute).
121
+ # Returns errors assigned to the base object through add_to_base according to the normal rules of on(attribute).
109
122
  def on_base
110
123
  on(:base)
111
124
  end
112
125
 
113
126
  # Yields each attribute and associated message per error added.
127
+ #
128
+ # class Company < ActiveRecord::Base
129
+ # validates_presence_of :name, :address, :email
130
+ # validates_length_of :name, :in => 5..30
131
+ # end
132
+ #
133
+ # company = Company.create(:address => '123 First St.')
134
+ # company.errors.each{|attr,msg| puts "#{attr} - #{msg}" } # =>
135
+ # name - is too short (minimum is 5 characters)
136
+ # name - can't be blank
137
+ # address - can't be blank
114
138
  def each
115
139
  @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
116
140
  end
117
141
 
118
142
  # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
119
143
  # through iteration as "First name can't be empty".
144
+ #
145
+ # class Company < ActiveRecord::Base
146
+ # validates_presence_of :name, :address, :email
147
+ # validates_length_of :name, :in => 5..30
148
+ # end
149
+ #
150
+ # company = Company.create(:address => '123 First St.')
151
+ # company.errors.each_full{|msg| puts msg } # =>
152
+ # Name is too short (minimum is 5 characters)
153
+ # Name can't be blank
154
+ # Address can't be blank
120
155
  def each_full
121
156
  full_messages.each { |msg| yield msg }
122
157
  end
123
158
 
124
159
  # Returns all the full error messages in an array.
160
+ #
161
+ # class Company < ActiveRecord::Base
162
+ # validates_presence_of :name, :address, :email
163
+ # validates_length_of :name, :in => 5..30
164
+ # end
165
+ #
166
+ # company = Company.create(:address => '123 First St.')
167
+ # company.errors.full_messages # =>
168
+ # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
125
169
  def full_messages
126
170
  full_messages = []
127
171
 
@@ -143,22 +187,35 @@ module ActiveRecord
143
187
  def empty?
144
188
  @errors.empty?
145
189
  end
146
-
147
- # Removes all the errors that have been added.
190
+
191
+ # Removes all errors that have been added.
148
192
  def clear
149
193
  @errors = {}
150
194
  end
151
195
 
152
- # Returns the total number of errors added. Two errors added to the same attribute will be counted as such
153
- # with this as well.
196
+ # Returns the total number of errors added. Two errors added to the same attribute will be counted as such.
154
197
  def size
155
198
  @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
156
199
  end
157
-
200
+
158
201
  alias_method :count, :size
159
202
  alias_method :length, :size
160
203
 
161
204
  # Return an XML representation of this error object.
205
+ #
206
+ # class Company < ActiveRecord::Base
207
+ # validates_presence_of :name, :address, :email
208
+ # validates_length_of :name, :in => 5..30
209
+ # end
210
+ #
211
+ # company = Company.create(:address => '123 First St.')
212
+ # company.errors.to_xml # =>
213
+ # <?xml version="1.0" encoding="UTF-8"?>
214
+ # <errors>
215
+ # <error>Name is too short (minimum is 5 characters)</error>
216
+ # <error>Name can't be blank</error>
217
+ # <error>Address can't be blank</error>
218
+ # </errors>
162
219
  def to_xml(options={})
163
220
  options[:root] ||= "errors"
164
221
  options[:indent] ||= 2
@@ -231,10 +288,15 @@ module ActiveRecord
231
288
  DEFAULT_VALIDATION_OPTIONS = {
232
289
  :on => :save,
233
290
  :allow_nil => false,
291
+ :allow_blank => false,
234
292
  :message => nil
235
293
  }.freeze
236
294
 
237
295
  ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
296
+ ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=',
297
+ :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
298
+ :odd => 'odd?', :even => 'even?' }.freeze
299
+
238
300
 
239
301
  def validate(*methods, &block)
240
302
  methods << block if block_given?
@@ -259,8 +321,8 @@ module ActiveRecord
259
321
  # whether or not to validate the record. See #validates_each.
260
322
  def evaluate_condition(condition, record)
261
323
  case condition
262
- when Symbol: record.send(condition)
263
- when String: eval(condition, binding)
324
+ when Symbol; record.send(condition)
325
+ when String; eval(condition, record.send(:binding))
264
326
  else
265
327
  if condition_block?(condition)
266
328
  condition.call(record)
@@ -285,20 +347,24 @@ module ActiveRecord
285
347
  # Options:
286
348
  # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
287
349
  # * <tt>allow_nil</tt> - Skip validation if attribute is nil.
350
+ # * <tt>allow_blank</tt> - Skip validation if attribute is blank.
288
351
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
289
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
290
- # method, proc or string should return or evaluate to a true or false value.
352
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
353
+ # method, proc or string should return or evaluate to a true or false value.
354
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
355
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
356
+ # method, proc or string should return or evaluate to a true or false value.
291
357
  def validates_each(*attrs)
292
- options = attrs.last.is_a?(Hash) ? attrs.pop.symbolize_keys : {}
358
+ options = attrs.extract_options!.symbolize_keys
293
359
  attrs = attrs.flatten
294
360
 
295
361
  # Declare the validation.
296
362
  send(validation_method(options[:on] || :save)) do |record|
297
- # Don't validate when there is an :if condition and that condition is false
298
- unless options[:if] && !evaluate_condition(options[:if], record)
363
+ # Don't validate when there is an :if condition and that condition is false or there is an :unless condition and that condition is true
364
+ unless (options[:if] && !evaluate_condition(options[:if], record)) || (options[:unless] && evaluate_condition(options[:unless], record))
299
365
  attrs.each do |attr|
300
366
  value = record.send(attr)
301
- next if value.nil? && options[:allow_nil]
367
+ next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
302
368
  yield record, attr, value
303
369
  end
304
370
  end
@@ -317,19 +383,25 @@ module ActiveRecord
317
383
  # <%= password_field "person", "password" %>
318
384
  # <%= password_field "person", "password_confirmation" %>
319
385
  #
320
- # The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
321
- # It exists only as an in-memory variable for validating the password. This check is performed only if password_confirmation
322
- # is not nil and by default on save.
386
+ # The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password.
387
+ # To achieve this, the validation adds acccessors to the model for the confirmation attribute. NOTE: This check is performed
388
+ # only if +password_confirmation+ is not nil, and by default only on save. To require confirmation, make sure to add a presence
389
+ # check for the confirmation attribute:
390
+ #
391
+ # validates_presence_of :password_confirmation, :if => :password_changed?
323
392
  #
324
393
  # Configuration options:
325
394
  # * <tt>message</tt> - A custom error message (default is: "doesn't match confirmation")
326
395
  # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
327
396
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
328
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
329
- # method, proc or string should return or evaluate to a true or false value.
397
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
398
+ # method, proc or string should return or evaluate to a true or false value.
399
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
400
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
401
+ # method, proc or string should return or evaluate to a true or false value.
330
402
  def validates_confirmation_of(*attr_names)
331
403
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
332
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
404
+ configuration.update(attr_names.extract_options!)
333
405
 
334
406
  attr_accessor *(attr_names.map { |n| "#{n}_confirmation" })
335
407
 
@@ -345,22 +417,26 @@ module ActiveRecord
345
417
  # validates_acceptance_of :eula, :message => "must be abided"
346
418
  # end
347
419
  #
348
- # The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed only if
349
- # terms_of_service is not nil and by default on save.
420
+ # If the database column does not exist, the terms_of_service attribute is entirely virtual. This check is
421
+ # performed only if terms_of_service is not nil and by default on save.
350
422
  #
351
423
  # Configuration options:
352
424
  # * <tt>message</tt> - A custom error message (default is: "must be accepted")
353
425
  # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
426
+ # * <tt>allow_nil</tt> - Skip validation if attribute is nil. (default is true)
354
427
  # * <tt>accept</tt> - Specifies value that is considered accepted. The default value is a string "1", which
355
- # makes it easy to relate to an HTML checkbox.
428
+ # makes it easy to relate to an HTML checkbox.
356
429
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
357
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
358
- # method, proc or string should return or evaluate to a true or false value.
430
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
431
+ # method, proc or string should return or evaluate to a true or false value.
432
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
433
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
434
+ # method, proc or string should return or evaluate to a true or false value.
359
435
  def validates_acceptance_of(*attr_names)
360
436
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
361
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
437
+ configuration.update(attr_names.extract_options!)
362
438
 
363
- attr_accessor *attr_names
439
+ attr_accessor *attr_names.reject { |name| column_names.include? name.to_s }
364
440
 
365
441
  validates_each(attr_names,configuration) do |record, attr_name, value|
366
442
  record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept]
@@ -374,7 +450,7 @@ module ActiveRecord
374
450
  # end
375
451
  #
376
452
  # The first_name attribute must be in the object and it cannot be blank.
377
- #
453
+ #
378
454
  # If you want to validate the presence of a boolean field (where the real values are true and false),
379
455
  # you will want to use validates_inclusion_of :field_name, :in => [true, false]
380
456
  # This is due to the way Object#blank? handles boolean values. false.blank? # => true
@@ -383,33 +459,34 @@ module ActiveRecord
383
459
  # * <tt>message</tt> - A custom error message (default is: "can't be blank")
384
460
  # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
385
461
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
386
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
387
- # method, proc or string should return or evaluate to a true or false value.
462
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
463
+ # method, proc or string should return or evaluate to a true or false value.
464
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
465
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
466
+ # method, proc or string should return or evaluate to a true or false value.
388
467
  #
389
468
  # === Warning
390
469
  # Validate the presence of the foreign key, not the instance variable itself.
391
470
  # Do this:
392
- # validate_presence_of :invoice_id
471
+ # validates_presence_of :invoice_id
393
472
  #
394
473
  # Not this:
395
- # validate_presence_of :invoice
474
+ # validates_presence_of :invoice
396
475
  #
397
476
  # If you validate the presence of the associated object, you will get
398
477
  # failures on saves when both the parent object and the child object are
399
478
  # new.
400
479
  def validates_presence_of(*attr_names)
401
480
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
402
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
481
+ configuration.update(attr_names.extract_options!)
403
482
 
404
483
  # can't use validates_each here, because it cannot cope with nonexistent attributes,
405
484
  # while errors.add_on_empty can
406
- attr_names.each do |attr_name|
407
- send(validation_method(configuration[:on])) do |record|
408
- unless configuration[:if] and not evaluate_condition(configuration[:if], record)
409
- record.errors.add_on_blank(attr_name,configuration[:message])
410
- end
411
- end
412
- end
485
+ send(validation_method(configuration[:on])) do |record|
486
+ unless (configuration[:if] && !evaluate_condition(configuration[:if], record)) || (configuration[:unless] && evaluate_condition(configuration[:unless], record))
487
+ record.errors.add_on_blank(attr_names, configuration[:message])
488
+ end
489
+ end
413
490
  end
414
491
 
415
492
  # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
@@ -418,6 +495,7 @@ module ActiveRecord
418
495
  # validates_length_of :first_name, :maximum=>30
419
496
  # validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
420
497
  # validates_length_of :fax, :in => 7..32, :allow_nil => true
498
+ # validates_length_of :phone, :in => 7..32, :allow_blank => true
421
499
  # validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
422
500
  # validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
423
501
  # validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
@@ -430,6 +508,7 @@ module ActiveRecord
430
508
  # * <tt>within</tt> - A range specifying the minimum and maximum size of the attribute
431
509
  # * <tt>in</tt> - A synonym(or alias) for :within
432
510
  # * <tt>allow_nil</tt> - Attribute may be nil; skip validation.
511
+ # * <tt>allow_blank</tt> - Attribute may be blank; skip validation.
433
512
  #
434
513
  # * <tt>too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)")
435
514
  # * <tt>too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)")
@@ -437,8 +516,11 @@ module ActiveRecord
437
516
  # * <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
438
517
  # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
439
518
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
440
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
441
- # method, proc or string should return or evaluate to a true or false value.
519
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
520
+ # method, proc or string should return or evaluate to a true or false value.
521
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
522
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
523
+ # method, proc or string should return or evaluate to a true or false value.
442
524
  def validates_length_of(*attrs)
443
525
  # Merge given options with defaults.
444
526
  options = {
@@ -446,7 +528,7 @@ module ActiveRecord
446
528
  :too_short => ActiveRecord::Errors.default_error_messages[:too_short],
447
529
  :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length]
448
530
  }.merge(DEFAULT_VALIDATION_OPTIONS)
449
- options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash)
531
+ options.update(attrs.extract_options!.symbolize_keys)
450
532
 
451
533
  # Ensure that one and only one range option is specified.
452
534
  range_options = ALL_RANGE_OPTIONS & options.keys
@@ -507,27 +589,34 @@ module ActiveRecord
507
589
  # end
508
590
  #
509
591
  # It can also validate whether the value of the specified attributes are unique based on multiple scope parameters. For example,
510
- # making sure that a teacher can only be on the schedule once per semester for a particular class.
592
+ # making sure that a teacher can only be on the schedule once per semester for a particular class.
511
593
  #
512
594
  # class TeacherSchedule < ActiveRecord::Base
513
- # validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
595
+ # validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
514
596
  # end
515
597
  #
516
598
  # 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
517
599
  # attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
518
600
  #
601
+ # Because this check is performed outside the database there is still a chance that duplicate values
602
+ # will be inserted in two parallel transactions. To guarantee against this you should create a
603
+ # unique index on the field. See +create_index+ for more information.
604
+ #
519
605
  # Configuration options:
520
606
  # * <tt>message</tt> - Specifies a custom error message (default is: "has already been taken")
521
607
  # * <tt>scope</tt> - One or more columns by which to limit the scope of the uniquness constraint.
522
608
  # * <tt>case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (true by default).
523
609
  # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
610
+ # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
524
611
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
525
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
526
- # method, proc or string should return or evaluate to a true or false value.
527
-
612
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
613
+ # method, proc or string should return or evaluate to a true or false value.
614
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
615
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
616
+ # method, proc or string should return or evaluate to a true or false value.
528
617
  def validates_uniqueness_of(*attr_names)
529
618
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
530
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
619
+ configuration.update(attr_names.extract_options!)
531
620
 
532
621
  validates_each(attr_names,configuration) do |record, attr_name, value|
533
622
  if value.nil? || (configuration[:case_sensitive] || !columns_hash[attr_name.to_s].text?)
@@ -537,6 +626,7 @@ module ActiveRecord
537
626
  condition_sql = "LOWER(#{record.class.table_name}.#{attr_name}) #{attribute_condition(value)}"
538
627
  condition_params = [value.downcase]
539
628
  end
629
+
540
630
  if scope = configuration[:scope]
541
631
  Array(scope).map do |scope_item|
542
632
  scope_value = record.send(scope_item)
@@ -544,17 +634,32 @@ module ActiveRecord
544
634
  condition_params << scope_value
545
635
  end
546
636
  end
637
+
547
638
  unless record.new_record?
548
639
  condition_sql << " AND #{record.class.table_name}.#{record.class.primary_key} <> ?"
549
640
  condition_params << record.send(:id)
550
641
  end
551
- if record.class.find(:first, :conditions => [condition_sql, *condition_params])
642
+
643
+ # The check for an existing value should be run from a class that
644
+ # isn't abstract. This means working down from the current class
645
+ # (self), to the first non-abstract class. Since classes don't know
646
+ # their subclasses, we have to build the hierarchy between self and
647
+ # the record's class.
648
+ class_hierarchy = [record.class]
649
+ while class_hierarchy.first != self
650
+ class_hierarchy.insert(0, class_hierarchy.first.superclass)
651
+ end
652
+
653
+ # Now we can work our way down the tree to the first non-abstract
654
+ # class (which has a database table to query from).
655
+ finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
656
+
657
+ if finder_class.find(:first, :conditions => [condition_sql, *condition_params])
552
658
  record.errors.add(attr_name, configuration[:message])
553
659
  end
554
660
  end
555
661
  end
556
662
 
557
-
558
663
 
559
664
  # Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression
560
665
  # provided.
@@ -572,11 +677,14 @@ module ActiveRecord
572
677
  # * <tt>with</tt> - The regular expression used to validate the format with (note: must be supplied!)
573
678
  # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
574
679
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
575
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
576
- # method, proc or string should return or evaluate to a true or false value.
680
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
681
+ # method, proc or string should return or evaluate to a true or false value.
682
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
683
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
684
+ # method, proc or string should return or evaluate to a true or false value.
577
685
  def validates_format_of(*attr_names)
578
686
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
579
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
687
+ configuration.update(attr_names.extract_options!)
580
688
 
581
689
  raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
582
690
 
@@ -588,27 +696,32 @@ module ActiveRecord
588
696
  # Validates whether the value of the specified attribute is available in a particular enumerable object.
589
697
  #
590
698
  # class Person < ActiveRecord::Base
591
- # validates_inclusion_of :gender, :in=>%w( m f ), :message=>"woah! what are you then!??!!"
592
- # validates_inclusion_of :age, :in=>0..99
699
+ # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
700
+ # validates_inclusion_of :age, :in => 0..99
701
+ # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
593
702
  # end
594
703
  #
595
704
  # Configuration options:
596
705
  # * <tt>in</tt> - An enumerable object of available items
597
706
  # * <tt>message</tt> - Specifies a customer error message (default is: "is not included in the list")
598
707
  # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
708
+ # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
599
709
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
600
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
601
- # method, proc or string should return or evaluate to a true or false value.
710
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
711
+ # method, proc or string should return or evaluate to a true or false value.
712
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
713
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
714
+ # method, proc or string should return or evaluate to a true or false value.
602
715
  def validates_inclusion_of(*attr_names)
603
716
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
604
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
717
+ configuration.update(attr_names.extract_options!)
605
718
 
606
719
  enum = configuration[:in] || configuration[:within]
607
720
 
608
721
  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?")
609
722
 
610
723
  validates_each(attr_names, configuration) do |record, attr_name, value|
611
- record.errors.add(attr_name, configuration[:message]) unless enum.include?(value)
724
+ record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value)
612
725
  end
613
726
  end
614
727
 
@@ -617,25 +730,30 @@ module ActiveRecord
617
730
  # class Person < ActiveRecord::Base
618
731
  # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
619
732
  # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
733
+ # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
620
734
  # end
621
735
  #
622
736
  # Configuration options:
623
737
  # * <tt>in</tt> - An enumerable object of items that the value shouldn't be part of
624
738
  # * <tt>message</tt> - Specifies a customer error message (default is: "is reserved")
625
739
  # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
740
+ # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
626
741
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
627
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
628
- # method, proc or string should return or evaluate to a true or false value.
742
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
743
+ # method, proc or string should return or evaluate to a true or false value.
744
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
745
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
746
+ # method, proc or string should return or evaluate to a true or false value.
629
747
  def validates_exclusion_of(*attr_names)
630
748
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
631
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
749
+ configuration.update(attr_names.extract_options!)
632
750
 
633
751
  enum = configuration[:in] || configuration[:within]
634
752
 
635
753
  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?")
636
754
 
637
755
  validates_each(attr_names, configuration) do |record, attr_name, value|
638
- record.errors.add(attr_name, configuration[:message]) if enum.include?(value)
756
+ record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value)
639
757
  end
640
758
  end
641
759
 
@@ -662,17 +780,21 @@ module ActiveRecord
662
780
  # is both present and guaranteed to be valid, you also need to use validates_presence_of.
663
781
  #
664
782
  # Configuration options:
783
+ # * <tt>message</tt> - A custom error message (default is: "is invalid")
665
784
  # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
666
785
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
667
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
668
- # method, proc or string should return or evaluate to a true or false value.
786
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
787
+ # method, proc or string should return or evaluate to a true or false value.
788
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
789
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
790
+ # method, proc or string should return or evaluate to a true or false value.
669
791
  def validates_associated(*attr_names)
670
792
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
671
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
793
+ configuration.update(attr_names.extract_options!)
672
794
 
673
795
  validates_each(attr_names, configuration) do |record, attr_name, value|
674
796
  record.errors.add(attr_name, configuration[:message]) unless
675
- (value.is_a?(Array) ? value : [value]).all? { |r| r.nil? or r.valid? }
797
+ (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
676
798
  end
677
799
  end
678
800
 
@@ -689,40 +811,67 @@ module ActiveRecord
689
811
  # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
690
812
  # * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
691
813
  # * <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
814
+ # * <tt>greater_than</tt> Specifies the value must be greater than the supplied value
815
+ # * <tt>greater_than_or_equal_to</tt> Specifies the value must be greater than or equal the supplied value
816
+ # * <tt>equal_to</tt> Specifies the value must be equal to the supplied value
817
+ # * <tt>less_than</tt> Specifies the value must be less than the supplied value
818
+ # * <tt>less_than_or_equal_to</tt> Specifies the value must be less than or equal the supplied value
819
+ # * <tt>odd</tt> Specifies the value must be an odd number
820
+ # * <tt>even</tt> Specifies the value must be an even number
692
821
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
693
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
694
- # method, proc or string should return or evaluate to a true or false value.
822
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
823
+ # method, proc or string should return or evaluate to a true or false value.
824
+ # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
825
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
826
+ # method, proc or string should return or evaluate to a true or false value.
695
827
  def validates_numericality_of(*attr_names)
696
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:not_a_number], :on => :save,
697
- :only_integer => false, :allow_nil => false }
698
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
828
+ configuration = { :on => :save, :only_integer => false, :allow_nil => false }
829
+ configuration.update(attr_names.extract_options!)
699
830
 
700
- if configuration[:only_integer]
701
- validates_each(attr_names,configuration) do |record, attr_name,value|
702
- record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_before_type_cast").to_s =~ /\A[+-]?\d+\Z/
703
- end
704
- else
705
- validates_each(attr_names,configuration) do |record, attr_name,value|
706
- next if configuration[:allow_nil] and record.send("#{attr_name}_before_type_cast").nil?
707
- begin
708
- Kernel.Float(record.send("#{attr_name}_before_type_cast").to_s)
831
+
832
+ numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys
833
+
834
+ (numericality_options - [ :odd, :even ]).each do |option|
835
+ raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric)
836
+ end
837
+
838
+ validates_each(attr_names,configuration) do |record, attr_name, value|
839
+ raw_value = record.send("#{attr_name}_before_type_cast") || value
840
+
841
+ next if configuration[:allow_nil] and raw_value.nil?
842
+
843
+ if configuration[:only_integer]
844
+ unless raw_value.to_s =~ /\A[+-]?\d+\Z/
845
+ record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
846
+ next
847
+ end
848
+ raw_value = raw_value.to_i
849
+ else
850
+ begin
851
+ raw_value = Kernel.Float(raw_value.to_s)
709
852
  rescue ArgumentError, TypeError
710
- record.errors.add(attr_name, configuration[:message])
853
+ record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
854
+ next
855
+ end
856
+ end
857
+
858
+ numericality_options.each do |option|
859
+ case option
860
+ when :odd, :even
861
+ record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
862
+ else
863
+ record.errors.add(attr_name, configuration[:message] || (ActiveRecord::Errors.default_error_messages[option] % configuration[option])) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
711
864
  end
712
865
  end
713
866
  end
714
867
  end
715
868
 
716
-
717
869
  # Creates an object just like Base.create but calls save! instead of save
718
870
  # so an exception is raised if the record is invalid.
719
871
  def create!(attributes = nil)
720
872
  if attributes.is_a?(Array)
721
873
  attributes.collect { |attr| create!(attr) }
722
874
  else
723
- attributes ||= {}
724
- attributes.reverse_merge!(scope(:create)) if scoped?(:create)
725
-
726
875
  object = new(attributes)
727
876
  object.save!
728
877
  object
@@ -733,7 +882,7 @@ module ActiveRecord
733
882
  private
734
883
  def write_inheritable_set(key, methods)
735
884
  existing_methods = read_inheritable_attribute(key) || []
736
- write_inheritable_attribute(key, methods | existing_methods)
885
+ write_inheritable_attribute(key, existing_methods | methods)
737
886
  end
738
887
 
739
888
  def validation_method(on)