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.
- data/CHANGELOG +2454 -34
- data/README +1 -1
- data/RUNNING_UNIT_TESTS +3 -34
- data/Rakefile +98 -77
- data/install.rb +1 -1
- data/lib/active_record.rb +13 -22
- data/lib/active_record/aggregations.rb +38 -49
- data/lib/active_record/associations.rb +452 -333
- data/lib/active_record/associations/association_collection.rb +66 -20
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +38 -18
- data/lib/active_record/associations/has_one_association.rb +30 -14
- data/lib/active_record/attribute_methods.rb +253 -0
- data/lib/active_record/base.rb +719 -494
- data/lib/active_record/calculations.rb +62 -63
- data/lib/active_record/callbacks.rb +57 -83
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
- data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
- data/lib/active_record/fixtures.rb +503 -113
- data/lib/active_record/locking/optimistic.rb +72 -34
- data/lib/active_record/migration.rb +80 -57
- data/lib/active_record/observer.rb +13 -10
- data/lib/active_record/query_cache.rb +16 -57
- data/lib/active_record/reflection.rb +35 -38
- data/lib/active_record/schema.rb +5 -5
- data/lib/active_record/schema_dumper.rb +35 -13
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
- data/lib/active_record/timestamp.rb +20 -21
- data/lib/active_record/transactions.rb +39 -43
- data/lib/active_record/validations.rb +256 -107
- data/lib/active_record/version.rb +3 -3
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +15 -2
- data/test/abstract_unit.rb +24 -17
- data/test/active_schema_test_mysql.rb +20 -8
- data/test/adapter_test.rb +23 -5
- data/test/adapter_test_sqlserver.rb +15 -1
- data/test/aggregations_test.rb +16 -1
- data/test/all.sh +2 -2
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +51 -30
- data/test/associations/cascaded_eager_loading_test.rb +1 -29
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +42 -6
- data/test/associations/extension_test.rb +6 -1
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +47 -16
- data/test/associations_test.rb +449 -226
- data/test/attribute_methods_test.rb +97 -0
- data/test/base_test.rb +251 -105
- data/test/binary_test.rb +22 -27
- data/test/calculations_test.rb +37 -5
- data/test/callbacks_test.rb +23 -0
- data/test/connection_test_firebird.rb +2 -2
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_mysql/connection.rb +3 -0
- data/test/connections/native_sqlite/connection.rb +5 -14
- data/test/connections/native_sqlite3/connection.rb +5 -14
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
- data/test/datatype_test_postgresql.rb +178 -27
- data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
- data/test/defaults_test.rb +8 -1
- data/test/deprecated_finder_test.rb +7 -128
- data/test/finder_test.rb +192 -54
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +12 -5
- data/test/fixtures/binaries.yml +130 -435
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +8 -1
- data/test/fixtures/computer.rb +1 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
- data/test/fixtures/db_definitions/firebird.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase.sql +5 -0
- data/test/fixtures/db_definitions/openbase.sql +41 -25
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +87 -58
- data/test/fixtures/db_definitions/postgresql2.sql +1 -2
- data/test/fixtures/db_definitions/schema.rb +280 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +4 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
- data/test/fixtures/db_definitions/sybase.sql +4 -0
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +0 -3
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixins.yml +2 -100
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +1 -0
- data/test/fixtures/project.rb +3 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/tagging.rb +4 -0
- data/test/fixtures/taggings.yml +8 -1
- data/test/fixtures/topic.rb +13 -1
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures_test.rb +205 -24
- data/test/inheritance_test.rb +7 -1
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +1 -1
- data/test/locking_test.rb +85 -2
- data/test/migration_test.rb +206 -40
- data/test/mixin_test.rb +13 -515
- data/test/pk_test.rb +3 -6
- data/test/query_cache_test.rb +104 -0
- data/test/reflection_test.rb +16 -0
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_dumper_test.rb +38 -3
- data/test/serialization_test.rb +47 -0
- data/test/transactions_test.rb +74 -23
- data/test/unconnected_test.rb +1 -1
- data/test/validations_test.rb +322 -32
- data/test/xml_serialization_test.rb +121 -44
- metadata +48 -41
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -85
- data/lib/active_record/acts/list.rb +0 -256
- data/lib/active_record/acts/nested_set.rb +0 -211
- data/lib/active_record/acts/tree.rb +0 -96
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
- data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
- data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
- data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
- data/lib/active_record/deprecated_associations.rb +0 -104
- data/lib/active_record/deprecated_finders.rb +0 -44
- data/lib/active_record/vendor/simple.rb +0 -693
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -58
- data/test/connections/native_sqlserver/connection.rb +0 -23
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
- data/test/deprecated_associations_test.rb +0 -396
- data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
- data/test/fixtures/db_definitions/mysql.sql +0 -234
- data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/mysql2.sql +0 -5
- data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
- data/test/fixtures/db_definitions/sqlserver.sql +0 -243
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
- data/test/fixtures/mixin.rb +0 -63
- 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
|
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
|
-
#
|
98
|
-
#
|
99
|
-
#
|
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
|
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
|
263
|
-
when String
|
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
|
-
#
|
290
|
-
#
|
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.
|
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
|
321
|
-
#
|
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
|
-
#
|
329
|
-
#
|
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.
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
358
|
-
#
|
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.
|
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
|
-
#
|
387
|
-
#
|
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
|
-
#
|
471
|
+
# validates_presence_of :invoice_id
|
393
472
|
#
|
394
473
|
# Not this:
|
395
|
-
#
|
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.
|
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
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
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
|
-
#
|
441
|
-
#
|
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.
|
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
|
-
#
|
526
|
-
#
|
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.
|
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
|
-
|
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
|
-
#
|
576
|
-
#
|
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.
|
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
|
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
|
-
#
|
601
|
-
#
|
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.
|
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
|
-
#
|
628
|
-
#
|
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.
|
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
|
-
#
|
668
|
-
#
|
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.
|
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]).
|
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
|
-
#
|
694
|
-
#
|
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 = { :
|
697
|
-
|
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
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
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,
|
885
|
+
write_inheritable_attribute(key, existing_methods | methods)
|
737
886
|
end
|
738
887
|
|
739
888
|
def validation_method(on)
|