activemodel 3.0.0.rc → 3.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|