activerecord 2.3.3 → 2.3.4
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 +8 -1
- data/Rakefile +32 -15
- data/examples/performance.rb +162 -0
- data/lib/active_record/associations.rb +37 -5
- data/lib/active_record/associations/association_collection.rb +1 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +16 -0
- data/lib/active_record/associations/has_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_through_association.rb +13 -3
- data/lib/active_record/associations/has_one_through_association.rb +8 -2
- data/lib/active_record/autosave_association.rb +4 -3
- data/lib/active_record/base.rb +18 -10
- data/lib/active_record/calculations.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +7 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +17 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -13
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
- data/lib/active_record/dirty.rb +1 -1
- data/lib/active_record/fixtures.rb +9 -7
- data/lib/active_record/i18n_interpolation_deprecation.rb +1 -1
- data/lib/active_record/locale/en.yml +4 -0
- data/lib/active_record/named_scope.rb +1 -6
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/schema_dumper.rb +1 -2
- data/lib/active_record/serializers/json_serializer.rb +5 -3
- data/lib/active_record/serializers/xml_serializer.rb +6 -2
- data/lib/active_record/validations.rb +148 -79
- data/lib/active_record/version.rb +1 -1
- data/test/cases/adapter_test.rb +12 -0
- data/test/cases/associations/belongs_to_associations_test.rb +0 -18
- data/test/cases/associations/eager_load_nested_include_test.rb +5 -5
- data/test/cases/associations/habtm_join_table_test.rb +56 -0
- data/test/cases/associations/has_many_associations_test.rb +56 -2
- data/test/cases/associations/has_many_through_associations_test.rb +46 -1
- data/test/cases/associations/has_one_through_associations_test.rb +10 -0
- data/test/cases/associations/join_model_test.rb +4 -4
- data/test/cases/base_test.rb +49 -4
- data/test/cases/calculations_test.rb +6 -0
- data/test/cases/column_definition_test.rb +34 -0
- data/test/cases/dirty_test.rb +10 -0
- data/test/cases/finder_test.rb +15 -50
- data/test/cases/fixtures_test.rb +1 -1
- data/test/cases/i18n_test.rb +5 -0
- data/test/cases/method_scoping_test.rb +1 -1
- data/test/cases/migration_test.rb +39 -11
- data/test/cases/modules_test.rb +42 -0
- data/test/cases/named_scope_test.rb +6 -4
- data/test/cases/pk_test.rb +18 -0
- data/test/cases/reflection_test.rb +2 -2
- data/test/cases/schema_dumper_test.rb +19 -1
- data/test/cases/validations_i18n_test.rb +656 -624
- data/test/cases/validations_test.rb +12 -2
- data/test/cases/xml_serialization_test.rb +20 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/posts.yml +3 -0
- data/test/models/author.rb +1 -0
- data/test/models/comment.rb +5 -1
- data/test/models/company.rb +2 -0
- data/test/models/company_in_module.rb +1 -1
- data/test/models/contract.rb +5 -0
- data/test/models/organization.rb +2 -0
- data/test/models/topic.rb +0 -2
- data/test/schema/postgresql_specific_schema.rb +13 -2
- data/test/schema/schema.rb +4 -0
- metadata +12 -54
- data/test/debug.log +0 -415
@@ -10,7 +10,7 @@ module I18n
|
|
10
10
|
|
11
11
|
protected
|
12
12
|
def interpolate_with_deprecated_syntax(locale, string, values = {})
|
13
|
-
return string unless string.is_a?(String)
|
13
|
+
return string unless string.is_a?(String) && !values.empty?
|
14
14
|
|
15
15
|
string = string.gsub(/%d|%s/) do |s|
|
16
16
|
instead = DEPRECATED_INTERPOLATORS[s]
|
@@ -23,8 +23,12 @@ en:
|
|
23
23
|
less_than_or_equal_to: "must be less than or equal to {{count}}"
|
24
24
|
odd: "must be odd"
|
25
25
|
even: "must be even"
|
26
|
+
record_invalid: "Validation failed: {{errors}}"
|
26
27
|
# Append your own errors here or at the model/attributes scope.
|
27
28
|
|
29
|
+
full_messages:
|
30
|
+
format: "{{attribute}} {{message}}"
|
31
|
+
|
28
32
|
# You can define own errors for models or model attributes.
|
29
33
|
# The values :model, :attribute and :value are always available for interpolation.
|
30
34
|
#
|
@@ -89,12 +89,7 @@ module ActiveRecord
|
|
89
89
|
when Hash
|
90
90
|
options
|
91
91
|
when Proc
|
92
|
-
|
93
|
-
when Scope
|
94
|
-
with_scope(:find => parent_scope.proxy_options) { options.call(*args) }
|
95
|
-
else
|
96
|
-
options.call(*args)
|
97
|
-
end
|
92
|
+
options.call(*args)
|
98
93
|
end, &block)
|
99
94
|
end
|
100
95
|
(class << self; self end).instance_eval do
|
@@ -297,7 +297,7 @@ module ActiveRecord
|
|
297
297
|
raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
|
298
298
|
end
|
299
299
|
|
300
|
-
unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
|
300
|
+
unless [:belongs_to, :has_many, :has_one].include?(source_reflection.macro) && source_reflection.options[:through].nil?
|
301
301
|
raise HasManyThroughSourceAssociationMacroError.new(self)
|
302
302
|
end
|
303
303
|
end
|
@@ -84,7 +84,6 @@ HEADER
|
|
84
84
|
elsif @connection.respond_to?(:primary_key)
|
85
85
|
pk = @connection.primary_key(table)
|
86
86
|
end
|
87
|
-
pk ||= 'id'
|
88
87
|
|
89
88
|
tbl.print " create_table #{table.inspect}"
|
90
89
|
if columns.detect { |c| c.name == pk }
|
@@ -180,4 +179,4 @@ HEADER
|
|
180
179
|
end
|
181
180
|
end
|
182
181
|
end
|
183
|
-
end
|
182
|
+
end
|
@@ -74,13 +74,15 @@ module ActiveRecord #:nodoc:
|
|
74
74
|
# {"comments": [{"body": "Don't think too hard"}],
|
75
75
|
# "title": "So I was thinking"}]}
|
76
76
|
def to_json(options = {})
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def as_json(options = nil) #:nodoc:
|
77
81
|
hash = Serializer.new(self, options).serializable_record
|
78
82
|
hash = { self.class.model_name.element => hash } if include_root_in_json
|
79
|
-
|
83
|
+
hash
|
80
84
|
end
|
81
85
|
|
82
|
-
def as_json(options = nil) self end #:nodoc:
|
83
|
-
|
84
86
|
def from_json(json)
|
85
87
|
self.attributes = ActiveSupport::JSON.decode(json)
|
86
88
|
self
|
@@ -178,7 +178,7 @@ module ActiveRecord #:nodoc:
|
|
178
178
|
end
|
179
179
|
|
180
180
|
def root
|
181
|
-
root = (options[:root] || @record.class.
|
181
|
+
root = (options[:root] || @record.class.model_name.singular).to_s
|
182
182
|
reformat_name(root)
|
183
183
|
end
|
184
184
|
|
@@ -320,7 +320,11 @@ module ActiveRecord #:nodoc:
|
|
320
320
|
|
321
321
|
protected
|
322
322
|
def compute_type
|
323
|
-
type = @record.class.serialized_attributes.has_key?(name)
|
323
|
+
type = if @record.class.serialized_attributes.has_key?(name)
|
324
|
+
:yaml
|
325
|
+
else
|
326
|
+
@record.class.columns_hash[name].try(:type)
|
327
|
+
end
|
324
328
|
|
325
329
|
case type
|
326
330
|
when :text
|
@@ -10,15 +10,122 @@ module ActiveRecord
|
|
10
10
|
attr_reader :record
|
11
11
|
def initialize(record)
|
12
12
|
@record = record
|
13
|
-
|
13
|
+
errors = @record.errors.full_messages.join(I18n.t('support.array.words_connector', :default => ', '))
|
14
|
+
super(I18n.t('activerecord.errors.messages.record_invalid', :errors => errors))
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
class Error
|
19
|
+
attr_accessor :base, :attribute, :type, :message, :options
|
20
|
+
|
21
|
+
def initialize(base, attribute, type = nil, options = {})
|
22
|
+
self.base = base
|
23
|
+
self.attribute = attribute
|
24
|
+
self.type = type || :invalid
|
25
|
+
self.options = options
|
26
|
+
self.message = options.delete(:message) || self.type
|
27
|
+
end
|
28
|
+
|
29
|
+
def message
|
30
|
+
generate_message(@message, options.dup)
|
31
|
+
end
|
32
|
+
|
33
|
+
def full_message
|
34
|
+
attribute.to_s == 'base' ? message : generate_full_message(message, options.dup)
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :to_s :message
|
38
|
+
|
39
|
+
def value
|
40
|
+
@base.respond_to?(attribute) ? @base.send(attribute) : nil
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
|
46
|
+
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
|
47
|
+
# it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
|
48
|
+
# default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
|
49
|
+
# translated attribute name and the value are available for interpolation.
|
50
|
+
#
|
51
|
+
# When using inheritence in your models, it will check all the inherited models too, but only if the model itself
|
52
|
+
# hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
|
53
|
+
# error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
|
54
|
+
#
|
55
|
+
# <ol>
|
56
|
+
# <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
|
57
|
+
# <li><tt>activerecord.errors.models.admin.blank</tt></li>
|
58
|
+
# <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
|
59
|
+
# <li><tt>activerecord.errors.models.user.blank</tt></li>
|
60
|
+
# <li><tt>activerecord.errors.messages.blank</tt></li>
|
61
|
+
# <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
|
62
|
+
# </ol>
|
63
|
+
def generate_message(message, options = {})
|
64
|
+
keys = @base.class.self_and_descendants_from_active_record.map do |klass|
|
65
|
+
[ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
|
66
|
+
:"models.#{klass.name.underscore}.#{message}" ]
|
67
|
+
end.flatten
|
68
|
+
|
69
|
+
keys << options.delete(:default)
|
70
|
+
keys << :"messages.#{message}"
|
71
|
+
keys << message if message.is_a?(String)
|
72
|
+
keys << @type unless @type == message
|
73
|
+
keys.compact!
|
74
|
+
|
75
|
+
options.reverse_merge! :default => keys,
|
76
|
+
:scope => [:activerecord, :errors],
|
77
|
+
:model => @base.class.human_name,
|
78
|
+
:attribute => @base.class.human_attribute_name(attribute.to_s),
|
79
|
+
:value => value
|
80
|
+
|
81
|
+
I18n.translate(keys.shift, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Wraps an error message into a full_message format.
|
85
|
+
#
|
86
|
+
# The default full_message format for any locale is <tt>"{{attribute}} {{message}}"</tt>.
|
87
|
+
# One can specify locale specific default full_message format by storing it as a
|
88
|
+
# translation for the key <tt>:"activerecord.errors.full_messages.format"</tt>.
|
89
|
+
#
|
90
|
+
# Additionally one can specify a validation specific error message format by
|
91
|
+
# storing a translation for <tt>:"activerecord.errors.full_messages.[message_key]"</tt>.
|
92
|
+
# E.g. the full_message format for any validation that uses :blank as a message
|
93
|
+
# key (such as validates_presence_of) can be stored to <tt>:"activerecord.errors.full_messages.blank".</tt>
|
94
|
+
#
|
95
|
+
# Because the message key used by a validation can be overwritten on the
|
96
|
+
# <tt>validates_*</tt> class macro level one can customize the full_message format for
|
97
|
+
# any particular validation:
|
98
|
+
#
|
99
|
+
# # app/models/article.rb
|
100
|
+
# class Article < ActiveRecord::Base
|
101
|
+
# validates_presence_of :title, :message => :"title.blank"
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# # config/locales/en.yml
|
105
|
+
# en:
|
106
|
+
# activerecord:
|
107
|
+
# errors:
|
108
|
+
# full_messages:
|
109
|
+
# title:
|
110
|
+
# blank: This title is screwed!
|
111
|
+
def generate_full_message(message, options = {})
|
112
|
+
options.reverse_merge! :message => self.message,
|
113
|
+
:model => @base.class.human_name,
|
114
|
+
:attribute => @base.class.human_attribute_name(attribute.to_s),
|
115
|
+
:value => value
|
116
|
+
|
117
|
+
key = :"full_messages.#{@message}"
|
118
|
+
defaults = [:'full_messages.format', '{{attribute}} {{message}}']
|
119
|
+
|
120
|
+
I18n.t(key, options.merge(:default => defaults, :scope => [:activerecord, :errors]))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
17
124
|
# Active Record validation is reported to and from this object, which is used by Base#save to
|
18
125
|
# determine whether the object is in a valid state to be saved. See usage example in Validations.
|
19
126
|
class Errors
|
20
127
|
include Enumerable
|
21
|
-
|
128
|
+
|
22
129
|
class << self
|
23
130
|
def default_error_messages
|
24
131
|
ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages').")
|
@@ -43,11 +150,19 @@ module ActiveRecord
|
|
43
150
|
# error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
|
44
151
|
# If no +messsage+ is supplied, :invalid is assumed.
|
45
152
|
# If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
|
46
|
-
def add(attribute, message = nil, options = {})
|
47
|
-
|
48
|
-
|
153
|
+
# def add(attribute, message = nil, options = {})
|
154
|
+
# message ||= :invalid
|
155
|
+
# message = generate_message(attribute, message, options)) if message.is_a?(Symbol)
|
156
|
+
# @errors[attribute.to_s] ||= []
|
157
|
+
# @errors[attribute.to_s] << message
|
158
|
+
# end
|
159
|
+
|
160
|
+
def add(error_or_attr, message = nil, options = {})
|
161
|
+
error, attribute = error_or_attr.is_a?(Error) ? [error_or_attr, error_or_attr.attribute] : [nil, error_or_attr]
|
162
|
+
options[:message] = options.delete(:default) if options.has_key?(:default)
|
163
|
+
|
49
164
|
@errors[attribute.to_s] ||= []
|
50
|
-
@errors[attribute.to_s] << message
|
165
|
+
@errors[attribute.to_s] << (error || Error.new(@base, attribute, message, options))
|
51
166
|
end
|
52
167
|
|
53
168
|
# Will add an error message to each of the attributes in +attributes+ that is empty.
|
@@ -66,49 +181,6 @@ module ActiveRecord
|
|
66
181
|
add(attr, :blank, :default => custom_message) if value.blank?
|
67
182
|
end
|
68
183
|
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_descendants_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
|
112
184
|
|
113
185
|
# Returns true if the specified +attribute+ has errors associated with it.
|
114
186
|
#
|
@@ -138,8 +210,9 @@ module ActiveRecord
|
|
138
210
|
# company.errors.on(:email) # => "can't be blank"
|
139
211
|
# company.errors.on(:address) # => nil
|
140
212
|
def on(attribute)
|
141
|
-
|
142
|
-
return nil
|
213
|
+
attribute = attribute.to_s
|
214
|
+
return nil unless @errors.has_key?(attribute)
|
215
|
+
errors = @errors[attribute].map(&:to_s)
|
143
216
|
errors.size == 1 ? errors.first : errors
|
144
217
|
end
|
145
218
|
|
@@ -163,7 +236,11 @@ module ActiveRecord
|
|
163
236
|
# # name - can't be blank
|
164
237
|
# # address - can't be blank
|
165
238
|
def each
|
166
|
-
@errors.each_key { |attr| @errors[attr].each { |
|
239
|
+
@errors.each_key { |attr| @errors[attr].each { |error| yield attr, error.message } }
|
240
|
+
end
|
241
|
+
|
242
|
+
def each_error
|
243
|
+
@errors.each_key { |attr| @errors[attr].each { |error| yield attr, error } }
|
167
244
|
end
|
168
245
|
|
169
246
|
# Yields each full error message added. So <tt>Person.errors.add("first_name", "can't be empty")</tt> will be returned
|
@@ -194,22 +271,10 @@ module ActiveRecord
|
|
194
271
|
# company.errors.full_messages # =>
|
195
272
|
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
|
196
273
|
def full_messages(options = {})
|
197
|
-
full_messages
|
198
|
-
|
199
|
-
@errors.each_key do |attr|
|
200
|
-
@errors[attr].each do |message|
|
201
|
-
next unless message
|
202
|
-
|
203
|
-
if attr == "base"
|
204
|
-
full_messages << message
|
205
|
-
else
|
206
|
-
attr_name = @base.class.human_attribute_name(attr)
|
207
|
-
full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message
|
208
|
-
end
|
209
|
-
end
|
274
|
+
@errors.values.inject([]) do |full_messages, errors|
|
275
|
+
full_messages + errors.map { |error| error.full_message }
|
210
276
|
end
|
211
|
-
|
212
|
-
end
|
277
|
+
end
|
213
278
|
|
214
279
|
# Returns true if no errors have been added.
|
215
280
|
def empty?
|
@@ -254,7 +319,11 @@ module ActiveRecord
|
|
254
319
|
full_messages.each { |msg| e.error(msg) }
|
255
320
|
end
|
256
321
|
end
|
257
|
-
|
322
|
+
|
323
|
+
def generate_message(attribute, message = :invalid, options = {})
|
324
|
+
ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error#generate_message.")
|
325
|
+
Error.new(@base, attribute, message, options).to_s
|
326
|
+
end
|
258
327
|
end
|
259
328
|
|
260
329
|
|
@@ -437,7 +506,7 @@ module ActiveRecord
|
|
437
506
|
|
438
507
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
439
508
|
unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
|
440
|
-
record.errors.add(attr_name, :confirmation, :default => configuration[:message])
|
509
|
+
record.errors.add(attr_name, :confirmation, :default => configuration[:message])
|
441
510
|
end
|
442
511
|
end
|
443
512
|
end
|
@@ -479,7 +548,7 @@ module ActiveRecord
|
|
479
548
|
|
480
549
|
validates_each(attr_names,configuration) do |record, attr_name, value|
|
481
550
|
unless value == configuration[:accept]
|
482
|
-
record.errors.add(attr_name, :accepted, :default => configuration[:message])
|
551
|
+
record.errors.add(attr_name, :accepted, :default => configuration[:message])
|
483
552
|
end
|
484
553
|
end
|
485
554
|
end
|
@@ -499,7 +568,7 @@ module ActiveRecord
|
|
499
568
|
#
|
500
569
|
# Configuration options:
|
501
570
|
# * <tt>message</tt> - A custom error message (default is: "can't be blank").
|
502
|
-
# * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
|
571
|
+
# * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
|
503
572
|
# <tt>:update</tt>).
|
504
573
|
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
505
574
|
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
|
@@ -599,7 +668,7 @@ module ActiveRecord
|
|
599
668
|
validates_each(attrs, options) do |record, attr, value|
|
600
669
|
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
601
670
|
unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
602
|
-
record.errors.add(attr, key, :default => custom_message, :count => option_value)
|
671
|
+
record.errors.add(attr, key, :default => custom_message, :count => option_value)
|
603
672
|
end
|
604
673
|
end
|
605
674
|
end
|
@@ -687,7 +756,7 @@ module ActiveRecord
|
|
687
756
|
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
|
688
757
|
# rare case that a race condition occurs, the database will guarantee
|
689
758
|
# the field's uniqueness.
|
690
|
-
#
|
759
|
+
#
|
691
760
|
# When the database catches such a duplicate insertion,
|
692
761
|
# ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
|
693
762
|
# exception. You can either choose to let this error propagate (which
|
@@ -696,7 +765,7 @@ module ActiveRecord
|
|
696
765
|
# that the title already exists, and asking him to re-enter the title).
|
697
766
|
# This technique is also known as optimistic concurrency control:
|
698
767
|
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control
|
699
|
-
#
|
768
|
+
#
|
700
769
|
# Active Record currently provides no way to distinguish unique
|
701
770
|
# index constraint errors from other types of database errors, so you
|
702
771
|
# will have to parse the (database-specific) exception message to detect
|
@@ -726,7 +795,7 @@ module ActiveRecord
|
|
726
795
|
comparison_operator = "IS ?"
|
727
796
|
elsif column.text?
|
728
797
|
comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
|
729
|
-
value = column.limit ? value.to_s[0, column.limit] : value.to_s
|
798
|
+
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s
|
730
799
|
else
|
731
800
|
comparison_operator = "= ?"
|
732
801
|
end
|
@@ -794,7 +863,7 @@ module ActiveRecord
|
|
794
863
|
|
795
864
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
796
865
|
unless value.to_s =~ configuration[:with]
|
797
|
-
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
|
866
|
+
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
|
798
867
|
end
|
799
868
|
end
|
800
869
|
end
|
@@ -828,7 +897,7 @@ module ActiveRecord
|
|
828
897
|
|
829
898
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
830
899
|
unless enum.include?(value)
|
831
|
-
record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
|
900
|
+
record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
|
832
901
|
end
|
833
902
|
end
|
834
903
|
end
|
@@ -862,7 +931,7 @@ module ActiveRecord
|
|
862
931
|
|
863
932
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
864
933
|
if enum.include?(value)
|
865
|
-
record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
|
934
|
+
record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
|
866
935
|
end
|
867
936
|
end
|
868
937
|
end
|
@@ -970,7 +1039,7 @@ module ActiveRecord
|
|
970
1039
|
case option
|
971
1040
|
when :odd, :even
|
972
1041
|
unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
|
973
|
-
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
|
1042
|
+
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
|
974
1043
|
end
|
975
1044
|
else
|
976
1045
|
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]]
|