activemodel 3.0.0.rc → 3.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -1
- data/README.rdoc +34 -34
- data/lib/active_model/attribute_methods.rb +43 -43
- data/lib/active_model/callbacks.rb +31 -28
- data/lib/active_model/conversion.rb +14 -11
- data/lib/active_model/dirty.rb +22 -24
- data/lib/active_model/errors.rb +44 -48
- data/lib/active_model/lint.rb +2 -1
- data/lib/active_model/naming.rb +11 -8
- data/lib/active_model/observing.rb +7 -7
- data/lib/active_model/serialization.rb +23 -21
- data/lib/active_model/serializers/xml.rb +1 -1
- data/lib/active_model/translation.rb +8 -8
- data/lib/active_model/validations.rb +40 -29
- data/lib/active_model/validations/acceptance.rb +11 -11
- data/lib/active_model/validations/callbacks.rb +18 -4
- data/lib/active_model/validations/confirmation.rb +10 -10
- data/lib/active_model/validations/length.rb +10 -11
- data/lib/active_model/validations/validates.rb +8 -8
- data/lib/active_model/validator.rb +14 -14
- data/lib/active_model/version.rb +1 -1
- metadata +7 -7
@@ -3,25 +3,25 @@ require 'active_support/core_ext/hash/slice'
|
|
3
3
|
|
4
4
|
module ActiveModel
|
5
5
|
# == Active Model Serialization
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# Provides a basic serialization to a serializable_hash for your object.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# A minimal implementation could be:
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# class Person
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# include ActiveModel::Serialization
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# attr_accessor :name
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# def attributes
|
18
18
|
# @attributes ||= {'name' => 'nil'}
|
19
19
|
# end
|
20
|
-
#
|
20
|
+
#
|
21
21
|
# end
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# Which would provide you with:
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# person = Person.new
|
26
26
|
# person.serializable_hash # => {"name"=>nil}
|
27
27
|
# person.name = "Bob"
|
@@ -29,38 +29,40 @@ module ActiveModel
|
|
29
29
|
#
|
30
30
|
# You need to declare some sort of attributes hash which contains the attributes
|
31
31
|
# you want to serialize and their current value.
|
32
|
-
#
|
33
|
-
# Most of the time though, you will want to include the JSON or XML
|
34
|
-
# serializations. Both of these modules automatically include the
|
32
|
+
#
|
33
|
+
# Most of the time though, you will want to include the JSON or XML
|
34
|
+
# serializations. Both of these modules automatically include the
|
35
35
|
# ActiveModel::Serialization module, so there is no need to explicitly
|
36
36
|
# include it.
|
37
|
-
#
|
37
|
+
#
|
38
38
|
# So a minimal implementation including XML and JSON would be:
|
39
|
-
#
|
39
|
+
#
|
40
40
|
# class Person
|
41
|
-
#
|
41
|
+
#
|
42
42
|
# include ActiveModel::Serializers::JSON
|
43
43
|
# include ActiveModel::Serializers::Xml
|
44
|
-
#
|
44
|
+
#
|
45
45
|
# attr_accessor :name
|
46
|
-
#
|
46
|
+
#
|
47
47
|
# def attributes
|
48
48
|
# @attributes ||= {'name' => 'nil'}
|
49
49
|
# end
|
50
|
-
#
|
50
|
+
#
|
51
51
|
# end
|
52
|
-
#
|
52
|
+
#
|
53
53
|
# Which would provide you with:
|
54
|
-
#
|
54
|
+
#
|
55
55
|
# person = Person.new
|
56
56
|
# person.serializable_hash # => {"name"=>nil}
|
57
57
|
# person.to_json # => "{\"name\":null}"
|
58
58
|
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
59
|
-
#
|
59
|
+
#
|
60
60
|
# person.name = "Bob"
|
61
61
|
# person.serializable_hash # => {"name"=>"Bob"}
|
62
62
|
# person.to_json # => "{\"name\":\"Bob\"}"
|
63
63
|
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
64
|
+
#
|
65
|
+
# Valid options are <tt>:only</tt>, <tt>:except</tt> and <tt>:methods</tt> .
|
64
66
|
module Serialization
|
65
67
|
def serializable_hash(options = nil)
|
66
68
|
options ||= {}
|
@@ -52,7 +52,7 @@ module ActiveModel
|
|
52
52
|
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
|
53
53
|
end
|
54
54
|
|
55
|
-
# To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
|
55
|
+
# To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
|
56
56
|
# takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
|
57
57
|
# for a N level model but is set for the N+1 level models,
|
58
58
|
# then because <tt>:except</tt> is set to a default value, the second
|
@@ -3,19 +3,19 @@ require 'active_support/core_ext/hash/reverse_merge'
|
|
3
3
|
module ActiveModel
|
4
4
|
|
5
5
|
# == Active Model Translation
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# Provides integration between your object and the Rails internationalization
|
8
8
|
# (i18n) framework.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# A minimal implementation could be:
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# class TranslatedPerson
|
13
13
|
# extend ActiveModel::Translation
|
14
14
|
# end
|
15
|
-
#
|
15
|
+
#
|
16
16
|
# TranslatedPerson.human_attribute_name('my_attribute')
|
17
|
-
#
|
18
|
-
#
|
17
|
+
# # => "My attribute"
|
18
|
+
#
|
19
19
|
# This also provides the required class methods for hooking into the
|
20
20
|
# Rails internationalization API, including being able to define a
|
21
21
|
# class based i18n_scope and lookup_ancestors to find translations in
|
@@ -28,9 +28,9 @@ module ActiveModel
|
|
28
28
|
:activemodel
|
29
29
|
end
|
30
30
|
|
31
|
-
# When localizing a string, it goes through the lookup returned by this
|
31
|
+
# When localizing a string, it goes through the lookup returned by this
|
32
32
|
# method, which is used in ActiveModel::Name#human,
|
33
|
-
# ActiveModel::Errors#full_messages and
|
33
|
+
# ActiveModel::Errors#full_messages and
|
34
34
|
# ActiveModel::Translation#human_attribute_name.
|
35
35
|
def lookup_ancestors
|
36
36
|
self.ancestors.select { |x| x.respond_to?(:model_name) }
|
@@ -2,47 +2,44 @@ require 'active_support/core_ext/array/extract_options'
|
|
2
2
|
require 'active_support/core_ext/array/wrap'
|
3
3
|
require 'active_support/core_ext/class/attribute'
|
4
4
|
require 'active_support/core_ext/hash/keys'
|
5
|
+
require 'active_support/core_ext/hash/except'
|
5
6
|
require 'active_model/errors'
|
6
7
|
require 'active_model/validations/callbacks'
|
7
8
|
|
8
9
|
module ActiveModel
|
9
10
|
|
10
11
|
# == Active Model Validations
|
11
|
-
#
|
12
|
+
#
|
12
13
|
# Provides a full validation framework to your objects.
|
13
|
-
#
|
14
|
+
#
|
14
15
|
# A minimal implementation could be:
|
15
|
-
#
|
16
|
+
#
|
16
17
|
# class Person
|
17
18
|
# include ActiveModel::Validations
|
18
|
-
#
|
19
|
+
#
|
19
20
|
# attr_accessor :first_name, :last_name
|
20
21
|
#
|
21
22
|
# validates_each :first_name, :last_name do |record, attr, value|
|
22
23
|
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
|
23
24
|
# end
|
24
25
|
# end
|
25
|
-
#
|
26
|
+
#
|
26
27
|
# Which provides you with the full standard validation stack that you
|
27
|
-
# know from
|
28
|
-
#
|
28
|
+
# know from Active Record:
|
29
|
+
#
|
29
30
|
# person = Person.new
|
30
|
-
# person.valid?
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# #=> false
|
31
|
+
# person.valid? # => true
|
32
|
+
# person.invalid? # => false
|
33
|
+
#
|
34
34
|
# person.first_name = 'zoolander'
|
35
|
-
# person.valid?
|
36
|
-
#
|
37
|
-
# person.
|
38
|
-
#
|
39
|
-
# person.errors
|
40
|
-
# #=> #<OrderedHash {:first_name=>["starts with z."]}>
|
41
|
-
#
|
35
|
+
# person.valid? # => false
|
36
|
+
# person.invalid? # => true
|
37
|
+
# person.errors # => #<OrderedHash {:first_name=>["starts with z."]}>
|
38
|
+
#
|
42
39
|
# Note that ActiveModel::Validations automatically adds an +errors+ method
|
43
40
|
# to your instances initialized with a new ActiveModel::Errors object, so
|
44
41
|
# there is no need for you to do this manually.
|
45
|
-
#
|
42
|
+
#
|
46
43
|
module Validations
|
47
44
|
extend ActiveSupport::Concern
|
48
45
|
include ActiveSupport::Callbacks
|
@@ -65,7 +62,7 @@ module ActiveModel
|
|
65
62
|
#
|
66
63
|
# class Person
|
67
64
|
# include ActiveModel::Validations
|
68
|
-
#
|
65
|
+
#
|
69
66
|
# attr_accessor :first_name, :last_name
|
70
67
|
#
|
71
68
|
# validates_each :first_name, :last_name do |record, attr, value|
|
@@ -74,7 +71,7 @@ module ActiveModel
|
|
74
71
|
# end
|
75
72
|
#
|
76
73
|
# Options:
|
77
|
-
# * <tt>:on</tt> - Specifies when this validation is active (default is
|
74
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is
|
78
75
|
# <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
79
76
|
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
|
80
77
|
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
|
@@ -99,7 +96,7 @@ module ActiveModel
|
|
99
96
|
#
|
100
97
|
# class Comment
|
101
98
|
# include ActiveModel::Validations
|
102
|
-
#
|
99
|
+
#
|
103
100
|
# validate :must_be_friends
|
104
101
|
#
|
105
102
|
# def must_be_friends
|
@@ -122,15 +119,29 @@ module ActiveModel
|
|
122
119
|
# end
|
123
120
|
#
|
124
121
|
def validate(*args, &block)
|
125
|
-
options = args.
|
126
|
-
if options.
|
122
|
+
options = args.extract_options!
|
123
|
+
if options.key?(:on)
|
124
|
+
options = options.dup
|
127
125
|
options[:if] = Array.wrap(options[:if])
|
128
126
|
options[:if] << "validation_context == :#{options[:on]}"
|
129
127
|
end
|
128
|
+
args << options
|
130
129
|
set_callback(:validate, *args, &block)
|
131
130
|
end
|
132
131
|
|
133
|
-
|
132
|
+
[:create, :update].each do |type|
|
133
|
+
class_eval <<-RUBY
|
134
|
+
def validate_on_#{type}(*args, &block)
|
135
|
+
msg = "validate_on_#{type} is deprecated. Please use validate(args, :on => :#{type})"
|
136
|
+
ActiveSupport::Deprecation.warn(msg, caller)
|
137
|
+
options = args.extract_options!
|
138
|
+
options[:on] = :#{type}
|
139
|
+
validate(*args.push(options), &block)
|
140
|
+
end
|
141
|
+
RUBY
|
142
|
+
end
|
143
|
+
|
144
|
+
# List all validators that are being used to validate the model using
|
134
145
|
# +validates_with+ method.
|
135
146
|
def validators
|
136
147
|
_validators.values.flatten.uniq
|
@@ -170,14 +181,14 @@ module ActiveModel
|
|
170
181
|
self.validation_context = current_context
|
171
182
|
end
|
172
183
|
|
173
|
-
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added,
|
184
|
+
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added,
|
174
185
|
# false otherwise.
|
175
186
|
def invalid?(context = nil)
|
176
187
|
!valid?(context)
|
177
188
|
end
|
178
189
|
|
179
|
-
# Hook method defining how an attribute value should be retrieved. By default
|
180
|
-
# this is assumed to be an instance named after the attribute. Override this
|
190
|
+
# Hook method defining how an attribute value should be retrieved. By default
|
191
|
+
# this is assumed to be an instance named after the attribute. Override this
|
181
192
|
# method in subclasses should you need to retrieve the value for a given
|
182
193
|
# attribute differently:
|
183
194
|
#
|
@@ -196,7 +207,7 @@ module ActiveModel
|
|
196
207
|
alias :read_attribute_for_validation :send
|
197
208
|
|
198
209
|
protected
|
199
|
-
|
210
|
+
|
200
211
|
def run_validations!
|
201
212
|
_run_validate_callbacks
|
202
213
|
errors.empty?
|
@@ -24,7 +24,7 @@ module ActiveModel
|
|
24
24
|
end
|
25
25
|
|
26
26
|
module HelperMethods
|
27
|
-
# Encapsulates the pattern of wanting to validate the acceptance of a
|
27
|
+
# Encapsulates the pattern of wanting to validate the acceptance of a
|
28
28
|
# terms of service check box (or similar agreement). Example:
|
29
29
|
#
|
30
30
|
# class Person < ActiveRecord::Base
|
@@ -32,33 +32,33 @@ module ActiveModel
|
|
32
32
|
# validates_acceptance_of :eula, :message => "must be abided"
|
33
33
|
# end
|
34
34
|
#
|
35
|
-
# If the database column does not exist, the +terms_of_service+ attribute
|
35
|
+
# If the database column does not exist, the +terms_of_service+ attribute
|
36
36
|
# is entirely virtual. This check is performed only if +terms_of_service+
|
37
37
|
# is not +nil+ and by default on save.
|
38
38
|
#
|
39
39
|
# Configuration options:
|
40
|
-
# * <tt>:message</tt> - A custom error message (default is: "must be
|
40
|
+
# * <tt>:message</tt> - A custom error message (default is: "must be
|
41
41
|
# accepted").
|
42
42
|
# * <tt>:on</tt> - Specifies when this validation is active (default is
|
43
|
-
# <tt>:save</tt>, other options are <tt>:create</tt> and
|
43
|
+
# <tt>:save</tt>, other options are <tt>:create</tt> and
|
44
44
|
# <tt>:update</tt>).
|
45
45
|
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
|
46
46
|
# is true).
|
47
|
-
# * <tt>:accept</tt> - Specifies value that is considered accepted.
|
47
|
+
# * <tt>:accept</tt> - Specifies value that is considered accepted.
|
48
48
|
# The default value is a string "1", which makes it easy to relate to
|
49
|
-
# an HTML checkbox. This should be set to +true+ if you are validating
|
49
|
+
# an HTML checkbox. This should be set to +true+ if you are validating
|
50
50
|
# a database column, since the attribute is typecast from "1" to +true+
|
51
51
|
# before validation.
|
52
52
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
53
53
|
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
|
54
54
|
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
55
|
-
# method, proc or string should return or evaluate to a true or false
|
55
|
+
# method, proc or string should return or evaluate to a true or false
|
56
56
|
# value.
|
57
|
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
|
58
|
-
# determine if the validation should not occur (for example,
|
59
|
-
# <tt>:unless => :skip_validation</tt>, or
|
57
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
|
58
|
+
# determine if the validation should not occur (for example,
|
59
|
+
# <tt>:unless => :skip_validation</tt>, or
|
60
60
|
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
|
61
|
-
# The method, proc or string should return or evaluate to a true or
|
61
|
+
# The method, proc or string should return or evaluate to a true or
|
62
62
|
# false value.
|
63
63
|
def validates_acceptance_of(*attr_names)
|
64
64
|
validates_with AcceptanceValidator, _merge_attributes(attr_names)
|
@@ -14,7 +14,7 @@ module ActiveModel
|
|
14
14
|
# include ActiveModel::Validations::Callbacks
|
15
15
|
#
|
16
16
|
# before_validation :do_stuff_before_validation
|
17
|
-
# after_validation :
|
17
|
+
# after_validation :do_stuff_after_validation
|
18
18
|
# end
|
19
19
|
#
|
20
20
|
# Like other before_* callbacks if <tt>before_validation</tt> returns false
|
@@ -28,22 +28,36 @@ module ActiveModel
|
|
28
28
|
|
29
29
|
module ClassMethods
|
30
30
|
def before_validation(*args, &block)
|
31
|
-
options = args.
|
31
|
+
options = args.extract_options!
|
32
32
|
if options.is_a?(Hash) && options[:on]
|
33
33
|
options[:if] = Array.wrap(options[:if])
|
34
34
|
options[:if] << "self.validation_context == :#{options[:on]}"
|
35
35
|
end
|
36
|
-
set_callback(:validation, :before, *args, &block)
|
36
|
+
set_callback(:validation, :before, *(args << options), &block)
|
37
37
|
end
|
38
38
|
|
39
39
|
def after_validation(*args, &block)
|
40
40
|
options = args.extract_options!
|
41
41
|
options[:prepend] = true
|
42
42
|
options[:if] = Array.wrap(options[:if])
|
43
|
-
options[:if] << "!halted
|
43
|
+
options[:if] << "!halted"
|
44
44
|
options[:if] << "self.validation_context == :#{options[:on]}" if options[:on]
|
45
45
|
set_callback(:validation, :after, *(args << options), &block)
|
46
46
|
end
|
47
|
+
|
48
|
+
[:before, :after].each do |type|
|
49
|
+
[:create, :update].each do |on|
|
50
|
+
class_eval <<-RUBY
|
51
|
+
def #{type}_validation_on_#{on}(*args, &block)
|
52
|
+
msg = "#{type}_validation_on_#{on} is deprecated. Please use #{type}_validation(arguments, :on => :#{on}"
|
53
|
+
ActiveSupport::Deprecation.warn(msg, caller)
|
54
|
+
options = args.extract_options!
|
55
|
+
options[:on] = :#{on}
|
56
|
+
before_validation(*args.push(options), &block)
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
end
|
60
|
+
end
|
47
61
|
end
|
48
62
|
|
49
63
|
protected
|
@@ -15,13 +15,13 @@ module ActiveModel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
module HelperMethods
|
18
|
-
# Encapsulates the pattern of wanting to validate a password or email
|
18
|
+
# Encapsulates the pattern of wanting to validate a password or email
|
19
19
|
# address field with a confirmation. For example:
|
20
20
|
#
|
21
21
|
# Model:
|
22
22
|
# class Person < ActiveRecord::Base
|
23
23
|
# validates_confirmation_of :user_name, :password
|
24
|
-
# validates_confirmation_of :email_address,
|
24
|
+
# validates_confirmation_of :email_address,
|
25
25
|
# :message => "should match confirmation"
|
26
26
|
# end
|
27
27
|
#
|
@@ -29,12 +29,12 @@ module ActiveModel
|
|
29
29
|
# <%= password_field "person", "password" %>
|
30
30
|
# <%= password_field "person", "password_confirmation" %>
|
31
31
|
#
|
32
|
-
# The added +password_confirmation+ attribute is virtual; it exists only
|
32
|
+
# The added +password_confirmation+ attribute is virtual; it exists only
|
33
33
|
# as an in-memory attribute for validating the password. To achieve this,
|
34
|
-
# the validation adds accessors to the model for the confirmation
|
34
|
+
# the validation adds accessors to the model for the confirmation
|
35
35
|
# attribute.
|
36
|
-
#
|
37
|
-
# NOTE: This check is performed only if +password_confirmation+ is not
|
36
|
+
#
|
37
|
+
# NOTE: This check is performed only if +password_confirmation+ is not
|
38
38
|
# +nil+, and by default only on save. To require confirmation, make sure
|
39
39
|
# to add a presence check for the confirmation attribute:
|
40
40
|
#
|
@@ -48,11 +48,11 @@ module ActiveModel
|
|
48
48
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
49
49
|
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
|
50
50
|
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
51
|
-
# method, proc or string should return or evaluate to a true or false
|
51
|
+
# method, proc or string should return or evaluate to a true or false
|
52
52
|
# value.
|
53
|
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
|
54
|
-
# determine if the validation should not occur (e.g.
|
55
|
-
# <tt>:unless => :skip_validation</tt>, or
|
53
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
|
54
|
+
# determine if the validation should not occur (e.g.
|
55
|
+
# <tt>:unless => :skip_validation</tt>, or
|
56
56
|
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
57
57
|
# method, proc or string should return or evaluate to a true or false value.
|
58
58
|
def validates_confirmation_of(*attr_names)
|
@@ -34,25 +34,24 @@ module ActiveModel
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def validate_each(record, attribute, value)
|
39
39
|
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
40
40
|
|
41
41
|
CHECKS.each do |key, validity_check|
|
42
42
|
next unless check_value = options[key]
|
43
|
-
default_message = options[MESSAGES[key]]
|
44
|
-
options[:message] ||= default_message if default_message
|
45
43
|
|
46
|
-
|
47
|
-
value.nil? || value.size.send(validity_check, check_value)
|
48
|
-
else
|
49
|
-
value && value.size.send(validity_check, check_value)
|
50
|
-
end
|
44
|
+
value ||= [] if key == :maximum
|
51
45
|
|
52
|
-
next if
|
46
|
+
next if value && value.size.send(validity_check, check_value)
|
47
|
+
|
48
|
+
errors_options = options.except(*RESERVED_OPTIONS)
|
49
|
+
errors_options[:count] = check_value
|
50
|
+
|
51
|
+
default_message = options[MESSAGES[key]]
|
52
|
+
errors_options[:message] ||= default_message if default_message
|
53
53
|
|
54
|
-
record.errors.add(attribute, MESSAGES[key],
|
55
|
-
options.except(*RESERVED_OPTIONS).merge!(:count => check_value))
|
54
|
+
record.errors.add(attribute, MESSAGES[key], errors_options)
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|