activerecord 2.1.2 → 2.2.2

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 (110) hide show
  1. data/CHANGELOG +32 -6
  2. data/README +0 -0
  3. data/Rakefile +4 -5
  4. data/lib/active_record.rb +11 -10
  5. data/lib/active_record/aggregations.rb +110 -38
  6. data/lib/active_record/association_preload.rb +104 -15
  7. data/lib/active_record/associations.rb +427 -212
  8. data/lib/active_record/associations/association_collection.rb +101 -16
  9. data/lib/active_record/associations/association_proxy.rb +65 -13
  10. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
  12. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -3
  13. data/lib/active_record/associations/has_many_association.rb +28 -28
  14. data/lib/active_record/associations/has_many_through_association.rb +21 -19
  15. data/lib/active_record/associations/has_one_association.rb +24 -7
  16. data/lib/active_record/associations/has_one_through_association.rb +3 -4
  17. data/lib/active_record/attribute_methods.rb +13 -5
  18. data/lib/active_record/base.rb +435 -212
  19. data/lib/active_record/calculations.rb +12 -5
  20. data/lib/active_record/callbacks.rb +28 -9
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +355 -0
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +42 -215
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -5
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +48 -7
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -4
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -26
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +71 -45
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +155 -84
  30. data/lib/active_record/dirty.rb +25 -7
  31. data/lib/active_record/dynamic_finder_match.rb +41 -0
  32. data/lib/active_record/fixtures.rb +10 -9
  33. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  34. data/lib/active_record/locale/en.yml +54 -0
  35. data/lib/active_record/migration.rb +47 -10
  36. data/lib/active_record/named_scope.rb +29 -16
  37. data/lib/active_record/reflection.rb +118 -54
  38. data/lib/active_record/schema_dumper.rb +13 -7
  39. data/lib/active_record/test_case.rb +18 -5
  40. data/lib/active_record/transactions.rb +89 -34
  41. data/lib/active_record/validations.rb +270 -180
  42. data/lib/active_record/version.rb +1 -1
  43. data/test/cases/active_schema_test_mysql.rb +5 -0
  44. data/test/cases/adapter_test.rb +6 -0
  45. data/test/cases/aggregations_test.rb +39 -0
  46. data/test/cases/associations/belongs_to_associations_test.rb +10 -0
  47. data/test/cases/associations/eager_load_nested_include_test.rb +30 -12
  48. data/test/cases/associations/eager_test.rb +54 -5
  49. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +77 -10
  50. data/test/cases/associations/has_many_associations_test.rb +74 -7
  51. data/test/cases/associations/has_many_through_associations_test.rb +50 -3
  52. data/test/cases/associations/has_one_associations_test.rb +17 -0
  53. data/test/cases/associations/has_one_through_associations_test.rb +49 -1
  54. data/test/cases/associations_test.rb +0 -0
  55. data/test/cases/attribute_methods_test.rb +59 -4
  56. data/test/cases/base_test.rb +93 -21
  57. data/test/cases/binary_test.rb +1 -5
  58. data/test/cases/calculations_test.rb +5 -0
  59. data/test/cases/callbacks_observers_test.rb +38 -0
  60. data/test/cases/connection_test_mysql.rb +1 -1
  61. data/test/cases/defaults_test.rb +32 -1
  62. data/test/cases/deprecated_finder_test.rb +0 -0
  63. data/test/cases/dirty_test.rb +13 -0
  64. data/test/cases/finder_test.rb +162 -12
  65. data/test/cases/fixtures_test.rb +32 -3
  66. data/test/cases/helper.rb +15 -0
  67. data/test/cases/i18n_test.rb +41 -0
  68. data/test/cases/inheritance_test.rb +2 -2
  69. data/test/cases/lifecycle_test.rb +0 -0
  70. data/test/cases/locking_test.rb +4 -9
  71. data/test/cases/method_scoping_test.rb +109 -2
  72. data/test/cases/migration_test.rb +43 -8
  73. data/test/cases/multiple_db_test.rb +25 -0
  74. data/test/cases/named_scope_test.rb +74 -0
  75. data/test/cases/pooled_connections_test.rb +103 -0
  76. data/test/cases/readonly_test.rb +0 -0
  77. data/test/cases/reflection_test.rb +11 -3
  78. data/test/cases/reload_models_test.rb +20 -0
  79. data/test/cases/sanitize_test.rb +25 -0
  80. data/test/cases/schema_authorization_test_postgresql.rb +2 -2
  81. data/test/cases/transactions_test.rb +62 -12
  82. data/test/cases/unconnected_test.rb +0 -0
  83. data/test/cases/validations_i18n_test.rb +921 -0
  84. data/test/cases/validations_test.rb +44 -33
  85. data/test/connections/native_mysql/connection.rb +1 -3
  86. data/test/fixtures/companies.yml +1 -0
  87. data/test/fixtures/customers.yml +10 -1
  88. data/test/fixtures/fixture_database.sqlite3 +0 -0
  89. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  90. data/test/fixtures/organizations.yml +5 -0
  91. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  92. data/test/models/author.rb +3 -0
  93. data/test/models/category.rb +3 -0
  94. data/test/models/club.rb +6 -0
  95. data/test/models/company.rb +25 -1
  96. data/test/models/customer.rb +19 -1
  97. data/test/models/member.rb +2 -0
  98. data/test/models/member_detail.rb +4 -0
  99. data/test/models/organization.rb +4 -0
  100. data/test/models/parrot.rb +1 -0
  101. data/test/models/post.rb +3 -0
  102. data/test/models/reply.rb +0 -0
  103. data/test/models/topic.rb +3 -0
  104. data/test/schema/schema.rb +12 -1
  105. metadata +22 -10
  106. data/lib/active_record/vendor/mysql.rb +0 -1214
  107. data/test/cases/adapter_test_sqlserver.rb +0 -95
  108. data/test/cases/table_name_test_sqlserver.rb +0 -23
  109. data/test/cases/threaded_connections_test.rb +0 -48
  110. data/test/schema/sqlserver_specific_schema.rb +0 -5
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
- # Raised by save! and create! when the record is invalid. Use the
2
+ # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
3
3
  # +record+ method to retrieve the record which did not validate.
4
4
  # begin
5
5
  # complex_operation_that_calls_save!_internally
@@ -18,70 +18,97 @@ module ActiveRecord
18
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
+
22
+ class << self
23
+ def default_error_messages
24
+ ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages').")
25
+ I18n.translate 'activerecord.errors.messages'
26
+ end
27
+ end
21
28
 
22
29
  def initialize(base) # :nodoc:
23
30
  @base, @errors = base, {}
24
31
  end
25
32
 
26
- @@default_error_messages = {
27
- :inclusion => "is not included in the list",
28
- :exclusion => "is reserved",
29
- :invalid => "is invalid",
30
- :confirmation => "doesn't match confirmation",
31
- :accepted => "must be accepted",
32
- :empty => "can't be empty",
33
- :blank => "can't be blank",
34
- :too_long => "is too long (maximum is %d characters)",
35
- :too_short => "is too short (minimum is %d characters)",
36
- :wrong_length => "is the wrong length (should be %d characters)",
37
- :taken => "has already been taken",
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"
46
- }
47
-
48
- # Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
49
- cattr_accessor :default_error_messages
50
-
51
-
52
33
  # Adds an error to the base object instead of any particular attribute. This is used
53
34
  # to report errors that don't tie to any specific attribute, but rather to the object
54
35
  # as a whole. These error messages don't get prepended with any field name when iterating
55
- # with each_full, so they should be complete sentences.
36
+ # with +each_full+, so they should be complete sentences.
56
37
  def add_to_base(msg)
57
38
  add(:base, msg)
58
39
  end
59
40
 
60
- # Adds an error message (+msg+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
41
+ # Adds an error message (+messsage+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
61
42
  # for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one
62
43
  # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
63
- # If no +msg+ is supplied, "invalid" is assumed.
64
- def add(attribute, msg = @@default_error_messages[:invalid])
65
- @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
66
- @errors[attribute.to_s] << msg
44
+ # If no +messsage+ is supplied, :invalid is assumed.
45
+ # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
46
+ def add(attribute, message = nil, options = {})
47
+ message ||= :invalid
48
+ message = generate_message(attribute, message, options) if message.is_a?(Symbol)
49
+ @errors[attribute.to_s] ||= []
50
+ @errors[attribute.to_s] << message
67
51
  end
68
52
 
69
53
  # Will add an error message to each of the attributes in +attributes+ that is empty.
70
- def add_on_empty(attributes, msg = @@default_error_messages[:empty])
54
+ def add_on_empty(attributes, custom_message = nil)
71
55
  for attr in [attributes].flatten
72
56
  value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s]
73
- is_empty = value.respond_to?("empty?") ? value.empty? : false
74
- add(attr, msg) unless !value.nil? && !is_empty
57
+ is_empty = value.respond_to?(:empty?) ? value.empty? : false
58
+ add(attr, :empty, :default => custom_message) unless !value.nil? && !is_empty
75
59
  end
76
60
  end
77
61
 
78
62
  # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
79
- def add_on_blank(attributes, msg = @@default_error_messages[:blank])
63
+ def add_on_blank(attributes, custom_message = nil)
80
64
  for attr in [attributes].flatten
81
65
  value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s]
82
- add(attr, msg) if value.blank?
66
+ add(attr, :blank, :default => custom_message) if value.blank?
83
67
  end
84
68
  end
69
+
70
+ # Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
71
+ # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
72
+ # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
73
+ # default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
74
+ # translated attribute name and the value are available for interpolation.
75
+ #
76
+ # When using inheritence in your models, it will check all the inherited models too, but only if the model itself
77
+ # hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
78
+ # error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
79
+ #
80
+ # <ol>
81
+ # <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
82
+ # <li><tt>activerecord.errors.models.admin.blank</tt></li>
83
+ # <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
84
+ # <li><tt>activerecord.errors.models.user.blank</tt></li>
85
+ # <li><tt>activerecord.errors.messages.blank</tt></li>
86
+ # <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
87
+ # </ol>
88
+ def generate_message(attribute, message = :invalid, options = {})
89
+
90
+ message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
91
+
92
+ defaults = @base.class.self_and_descendents_from_active_record.map do |klass|
93
+ [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
94
+ :"models.#{klass.name.underscore}.#{message}" ]
95
+ end
96
+
97
+ defaults << options.delete(:default)
98
+ defaults = defaults.compact.flatten << :"messages.#{message}"
99
+
100
+ key = defaults.shift
101
+ value = @base.respond_to?(attribute) ? @base.send(attribute) : nil
102
+
103
+ options = { :default => defaults,
104
+ :model => @base.class.human_name,
105
+ :attribute => @base.class.human_attribute_name(attribute.to_s),
106
+ :value => value,
107
+ :scope => [:activerecord, :errors]
108
+ }.merge(options)
109
+
110
+ I18n.translate(key, options)
111
+ end
85
112
 
86
113
  # Returns true if the specified +attribute+ has errors associated with it.
87
114
  #
@@ -97,7 +124,7 @@ module ActiveRecord
97
124
  !@errors[attribute.to_s].nil?
98
125
  end
99
126
 
100
- # Returns nil, if no errors are associated with the specified +attribute+.
127
+ # Returns +nil+, if no errors are associated with the specified +attribute+.
101
128
  # Returns the error message, if one error is associated with the specified +attribute+.
102
129
  # Returns an array of error messages, if more than one error is associated with the specified +attribute+.
103
130
  #
@@ -118,7 +145,7 @@ module ActiveRecord
118
145
 
119
146
  alias :[] :on
120
147
 
121
- # Returns errors assigned to the base object through add_to_base according to the normal rules of on(attribute).
148
+ # Returns errors assigned to the base object through +add_to_base+ according to the normal rules of <tt>on(attribute)</tt>.
122
149
  def on_base
123
150
  on(:base)
124
151
  end
@@ -131,15 +158,15 @@ module ActiveRecord
131
158
  # end
132
159
  #
133
160
  # 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
161
+ # company.errors.each{|attr,msg| puts "#{attr} - #{msg}" }
162
+ # # => name - is too short (minimum is 5 characters)
163
+ # # name - can't be blank
164
+ # # address - can't be blank
138
165
  def each
139
166
  @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
140
167
  end
141
168
 
142
- # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
169
+ # Yields each full error message added. So <tt>Person.errors.add("first_name", "can't be empty")</tt> will be returned
143
170
  # through iteration as "First name can't be empty".
144
171
  #
145
172
  # class Company < ActiveRecord::Base
@@ -148,10 +175,10 @@ module ActiveRecord
148
175
  # end
149
176
  #
150
177
  # 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
178
+ # company.errors.each_full{|msg| puts msg }
179
+ # # => Name is too short (minimum is 5 characters)
180
+ # # Name can't be blank
181
+ # # Address can't be blank
155
182
  def each_full
156
183
  full_messages.each { |msg| yield msg }
157
184
  end
@@ -166,22 +193,24 @@ module ActiveRecord
166
193
  # company = Company.create(:address => '123 First St.')
167
194
  # company.errors.full_messages # =>
168
195
  # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
169
- def full_messages
196
+ def full_messages(options = {})
170
197
  full_messages = []
171
198
 
172
199
  @errors.each_key do |attr|
173
- @errors[attr].each do |msg|
174
- next if msg.nil?
200
+ @errors[attr].each do |message|
201
+ next unless message
175
202
 
176
203
  if attr == "base"
177
- full_messages << msg
204
+ full_messages << message
178
205
  else
179
- full_messages << @base.class.human_attribute_name(attr) + " " + msg
206
+ #key = :"activerecord.att.#{@base.class.name.underscore.to_sym}.#{attr}"
207
+ attr_name = @base.class.human_attribute_name(attr)
208
+ full_messages << attr_name + ' ' + message
180
209
  end
181
210
  end
182
211
  end
183
212
  full_messages
184
- end
213
+ end
185
214
 
186
215
  # Returns true if no errors have been added.
187
216
  def empty?
@@ -209,13 +238,13 @@ module ActiveRecord
209
238
  # end
210
239
  #
211
240
  # 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>
241
+ # company.errors.to_xml
242
+ # # => <?xml version="1.0" encoding="UTF-8"?>
243
+ # # <errors>
244
+ # # <error>Name is too short (minimum is 5 characters)</error>
245
+ # # <error>Name can't be blank</error>
246
+ # # <error>Address can't be blank</error>
247
+ # # </errors>
219
248
  def to_xml(options={})
220
249
  options[:root] ||= "errors"
221
250
  options[:indent] ||= 2
@@ -226,9 +255,12 @@ module ActiveRecord
226
255
  full_messages.each { |msg| e.error(msg) }
227
256
  end
228
257
  end
258
+
229
259
  end
230
260
 
231
261
 
262
+ # Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations.
263
+ #
232
264
  # Active Records implement validation by overwriting Base#validate (or the variations, +validate_on_create+ and
233
265
  # +validate_on_update+). Each of these methods can inspect the state of the object, which usually means ensuring
234
266
  # that a number of attributes have a certain value (such as not empty, within a given range, matching a certain regular expression).
@@ -261,14 +293,12 @@ module ActiveRecord
261
293
  # person.errors.on "phone_number" # => "has invalid format"
262
294
  # person.errors.each_full { |msg| puts msg }
263
295
  # # => "Last name can't be empty\n" +
264
- # "Phone number has invalid format"
296
+ # # "Phone number has invalid format"
265
297
  #
266
298
  # person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
267
299
  # person.save # => true (and person is now saved in the database)
268
300
  #
269
301
  # An Errors object is automatically created for every Active Record.
270
- #
271
- # Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations.
272
302
  module Validations
273
303
  VALIDATIONS = %w( validate validate_on_create validate_on_update )
274
304
 
@@ -277,16 +307,56 @@ module ActiveRecord
277
307
  base.class_eval do
278
308
  alias_method_chain :save, :validation
279
309
  alias_method_chain :save!, :validation
280
- alias_method_chain :update_attribute, :validation_skipping
281
310
  end
282
311
 
283
312
  base.send :include, ActiveSupport::Callbacks
284
313
  base.define_callbacks *VALIDATIONS
285
314
  end
286
315
 
287
- # All of the following validations are defined in the class scope of the model that you're interested in validating.
288
- # They offer a more declarative way of specifying when the model is valid and when it is not. It is recommended to use
289
- # these over the low-level calls to +validate+ and +validate_on_create+ when possible.
316
+ # Active Record classes can implement validations in several ways. The highest level, easiest to read,
317
+ # and recommended approach is to use the declarative <tt>validates_..._of</tt> class methods (and
318
+ # +validates_associated+) documented below. These are sufficient for most model validations.
319
+ #
320
+ # Slightly lower level is +validates_each+. It provides some of the same options as the purely declarative
321
+ # validation methods, but like all the lower-level approaches it requires manually adding to the errors collection
322
+ # when the record is invalid.
323
+ #
324
+ # At a yet lower level, a model can use the class methods +validate+, +validate_on_create+ and +validate_on_update+
325
+ # to add validation methods or blocks. These are ActiveSupport::Callbacks and follow the same rules of inheritance
326
+ # and chaining.
327
+ #
328
+ # The lowest level style is to define the instance methods +validate+, +validate_on_create+ and +validate_on_update+
329
+ # as documented in ActiveRecord::Validations.
330
+ #
331
+ # == +validate+, +validate_on_create+ and +validate_on_update+ Class Methods
332
+ #
333
+ # Calls to these methods add a validation method or block to the class. Again, this approach is recommended
334
+ # only when the higher-level methods documented below (<tt>validates_..._of</tt> and +validates_associated+) are
335
+ # insufficient to handle the required validation.
336
+ #
337
+ # This can be done with a symbol pointing to a method:
338
+ #
339
+ # class Comment < ActiveRecord::Base
340
+ # validate :must_be_friends
341
+ #
342
+ # def must_be_friends
343
+ # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
344
+ # end
345
+ # end
346
+ #
347
+ # Or with a block which is passed the current record to be validated:
348
+ #
349
+ # class Comment < ActiveRecord::Base
350
+ # validate do |comment|
351
+ # comment.must_be_friends
352
+ # end
353
+ #
354
+ # def must_be_friends
355
+ # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
356
+ # end
357
+ # end
358
+ #
359
+ # This usage applies to +validate_on_create+ and +validate_on_update+ as well.
290
360
  module ClassMethods
291
361
  DEFAULT_VALIDATION_OPTIONS = {
292
362
  :on => :save,
@@ -300,34 +370,6 @@ module ActiveRecord
300
370
  :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
301
371
  :odd => 'odd?', :even => 'even?' }.freeze
302
372
 
303
- # Adds a validation method or block to the class. This is useful when
304
- # overriding the +validate+ instance method becomes too unwieldly and
305
- # you're looking for more descriptive declaration of your validations.
306
- #
307
- # This can be done with a symbol pointing to a method:
308
- #
309
- # class Comment < ActiveRecord::Base
310
- # validate :must_be_friends
311
- #
312
- # def must_be_friends
313
- # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
314
- # end
315
- # end
316
- #
317
- # Or with a block which is passed the current record to be validated:
318
- #
319
- # class Comment < ActiveRecord::Base
320
- # validate do |comment|
321
- # comment.must_be_friends
322
- # end
323
- #
324
- # def must_be_friends
325
- # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
326
- # end
327
- # end
328
- #
329
- # This usage applies to +validate_on_create+ and +validate_on_update+ as well.
330
-
331
373
  # Validates each attribute against a block.
332
374
  #
333
375
  # class Person < ActiveRecord::Base
@@ -389,13 +431,15 @@ module ActiveRecord
389
431
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
390
432
  # method, proc or string should return or evaluate to a true or false value.
391
433
  def validates_confirmation_of(*attr_names)
392
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
434
+ configuration = { :on => :save }
393
435
  configuration.update(attr_names.extract_options!)
394
436
 
395
437
  attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" }))
396
438
 
397
439
  validates_each(attr_names, configuration) do |record, attr_name, value|
398
- record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
440
+ unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
441
+ record.errors.add(attr_name, :confirmation, :default => configuration[:message])
442
+ end
399
443
  end
400
444
  end
401
445
 
@@ -423,7 +467,7 @@ module ActiveRecord
423
467
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
424
468
  # method, proc or string should return or evaluate to a true or false value.
425
469
  def validates_acceptance_of(*attr_names)
426
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
470
+ configuration = { :on => :save, :allow_nil => true, :accept => "1" }
427
471
  configuration.update(attr_names.extract_options!)
428
472
 
429
473
  db_cols = begin
@@ -435,7 +479,9 @@ module ActiveRecord
435
479
  attr_accessor(*names)
436
480
 
437
481
  validates_each(attr_names,configuration) do |record, attr_name, value|
438
- record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept]
482
+ unless value == configuration[:accept]
483
+ record.errors.add(attr_name, :accepted, :default => configuration[:message])
484
+ end
439
485
  end
440
486
  end
441
487
 
@@ -462,7 +508,7 @@ module ActiveRecord
462
508
  # method, proc or string should return or evaluate to a true or false value.
463
509
  #
464
510
  def validates_presence_of(*attr_names)
465
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
511
+ configuration = { :on => :save }
466
512
  configuration.update(attr_names.extract_options!)
467
513
 
468
514
  # can't use validates_each here, because it cannot cope with nonexistent attributes,
@@ -476,13 +522,13 @@ module ActiveRecord
476
522
  #
477
523
  # class Person < ActiveRecord::Base
478
524
  # validates_length_of :first_name, :maximum=>30
479
- # validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
525
+ # validates_length_of :last_name, :maximum=>30, :message=>"less than {{count}} if you don't mind"
480
526
  # validates_length_of :fax, :in => 7..32, :allow_nil => true
481
527
  # validates_length_of :phone, :in => 7..32, :allow_blank => true
482
528
  # validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
483
- # validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least %d character"
484
- # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with %d characters... don't play me."
485
- # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
529
+ # validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least {{count}} character"
530
+ # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with {{count}} characters... don't play me."
531
+ # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least {{count}} words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
486
532
  # end
487
533
  #
488
534
  # Configuration options:
@@ -493,9 +539,9 @@ module ActiveRecord
493
539
  # * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
494
540
  # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
495
541
  # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
496
- # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)").
497
- # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)").
498
- # * <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 %d characters)").
542
+ # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {{count}} characters)").
543
+ # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is {{count}} characters)").
544
+ # * <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)").
499
545
  # * <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.
500
546
  # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
501
547
  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
@@ -510,10 +556,7 @@ module ActiveRecord
510
556
  def validates_length_of(*attrs)
511
557
  # Merge given options with defaults.
512
558
  options = {
513
- :too_long => ActiveRecord::Errors.default_error_messages[:too_long],
514
- :too_short => ActiveRecord::Errors.default_error_messages[:too_short],
515
- :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length],
516
- :tokenizer => lambda {|value| value.split(//)}
559
+ :tokenizer => lambda {|value| value.split(//)}
517
560
  }.merge(DEFAULT_VALIDATION_OPTIONS)
518
561
  options.update(attrs.extract_options!.symbolize_keys)
519
562
 
@@ -536,15 +579,12 @@ module ActiveRecord
536
579
  when :within, :in
537
580
  raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
538
581
 
539
- too_short = options[:too_short] % option_value.begin
540
- too_long = options[:too_long] % option_value.end
541
-
542
582
  validates_each(attrs, options) do |record, attr, value|
543
583
  value = options[:tokenizer].call(value) if value.kind_of?(String)
544
584
  if value.nil? or value.size < option_value.begin
545
- record.errors.add(attr, too_short)
585
+ record.errors.add(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
546
586
  elsif value.size > option_value.end
547
- record.errors.add(attr, too_long)
587
+ record.errors.add(attr, :too_long, :default => options[:too_long], :count => option_value.end)
548
588
  end
549
589
  end
550
590
  when :is, :minimum, :maximum
@@ -554,11 +594,13 @@ module ActiveRecord
554
594
  validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
555
595
  message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
556
596
 
557
- message = (options[:message] || options[message_options[option]]) % option_value
558
-
559
597
  validates_each(attrs, options) do |record, attr, value|
560
598
  value = options[:tokenizer].call(value) if value.kind_of?(String)
561
- record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
599
+ unless !value.nil? and value.size.method(validity_checks[option])[option_value]
600
+ key = message_options[option]
601
+ custom_message = options[:message] || options[key]
602
+ record.errors.add(attr, key, :default => custom_message, :count => option_value)
603
+ end
562
604
  end
563
605
  end
564
606
  end
@@ -583,14 +625,10 @@ module ActiveRecord
583
625
  # 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
584
626
  # attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
585
627
  #
586
- # Because this check is performed outside the database there is still a chance that duplicate values
587
- # will be inserted in two parallel transactions. To guarantee against this you should create a
588
- # unique index on the field. See +add_index+ for more information.
589
- #
590
628
  # Configuration options:
591
629
  # * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
592
630
  # * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
593
- # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
631
+ # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
594
632
  # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
595
633
  # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
596
634
  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
@@ -599,8 +637,72 @@ module ActiveRecord
599
637
  # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
600
638
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
601
639
  # method, proc or string should return or evaluate to a true or false value.
640
+ #
641
+ # === Concurrency and integrity
642
+ #
643
+ # Using this validation method in conjunction with ActiveRecord::Base#save
644
+ # does not guarantee the absence of duplicate record insertions, because
645
+ # uniqueness checks on the application level are inherently prone to race
646
+ # conditions. For example, suppose that two users try to post a Comment at
647
+ # the same time, and a Comment's title must be unique. At the database-level,
648
+ # the actions performed by these users could be interleaved in the following manner:
649
+ #
650
+ # User 1 | User 2
651
+ # ------------------------------------+--------------------------------------
652
+ # # User 1 checks whether there's |
653
+ # # already a comment with the title |
654
+ # # 'My Post'. This is not the case. |
655
+ # SELECT * FROM comments |
656
+ # WHERE title = 'My Post' |
657
+ # |
658
+ # | # User 2 does the same thing and also
659
+ # | # infers that his title is unique.
660
+ # | SELECT * FROM comments
661
+ # | WHERE title = 'My Post'
662
+ # |
663
+ # # User 1 inserts his comment. |
664
+ # INSERT INTO comments |
665
+ # (title, content) VALUES |
666
+ # ('My Post', 'hi!') |
667
+ # |
668
+ # | # User 2 does the same thing.
669
+ # | INSERT INTO comments
670
+ # | (title, content) VALUES
671
+ # | ('My Post', 'hello!')
672
+ # |
673
+ # | # ^^^^^^
674
+ # | # Boom! We now have a duplicate
675
+ # | # title!
676
+ #
677
+ # This could even happen if you use transactions with the 'serializable'
678
+ # isolation level. There are several ways to get around this problem:
679
+ # - By locking the database table before validating, and unlocking it after
680
+ # saving. However, table locking is very expensive, and thus not
681
+ # recommended.
682
+ # - By locking a lock file before validating, and unlocking it after saving.
683
+ # This does not work if you've scaled your Rails application across
684
+ # multiple web servers (because they cannot share lock files, or cannot
685
+ # do that efficiently), and thus not recommended.
686
+ # - Creating a unique index on the field, by using
687
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
688
+ # rare case that a race condition occurs, the database will guarantee
689
+ # the field's uniqueness.
690
+ #
691
+ # When the database catches such a duplicate insertion,
692
+ # ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
693
+ # exception. You can either choose to let this error propagate (which
694
+ # will result in the default Rails exception page being shown), or you
695
+ # can catch it and restart the transaction (e.g. by telling the user
696
+ # that the title already exists, and asking him to re-enter the title).
697
+ # This technique is also known as optimistic concurrency control:
698
+ # http://en.wikipedia.org/wiki/Optimistic_concurrency_control
699
+ #
700
+ # Active Record currently provides no way to distinguish unique
701
+ # index constraint errors from other types of database errors, so you
702
+ # will have to parse the (database-specific) exception message to detect
703
+ # such a case.
602
704
  def validates_uniqueness_of(*attr_names)
603
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
705
+ configuration = { :case_sensitive => true }
604
706
  configuration.update(attr_names.extract_options!)
605
707
 
606
708
  validates_each(attr_names,configuration) do |record, attr_name, value|
@@ -620,18 +722,23 @@ module ActiveRecord
620
722
 
621
723
  is_text_column = finder_class.columns_hash[attr_name.to_s].text?
622
724
 
623
- if !value.nil? && is_text_column
725
+ if value.nil?
726
+ comparison_operator = "IS ?"
727
+ elsif is_text_column
728
+ comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
624
729
  value = value.to_s
730
+ else
731
+ comparison_operator = "= ?"
625
732
  end
626
733
 
734
+ sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
735
+
627
736
  if value.nil? || (configuration[:case_sensitive] || !is_text_column)
628
- condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
737
+ condition_sql = "#{sql_attribute} #{comparison_operator}"
629
738
  condition_params = [value]
630
739
  else
631
- # sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
632
- # Hence, this is needed only for sqlite.
633
- condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
634
- condition_params = [value.chars.downcase.to_s]
740
+ condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}"
741
+ condition_params = [value.mb_chars.downcase]
635
742
  end
636
743
 
637
744
  if scope = configuration[:scope]
@@ -647,26 +754,10 @@ module ActiveRecord
647
754
  condition_params << record.send(:id)
648
755
  end
649
756
 
650
- results = finder_class.with_exclusive_scope do
651
- connection.select_all(
652
- construct_finder_sql(
653
- :select => "#{connection.quote_column_name(attr_name)}",
654
- :from => "#{finder_class.quoted_table_name}",
655
- :conditions => [condition_sql, *condition_params]
656
- )
657
- )
658
- end
659
-
660
- unless results.length.zero?
661
- found = true
662
-
663
- # As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate
664
- # column in ruby when case sensitive option
665
- if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text?
666
- found = results.any? { |a| a[attr_name.to_s] == value }
757
+ finder_class.with_exclusive_scope do
758
+ if finder_class.exists?([condition_sql, *condition_params])
759
+ record.errors.add(attr_name, :taken, :default => configuration[:message], :value => value)
667
760
  end
668
-
669
- record.errors.add(attr_name, configuration[:message]) if found
670
761
  end
671
762
  end
672
763
  end
@@ -696,13 +787,15 @@ module ActiveRecord
696
787
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
697
788
  # method, proc or string should return or evaluate to a true or false value.
698
789
  def validates_format_of(*attr_names)
699
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
790
+ configuration = { :on => :save, :with => nil }
700
791
  configuration.update(attr_names.extract_options!)
701
792
 
702
793
  raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
703
794
 
704
795
  validates_each(attr_names, configuration) do |record, attr_name, value|
705
- record.errors.add(attr_name, configuration[:message] % value) unless value.to_s =~ configuration[:with]
796
+ unless value.to_s =~ configuration[:with]
797
+ record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
798
+ end
706
799
  end
707
800
  end
708
801
 
@@ -711,7 +804,7 @@ module ActiveRecord
711
804
  # class Person < ActiveRecord::Base
712
805
  # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
713
806
  # validates_inclusion_of :age, :in => 0..99
714
- # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
807
+ # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list"
715
808
  # end
716
809
  #
717
810
  # Configuration options:
@@ -726,15 +819,17 @@ module ActiveRecord
726
819
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
727
820
  # method, proc or string should return or evaluate to a true or false value.
728
821
  def validates_inclusion_of(*attr_names)
729
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
822
+ configuration = { :on => :save }
730
823
  configuration.update(attr_names.extract_options!)
731
824
 
732
825
  enum = configuration[:in] || configuration[:within]
733
826
 
734
- 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?")
827
+ 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?)
735
828
 
736
829
  validates_each(attr_names, configuration) do |record, attr_name, value|
737
- record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value)
830
+ unless enum.include?(value)
831
+ record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
832
+ end
738
833
  end
739
834
  end
740
835
 
@@ -743,7 +838,7 @@ module ActiveRecord
743
838
  # class Person < ActiveRecord::Base
744
839
  # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
745
840
  # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
746
- # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
841
+ # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {{value}} is not allowed"
747
842
  # end
748
843
  #
749
844
  # Configuration options:
@@ -758,15 +853,17 @@ module ActiveRecord
758
853
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
759
854
  # method, proc or string should return or evaluate to a true or false value.
760
855
  def validates_exclusion_of(*attr_names)
761
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
856
+ configuration = { :on => :save }
762
857
  configuration.update(attr_names.extract_options!)
763
858
 
764
859
  enum = configuration[:in] || configuration[:within]
765
860
 
766
- 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?")
861
+ 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?)
767
862
 
768
863
  validates_each(attr_names, configuration) do |record, attr_name, value|
769
- record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value)
864
+ if enum.include?(value)
865
+ record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
866
+ end
770
867
  end
771
868
  end
772
869
 
@@ -802,12 +899,13 @@ module ActiveRecord
802
899
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
803
900
  # method, proc or string should return or evaluate to a true or false value.
804
901
  def validates_associated(*attr_names)
805
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
902
+ configuration = { :on => :save }
806
903
  configuration.update(attr_names.extract_options!)
807
904
 
808
905
  validates_each(attr_names, configuration) do |record, attr_name, value|
809
- record.errors.add(attr_name, configuration[:message]) unless
810
- (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
906
+ unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
907
+ record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
908
+ end
811
909
  end
812
910
  end
813
911
 
@@ -855,15 +953,15 @@ module ActiveRecord
855
953
 
856
954
  if configuration[:only_integer]
857
955
  unless raw_value.to_s =~ /\A[+-]?\d+\Z/
858
- record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
956
+ record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
859
957
  next
860
958
  end
861
959
  raw_value = raw_value.to_i
862
960
  else
863
- begin
961
+ begin
864
962
  raw_value = Kernel.Float(raw_value)
865
963
  rescue ArgumentError, TypeError
866
- record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
964
+ record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
867
965
  next
868
966
  end
869
967
  end
@@ -871,11 +969,11 @@ module ActiveRecord
871
969
  numericality_options.each do |option|
872
970
  case option
873
971
  when :odd, :even
874
- record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
972
+ unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
973
+ record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
974
+ end
875
975
  else
876
- message = configuration[:message] || ActiveRecord::Errors.default_error_messages[option]
877
- message = message % configuration[option] if configuration[option]
878
- record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
976
+ record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
879
977
  end
880
978
  end
881
979
  end
@@ -924,14 +1022,6 @@ module ActiveRecord
924
1022
  end
925
1023
  end
926
1024
 
927
- # Updates a single attribute and saves the record without going through the normal validation procedure.
928
- # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
929
- # in Base is replaced with this when the validations module is mixed in, which it is by default.
930
- def update_attribute_with_validation_skipping(name, value)
931
- send(name.to_s + '=', value)
932
- save(false)
933
- end
934
-
935
1025
  # Runs +validate+ and +validate_on_create+ or +validate_on_update+ and returns true if no errors were added otherwise false.
936
1026
  def valid?
937
1027
  errors.clear