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.
@@ -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