activerecord 1.1.0 → 1.2.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 +250 -0
- data/README +17 -9
- data/dev-utils/eval_debugger.rb +1 -1
- data/install.rb +3 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/acts/list.rb +178 -0
- data/lib/active_record/acts/tree.rb +44 -0
- data/lib/active_record/associations.rb +45 -8
- data/lib/active_record/associations/association_collection.rb +18 -9
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +14 -13
- data/lib/active_record/associations/has_many_association.rb +21 -12
- data/lib/active_record/base.rb +137 -37
- data/lib/active_record/callbacks.rb +30 -25
- data/lib/active_record/connection_adapters/abstract_adapter.rb +57 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +4 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +298 -0
- data/lib/active_record/fixtures.rb +241 -147
- data/lib/active_record/support/class_inheritable_attributes.rb +5 -2
- data/lib/active_record/support/inflector.rb +13 -12
- data/lib/active_record/support/misc.rb +6 -0
- data/lib/active_record/timestamp.rb +33 -0
- data/lib/active_record/transactions.rb +1 -1
- data/lib/active_record/validations.rb +294 -16
- data/rakefile +3 -7
- data/test/abstract_unit.rb +1 -4
- data/test/associations_test.rb +17 -4
- data/test/base_test.rb +37 -5
- data/test/connections/native_sqlserver/connection.rb +15 -0
- data/test/deprecated_associations_test.rb +40 -38
- data/test/finder_test.rb +82 -4
- data/test/fixtures/accounts.yml +8 -0
- data/test/fixtures/company.rb +6 -0
- data/test/fixtures/company_in_module.rb +1 -1
- data/test/fixtures/db_definitions/mysql.sql +13 -0
- data/test/fixtures/db_definitions/postgresql.sql +13 -0
- data/test/fixtures/db_definitions/sqlite.sql +14 -0
- data/test/fixtures/db_definitions/sqlserver.sql +110 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +4 -0
- data/test/fixtures/developer.rb +2 -2
- data/test/fixtures/developers.yml +13 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/mixin.rb +17 -0
- data/test/fixtures/mixins.yml +14 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/project.rb +6 -0
- data/test/fixtures/reply.rb +14 -1
- data/test/fixtures/topic.rb +2 -2
- data/test/fixtures/topics/first +1 -0
- data/test/fixtures_test.rb +42 -12
- data/test/inflector_test.rb +2 -1
- data/test/inheritance_test.rb +22 -12
- data/test/mixin_test.rb +138 -0
- data/test/pk_test.rb +4 -2
- data/test/reflection_test.rb +3 -3
- data/test/transactions_test.rb +15 -0
- data/test/validations_test.rb +229 -4
- metadata +24 -10
- data/lib/active_record/associations.rb.orig +0 -555
- data/test/deprecated_associations_test.rb.orig +0 -334
- data/test/fixtures/accounts/signals37 +0 -3
- data/test/fixtures/accounts/unknown +0 -2
- data/test/fixtures/developers/david +0 -2
- data/test/fixtures/developers/jamis +0 -2
@@ -28,10 +28,13 @@ module ClassInheritableAttributes # :nodoc:
|
|
28
28
|
inheritable_attributes[key]
|
29
29
|
end
|
30
30
|
|
31
|
+
def reset_inheritable_attributes
|
32
|
+
inheritable_attributes.clear
|
33
|
+
end
|
34
|
+
|
31
35
|
private
|
32
36
|
def inherited(child)
|
33
37
|
@@classes[child] = inheritable_attributes.dup
|
34
|
-
end
|
35
|
-
|
38
|
+
end
|
36
39
|
end
|
37
40
|
end
|
@@ -62,17 +62,18 @@ module Inflector
|
|
62
62
|
|
63
63
|
def singular_rules #:doc:
|
64
64
|
[
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
]
|
65
|
+
[/(x|ch|ss)es$/, '\1'],
|
66
|
+
[/movies$/, 'movie'],
|
67
|
+
[/([^aeiouy]|qu)ies$/, '\1y'],
|
68
|
+
[/([lr])ves$/, '\1f'],
|
69
|
+
[/([^f])ves$/, '\1fe'],
|
70
|
+
[/(analy|ba|diagno|parenthe|progno|synop|the)ses$/, '\1sis'],
|
71
|
+
[/([ti])a$/, '\1um'],
|
72
|
+
[/people$/, 'person'],
|
73
|
+
[/men$/, 'man'],
|
74
|
+
[/status$/, 'status'],
|
75
|
+
[/children$/, 'child'],
|
76
|
+
[/s$/, '']
|
77
|
+
]
|
77
78
|
end
|
78
79
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# Active Records will automatically record creation and/or update timestamps of database objects
|
3
|
+
# if fields of the names created_at/created_on or updated_at/updated_on are present. This module is
|
4
|
+
# automatically included, so you don't need to do that manually.
|
5
|
+
#
|
6
|
+
# This behavior can be turned off by setting <tt>ActiveRecord::Base.record_timestamps = false</tt>.
|
7
|
+
module Timestamp
|
8
|
+
def self.append_features(base) # :nodoc:
|
9
|
+
super
|
10
|
+
base.before_create :timestamp_before_create
|
11
|
+
base.before_update :timestamp_before_update
|
12
|
+
end
|
13
|
+
|
14
|
+
def timestamp_before_create
|
15
|
+
write_attribute("created_at", Time.now) if record_timestamps && respond_to?(:created_at) && created_at.nil?
|
16
|
+
write_attribute("created_on", Time.now) if record_timestamps && respond_to?(:created_on) && created_on.nil?
|
17
|
+
timestamp_before_update
|
18
|
+
end
|
19
|
+
|
20
|
+
def timestamp_before_update
|
21
|
+
write_attribute("updated_at", Time.now) if record_timestamps && respond_to?(:updated_at)
|
22
|
+
write_attribute("updated_on", Time.now) if record_timestamps && respond_to?(:updated_on)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Base
|
27
|
+
# Records the creation date and possibly time in created_on (date only) or created_at (date and time) and the update date and possibly
|
28
|
+
# time in updated_on and updated_at. This only happens if the object responds to either of these messages, which they will do automatically
|
29
|
+
# if the table has columns of either of these names. This feature is turned on by default.
|
30
|
+
@@record_timestamps = true
|
31
|
+
cattr_accessor :record_timestamps
|
32
|
+
end
|
33
|
+
end
|
@@ -36,7 +36,11 @@ module ActiveRecord
|
|
36
36
|
# person.save # => true (and person is now saved in the database)
|
37
37
|
#
|
38
38
|
# An +Errors+ object is automatically created for every Active Record.
|
39
|
+
#
|
40
|
+
# Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations.
|
39
41
|
module Validations
|
42
|
+
VALIDATIONS = %w( validate validate_on_create validate_on_update )
|
43
|
+
|
40
44
|
def self.append_features(base) # :nodoc:
|
41
45
|
super
|
42
46
|
|
@@ -46,7 +50,224 @@ module ActiveRecord
|
|
46
50
|
|
47
51
|
alias_method :update_attribute_without_validation_skipping, :update_attribute
|
48
52
|
alias_method :update_attribute, :update_attribute_with_validation_skipping
|
53
|
+
|
54
|
+
VALIDATIONS.each { |vd| base.class_eval("def self.#{vd}(*methods) write_inheritable_array(\"#{vd}\", methods - (read_inheritable_attribute(\"#{vd}\") || [])) end") }
|
49
55
|
end
|
56
|
+
|
57
|
+
base.extend(ClassMethods)
|
58
|
+
end
|
59
|
+
|
60
|
+
# All of the following validations are defined in the class scope of the model that you're interested in validating.
|
61
|
+
# They offer a more declarative way of specifying when the model is valid and when it is not. It is recommended to use
|
62
|
+
# these over the low-level calls to validate and validate_on_create when possible.
|
63
|
+
module ClassMethods
|
64
|
+
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
|
65
|
+
#
|
66
|
+
# Model:
|
67
|
+
# class Person < ActiveRecord::Base
|
68
|
+
# validates_confirmation_of :user_name, :password
|
69
|
+
# validates_confirmation_of :email_address, :message => "should match confirmation"
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# View:
|
73
|
+
# <%= password_field "person", "password" %>
|
74
|
+
# <%= password_field "person", "password_confirmation" %>
|
75
|
+
#
|
76
|
+
# The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
|
77
|
+
# It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
|
78
|
+
#
|
79
|
+
# Configuration options:
|
80
|
+
# * <tt>message</tt> - A custom error message (default is: "doesn't match confirmation")
|
81
|
+
# * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
|
82
|
+
def validates_confirmation_of(*attr_names)
|
83
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
|
84
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
85
|
+
|
86
|
+
for attr_name in attr_names
|
87
|
+
attr_accessor "#{attr_name}_confirmation"
|
88
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add('#{attr_name}', "#{configuration[:message]}") unless #{attr_name} == #{attr_name}_confirmation}))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
|
93
|
+
#
|
94
|
+
# class Person < ActiveRecord::Base
|
95
|
+
# validates_acceptance_of :terms_of_service
|
96
|
+
# validates_acceptance_of :eula, :message => "must be abided"
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
|
100
|
+
#
|
101
|
+
# Configuration options:
|
102
|
+
# * <tt>message</tt> - A custom error message (default is: "must be accepted")
|
103
|
+
# * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
|
104
|
+
#
|
105
|
+
# NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
|
106
|
+
def validates_acceptance_of(*attr_names)
|
107
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save }
|
108
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
109
|
+
|
110
|
+
for attr_name in attr_names
|
111
|
+
attr_accessor(attr_name)
|
112
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add('#{attr_name}', '#{configuration[:message]}') unless #{attr_name} == "1"}))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Validates that the specified attributes are neither nil nor empty. Happens by default on both create and update.
|
117
|
+
#
|
118
|
+
# Configuration options:
|
119
|
+
# * <tt>message</tt> - A custom error message (default is: "has already been taken")
|
120
|
+
# * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
|
121
|
+
def validates_presence_of(*attr_names)
|
122
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:empty], :on => :save }
|
123
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
124
|
+
|
125
|
+
for attr_name in attr_names
|
126
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add_on_empty('#{attr_name}', "#{configuration[:message]}")}))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
|
131
|
+
#
|
132
|
+
# class Person < ActiveRecord::Base
|
133
|
+
# validates_length_of :first_name, :maximum=>30
|
134
|
+
# validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
|
135
|
+
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
|
136
|
+
# validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
|
137
|
+
# validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# Configuration options:
|
141
|
+
# * <tt>minimum</tt> - The minimum size of the attribute
|
142
|
+
# * <tt>maximum</tt> - The maximum size of the attribute
|
143
|
+
# * <tt>is</tt> - The exact size of the attribute
|
144
|
+
# * <tt>within</tt> - A range specifying the minimum and maximum size of the attribute
|
145
|
+
# * <tt>in</tt> - A synonym(or alias) for :within
|
146
|
+
#
|
147
|
+
# * <tt>too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (max is %d characters)")
|
148
|
+
# * <tt>too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)")
|
149
|
+
# * <tt>wrong_length</tt> - The error message if using the :is method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)")
|
150
|
+
# * <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
|
151
|
+
# * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
|
152
|
+
def validates_length_of(*attr_names)
|
153
|
+
configuration = { :too_long => ActiveRecord::Errors.default_error_messages[:too_long], :too_short => ActiveRecord::Errors.default_error_messages[:too_short], :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length], :on => :save }
|
154
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
155
|
+
|
156
|
+
# you must use one of 4 options, :within, :maximum, :minimum, or :is
|
157
|
+
within = configuration[:within] || configuration[:in]
|
158
|
+
maximum = configuration[:maximum]
|
159
|
+
minimum = configuration[:minimum]
|
160
|
+
is = configuration[:is]
|
161
|
+
|
162
|
+
raise(ArgumentError, "The :within, :maximum, :minimum, or :is options must be passed in the configuration hash") unless within or maximum or minimum or is
|
163
|
+
# but not more than 1 of them at a time
|
164
|
+
options_used = 0
|
165
|
+
options_used += 1 if within
|
166
|
+
options_used += 1 if maximum
|
167
|
+
options_used += 1 if minimum
|
168
|
+
options_used += 1 if is
|
169
|
+
raise(ArgumentError, "The :within, :maximum, :minimum, and :is options are mutually exclusive") if options_used > 1
|
170
|
+
|
171
|
+
option_to_use = within || maximum || minimum || is
|
172
|
+
for attr_name in attr_names
|
173
|
+
if within
|
174
|
+
raise(ArgumentError, "The :within option must be a Range") unless within.kind_of?(Range)
|
175
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add_on_boundary_breaking('#{attr_name}', #{within}, "#{configuration[:too_long]}", "#{configuration[:too_short]}")}))
|
176
|
+
elsif maximum
|
177
|
+
raise(ArgumentError, "The :maximum option must be a Fixnum") unless maximum.kind_of?(Fixnum)
|
178
|
+
msg = configuration[:message] || configuration[:too_long]
|
179
|
+
msg = (msg % maximum) rescue msg
|
180
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add( '#{attr_name}', '#{msg}') if #{attr_name}.to_s.length > #{maximum} }))
|
181
|
+
elsif minimum
|
182
|
+
raise(ArgumentError, "The :minimum option must be a Fixnum") unless minimum.kind_of?(Fixnum)
|
183
|
+
msg = configuration[:message] || configuration[:too_short]
|
184
|
+
msg = (msg % minimum) rescue msg
|
185
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add( '#{attr_name}', '#{msg}') if #{attr_name}.to_s.length < #{minimum} }))
|
186
|
+
else
|
187
|
+
raise(ArgumentError, "The :is option must be a Fixnum") unless is.kind_of?(Fixnum)
|
188
|
+
msg = configuration[:message] || configuration[:wrong_length]
|
189
|
+
msg = (msg % is) rescue msg
|
190
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add( '#{attr_name}', '#{msg}') if #{attr_name}.to_s.length != #{is} }))
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Validates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user
|
196
|
+
# can be named "davidhh".
|
197
|
+
#
|
198
|
+
# class Person < ActiveRecord::Base
|
199
|
+
# validates_uniqueness_of :user_name
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# When the record is created, a check is performed to make sure that no record exist in the database with the given value for the specified
|
203
|
+
# attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
|
204
|
+
#
|
205
|
+
# Configuration options:
|
206
|
+
# * <tt>message</tt> - Specifies a custom error message (default is: "has already been taken")
|
207
|
+
def validates_uniqueness_of(*attr_names)
|
208
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
|
209
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
210
|
+
|
211
|
+
for attr_name in attr_names
|
212
|
+
class_eval(%(validate %{errors.add("#{attr_name}", "#{configuration[:message]}") if self.class.find_first(new_record? ? ["#{attr_name} = ?", #{attr_name}] : ["#{attr_name} = ? AND id <> ?", #{attr_name}, id])}))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression
|
217
|
+
# provided.
|
218
|
+
#
|
219
|
+
# class Person < ActiveRecord::Base
|
220
|
+
# validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :on => :create
|
221
|
+
# end
|
222
|
+
#
|
223
|
+
# A regular expression must be provided or else an exception will be raised.
|
224
|
+
#
|
225
|
+
# Configuration options:
|
226
|
+
# * <tt>message</tt> - A custom error message (default is: "is invalid")
|
227
|
+
# * <tt>with</tt> - The regular expression used to validate the format with (note: must be supplied!)
|
228
|
+
# * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
|
229
|
+
def validates_format_of(*attr_names)
|
230
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
|
231
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
232
|
+
|
233
|
+
raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
|
234
|
+
|
235
|
+
for attr_name in attr_names
|
236
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name} and #{attr_name}.to_s.match(/#{configuration[:with]}/)}))
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Validates whether the value of the specified attribute is available in a particular enumerable object.
|
241
|
+
#
|
242
|
+
# class Person < ActiveRecord::Base
|
243
|
+
# validates_inclusion_of :gender, :in=>%w( m f ), :message=>"woah! what are you then!??!!"
|
244
|
+
# validates_inclusion_of :age, :in=>0..99
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# Configuration options:
|
248
|
+
# * <tt>in</tt> - An enumerable object of available items
|
249
|
+
# * <tt>message</tt> - Specifieds a customer error message (default is: "is not included in the list")
|
250
|
+
def validates_inclusion_of(*attr_names)
|
251
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
|
252
|
+
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
253
|
+
enum = configuration[:in] || configuration[:within]
|
254
|
+
|
255
|
+
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?")
|
256
|
+
|
257
|
+
for attr_name in attr_names
|
258
|
+
class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless (#{enum.inspect}).include?(#{attr_name}) }))
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
private
|
264
|
+
def validation_method(on)
|
265
|
+
case on
|
266
|
+
when :save then :validate
|
267
|
+
when :create then :validate_on_create
|
268
|
+
when :update then :validate_on_update
|
269
|
+
end
|
270
|
+
end
|
50
271
|
end
|
51
272
|
|
52
273
|
# The validation process on save can be skipped by passing false. The regular Base#save method is
|
@@ -66,8 +287,18 @@ module ActiveRecord
|
|
66
287
|
# Runs validate and validate_on_create or validate_on_update and returns true if no errors were added otherwise false.
|
67
288
|
def valid?
|
68
289
|
errors.clear
|
290
|
+
|
291
|
+
run_validations(:validate)
|
69
292
|
validate
|
70
|
-
|
293
|
+
|
294
|
+
if new_record?
|
295
|
+
run_validations(:validate_on_create)
|
296
|
+
validate_on_create
|
297
|
+
else
|
298
|
+
run_validations(:validate_on_update)
|
299
|
+
validate_on_update
|
300
|
+
end
|
301
|
+
|
71
302
|
errors.empty?
|
72
303
|
end
|
73
304
|
|
@@ -89,6 +320,37 @@ module ActiveRecord
|
|
89
320
|
# Overwrite this method for validation checks used only on updates.
|
90
321
|
def validate_on_update # :doc:
|
91
322
|
end
|
323
|
+
|
324
|
+
private
|
325
|
+
def run_validations(validation_method)
|
326
|
+
validations = self.class.read_inheritable_attribute(validation_method.to_s)
|
327
|
+
if validations.nil? then return end
|
328
|
+
validations.each do |validation|
|
329
|
+
if Symbol === validation
|
330
|
+
self.send(validation)
|
331
|
+
elsif String === validation
|
332
|
+
eval(validation, binding)
|
333
|
+
elsif validation_block?(validation)
|
334
|
+
validation.call(self)
|
335
|
+
elsif filter_class?(validation, validation_method)
|
336
|
+
validation.send(validation_method, self)
|
337
|
+
else
|
338
|
+
raise(
|
339
|
+
ActiveRecordError,
|
340
|
+
"Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
|
341
|
+
"class implementing a static validation method"
|
342
|
+
)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def validation_block?(validation)
|
348
|
+
validation.respond_to?("call") && (validation.arity == 1 || validation.arity == -1)
|
349
|
+
end
|
350
|
+
|
351
|
+
def validation_class?(validation, validation_method)
|
352
|
+
validation.respond_to?(validation_method)
|
353
|
+
end
|
92
354
|
end
|
93
355
|
|
94
356
|
# Active Record validation is reported to and from this object, which is used by Base#save to
|
@@ -98,6 +360,20 @@ module ActiveRecord
|
|
98
360
|
@base, @errors = base, {}
|
99
361
|
end
|
100
362
|
|
363
|
+
@@default_error_messages = {
|
364
|
+
:inclusion => "is not included in the list",
|
365
|
+
:invalid => "is invalid",
|
366
|
+
:confirmation => "doesn't match confirmation",
|
367
|
+
:accepted => "must be accepted",
|
368
|
+
:empty => "can't be empty",
|
369
|
+
:too_long => "is too long (max is %d characters)",
|
370
|
+
:too_short => "is too short (min is %d characters)",
|
371
|
+
:wrong_length => "is the wrong length (should be %d characters)",
|
372
|
+
:taken => "has already been taken",
|
373
|
+
}
|
374
|
+
cattr_accessor :default_error_messages
|
375
|
+
|
376
|
+
|
101
377
|
# Adds an error to the base object instead of any particular attribute. This is used
|
102
378
|
# to report errors that doesn't tie to any specific attribute, but rather to the object
|
103
379
|
# as a whole. These error messages doesn't get prepended with any field name when iterating
|
@@ -110,42 +386,42 @@ module ActiveRecord
|
|
110
386
|
# for the same attribute and ensure that this error object returns false when asked if +empty?+. More than one
|
111
387
|
# error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
|
112
388
|
# If no +msg+ is supplied, "invalid" is assumed.
|
113
|
-
def add(attribute, msg =
|
114
|
-
@errors[attribute] = [] if @errors[attribute].nil?
|
115
|
-
@errors[attribute] << msg
|
389
|
+
def add(attribute, msg = @@default_error_messages[:invalid])
|
390
|
+
@errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
|
391
|
+
@errors[attribute.to_s] << msg
|
116
392
|
end
|
117
393
|
|
118
394
|
# Will add an error message to each of the attributes in +attributes+ that is empty (defined by <tt>attribute_present?</tt>).
|
119
|
-
def add_on_empty(attributes, msg =
|
120
|
-
[attributes].flatten.each { |attr| add(attr, msg) unless @base.attribute_present?(attr) }
|
395
|
+
def add_on_empty(attributes, msg = @@default_error_messages[:empty])
|
396
|
+
[attributes].flatten.each { |attr| add(attr, msg) unless @base.attribute_present?(attr.to_s) }
|
121
397
|
end
|
122
398
|
|
123
399
|
# Will add an error message to each of the attributes in +attributes+ that has a length outside of the passed boundary +range+.
|
124
400
|
# If the length is above the boundary, the too_long_msg message will be used. If below, the too_short_msg.
|
125
|
-
def add_on_boundary_breaking(attributes, range, too_long_msg =
|
401
|
+
def add_on_boundary_breaking(attributes, range, too_long_msg = @@default_error_messages[:too_long], too_short_msg = @@default_error_messages[:too_short])
|
126
402
|
for attr in [attributes].flatten
|
127
|
-
add(attr, too_short_msg % range.begin) if @base.
|
128
|
-
add(attr, too_long_msg % range.end) if @base.
|
403
|
+
add(attr, too_short_msg % range.begin) if @base[attr.to_s] && @base.send(attr.to_s).length < range.begin
|
404
|
+
add(attr, too_long_msg % range.end) if @base[attr.to_s] && @base.send(attr.to_s).length > range.end
|
129
405
|
end
|
130
406
|
end
|
131
407
|
|
132
|
-
alias :
|
408
|
+
alias :add_on_boundary_breaking :add_on_boundary_breaking
|
133
409
|
|
134
410
|
# Returns true if the specified +attribute+ has errors associated with it.
|
135
411
|
def invalid?(attribute)
|
136
|
-
!@errors[attribute].nil?
|
412
|
+
!@errors[attribute.to_s].nil?
|
137
413
|
end
|
138
414
|
|
139
415
|
# * Returns nil, if no errors are associated with the specified +attribute+.
|
140
416
|
# * Returns the error message, if one error is associated with the specified +attribute+.
|
141
417
|
# * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
|
142
418
|
def on(attribute)
|
143
|
-
if @errors[attribute].nil?
|
419
|
+
if @errors[attribute.to_s].nil?
|
144
420
|
nil
|
145
|
-
elsif @errors[attribute].length == 1
|
146
|
-
@errors[attribute].first
|
421
|
+
elsif @errors[attribute.to_s].length == 1
|
422
|
+
@errors[attribute.to_s].first
|
147
423
|
else
|
148
|
-
@errors[attribute]
|
424
|
+
@errors[attribute.to_s]
|
149
425
|
end
|
150
426
|
end
|
151
427
|
|
@@ -173,7 +449,9 @@ module ActiveRecord
|
|
173
449
|
|
174
450
|
@errors.each_key do |attr|
|
175
451
|
@errors[attr].each do |msg|
|
176
|
-
if
|
452
|
+
next if msg.nil?
|
453
|
+
|
454
|
+
if attr == "base"
|
177
455
|
full_messages << msg
|
178
456
|
else
|
179
457
|
full_messages << @base.class.human_attribute_name(attr) + " " + msg
|