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.
@@ -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
- # #=> "My attribute"
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 ActiveRecord.
28
- #
28
+ # know from Active Record:
29
+ #
29
30
  # person = Person.new
30
- # person.valid?
31
- # #=> true
32
- # person.invalid?
33
- # #=> false
31
+ # person.valid? # => true
32
+ # person.invalid? # => false
33
+ #
34
34
  # person.first_name = 'zoolander'
35
- # person.valid?
36
- # #=> false
37
- # person.invalid?
38
- # #=> true
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.last
126
- if options.is_a?(Hash) && options.key?(:on)
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
- # List all validators that are being used to validate the model using
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 :do_tuff_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.last
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 && value != false"
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
- valid_value = if key == :maximum
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 valid_value
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