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
@@ -17,24 +17,27 @@ module ActiveModel
|
|
17
17
|
# end
|
18
18
|
#
|
19
19
|
# cm = ContactMessage.new
|
20
|
-
# cm.to_model == self
|
21
|
-
# cm.to_key
|
22
|
-
# cm.to_param
|
20
|
+
# cm.to_model == self # => true
|
21
|
+
# cm.to_key # => nil
|
22
|
+
# cm.to_param # => nil
|
23
23
|
#
|
24
24
|
module Conversion
|
25
|
-
# If your object is already designed to implement all of the Active Model
|
26
|
-
# you can use the default to_model implementation, which simply returns
|
25
|
+
# If your object is already designed to implement all of the Active Model
|
26
|
+
# you can use the default to_model implementation, which simply returns
|
27
27
|
# self.
|
28
|
-
#
|
29
|
-
# If your model does not act like an Active Model object, then you should
|
30
|
-
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
|
28
|
+
#
|
29
|
+
# If your model does not act like an Active Model object, then you should
|
30
|
+
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
|
31
31
|
# your object with Active Model compliant methods.
|
32
32
|
def to_model
|
33
33
|
self
|
34
34
|
end
|
35
35
|
|
36
|
-
# Returns an Enumerable of all
|
37
|
-
#
|
36
|
+
# Returns an Enumerable of all key attributes if any is set, regardless
|
37
|
+
# if the object is persisted or not.
|
38
|
+
#
|
39
|
+
# Note the default implementation uses persisted? just because all objects
|
40
|
+
# in Ruby 1.8.x responds to :id.
|
38
41
|
def to_key
|
39
42
|
persisted? ? [id] : nil
|
40
43
|
end
|
@@ -42,7 +45,7 @@ module ActiveModel
|
|
42
45
|
# Returns a string representing the object's key suitable for use in URLs,
|
43
46
|
# or nil if persisted? is false
|
44
47
|
def to_param
|
45
|
-
|
48
|
+
persisted? ? to_key.join('-') : nil
|
46
49
|
end
|
47
50
|
end
|
48
51
|
end
|
data/lib/active_model/dirty.rb
CHANGED
@@ -6,47 +6,48 @@ require 'active_support/core_ext/object/duplicable'
|
|
6
6
|
module ActiveModel
|
7
7
|
# == Active Model Dirty
|
8
8
|
#
|
9
|
-
# Provides a way to track changes in your object in the same way as
|
9
|
+
# Provides a way to track changes in your object in the same way as
|
10
10
|
# Active Record does.
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# The requirements to implement ActiveModel::Dirty are to:
|
13
13
|
#
|
14
14
|
# * <tt>include ActiveModel::Dirty</tt> in your object
|
15
|
-
# * Call <tt>define_attribute_methods</tt> passing each method you want to
|
15
|
+
# * Call <tt>define_attribute_methods</tt> passing each method you want to
|
16
16
|
# track
|
17
|
-
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
|
17
|
+
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
|
18
18
|
# attribute
|
19
|
-
#
|
20
|
-
# If you wish to also track previous changes on save or update, you need to
|
19
|
+
#
|
20
|
+
# If you wish to also track previous changes on save or update, you need to
|
21
21
|
# add
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# @previously_changed = changes
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# inside of your save or update method.
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# A minimal implementation could be:
|
28
|
-
#
|
28
|
+
#
|
29
29
|
# class Person
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# include ActiveModel::Dirty
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# define_attribute_methods [:name]
|
34
|
-
#
|
34
|
+
#
|
35
35
|
# def name
|
36
36
|
# @name
|
37
37
|
# end
|
38
|
-
#
|
38
|
+
#
|
39
39
|
# def name=(val)
|
40
|
-
# name_will_change!
|
40
|
+
# name_will_change! unless val == @name
|
41
41
|
# @name = val
|
42
42
|
# end
|
43
|
-
#
|
43
|
+
#
|
44
44
|
# def save
|
45
45
|
# @previously_changed = changes
|
46
|
+
# @changed_attributes.clear
|
46
47
|
# end
|
47
|
-
#
|
48
|
+
#
|
48
49
|
# end
|
49
|
-
#
|
50
|
+
#
|
50
51
|
# == Examples:
|
51
52
|
#
|
52
53
|
# A newly instantiated object is unchanged:
|
@@ -77,13 +78,10 @@ module ActiveModel
|
|
77
78
|
# person.changed # => ['name']
|
78
79
|
# person.changes # => { 'name' => ['Bill', 'Bob'] }
|
79
80
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
# person.name_changed? # => false
|
84
|
-
# person.name # => 'Bill'
|
81
|
+
# If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
|
82
|
+
# to mark that the attribute is changing. Otherwise ActiveModel can't track changes to
|
83
|
+
# in-place attributes.
|
85
84
|
#
|
86
|
-
# Before modifying an attribute in-place:
|
87
85
|
# person.name_will_change!
|
88
86
|
# person.name << 'y'
|
89
87
|
# person.name_change # => ['Bill', 'Billy']
|
data/lib/active_model/errors.rb
CHANGED
@@ -12,50 +12,50 @@ module ActiveModel
|
|
12
12
|
#
|
13
13
|
# Provides a modified +OrderedHash+ that you can include in your object
|
14
14
|
# for handling error messages and interacting with Action Pack helpers.
|
15
|
-
#
|
15
|
+
#
|
16
16
|
# A minimal implementation could be:
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# class Person
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# # Required dependency for ActiveModel::Errors
|
21
21
|
# extend ActiveModel::Naming
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# def initialize
|
24
24
|
# @errors = ActiveModel::Errors.new(self)
|
25
25
|
# end
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# attr_accessor :name
|
28
28
|
# attr_reader :errors
|
29
|
-
#
|
29
|
+
#
|
30
30
|
# def validate!
|
31
31
|
# errors.add(:name, "can not be nil") if name == nil
|
32
32
|
# end
|
33
|
-
#
|
33
|
+
#
|
34
34
|
# # The following methods are needed to be minimally implemented
|
35
35
|
#
|
36
36
|
# def read_attribute_for_validation(attr)
|
37
37
|
# send(attr)
|
38
38
|
# end
|
39
|
-
#
|
40
|
-
# def
|
39
|
+
#
|
40
|
+
# def Person.human_attribute_name(attr, options = {})
|
41
41
|
# attr
|
42
42
|
# end
|
43
|
-
#
|
44
|
-
# def
|
43
|
+
#
|
44
|
+
# def Person.lookup_ancestors
|
45
45
|
# [self]
|
46
46
|
# end
|
47
|
-
#
|
47
|
+
#
|
48
48
|
# end
|
49
|
-
#
|
49
|
+
#
|
50
50
|
# The last three methods are required in your object for Errors to be
|
51
51
|
# able to generate error messages correctly and also handle multiple
|
52
52
|
# languages. Of course, if you extend your object with ActiveModel::Translations
|
53
53
|
# you will not need to implement the last two. Likewise, using
|
54
54
|
# ActiveModel::Validations will handle the validation related methods
|
55
55
|
# for you.
|
56
|
-
#
|
56
|
+
#
|
57
57
|
# The above allows you to do:
|
58
|
-
#
|
58
|
+
#
|
59
59
|
# p = Person.new
|
60
60
|
# p.validate! # => ["can not be nil"]
|
61
61
|
# p.errors.full_messages # => ["name can not be nil"]
|
@@ -66,7 +66,7 @@ module ActiveModel
|
|
66
66
|
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank]
|
67
67
|
|
68
68
|
# Pass in the instance of the object that is using the errors object.
|
69
|
-
#
|
69
|
+
#
|
70
70
|
# class Person
|
71
71
|
# def initialize
|
72
72
|
# @errors = ActiveModel::Errors.new(self)
|
@@ -80,23 +80,19 @@ module ActiveModel
|
|
80
80
|
alias_method :get, :[]
|
81
81
|
alias_method :set, :[]=
|
82
82
|
|
83
|
-
# When passed a symbol or a name of a method, returns an array of errors
|
83
|
+
# When passed a symbol or a name of a method, returns an array of errors
|
84
84
|
# for the method.
|
85
|
-
#
|
86
|
-
# p.errors[:name]
|
87
|
-
# p.errors['name']
|
85
|
+
#
|
86
|
+
# p.errors[:name] # => ["can not be nil"]
|
87
|
+
# p.errors['name'] # => ["can not be nil"]
|
88
88
|
def [](attribute)
|
89
|
-
|
90
|
-
errors
|
91
|
-
else
|
92
|
-
set(attribute.to_sym, [])
|
93
|
-
end
|
89
|
+
get(attribute.to_sym) || set(attribute.to_sym, [])
|
94
90
|
end
|
95
91
|
|
96
92
|
# Adds to the supplied attribute the supplied error message.
|
97
|
-
#
|
93
|
+
#
|
98
94
|
# p.errors[:name] = "must be set"
|
99
|
-
# p.errors[:name]
|
95
|
+
# p.errors[:name] # => ['must be set']
|
100
96
|
def []=(attribute, error)
|
101
97
|
self[attribute.to_sym] << error
|
102
98
|
end
|
@@ -104,12 +100,12 @@ module ActiveModel
|
|
104
100
|
# Iterates through each error key, value pair in the error messages hash.
|
105
101
|
# Yields the attribute and the error for that attribute. If the attribute
|
106
102
|
# has more than one error message, yields once for each error message.
|
107
|
-
#
|
103
|
+
#
|
108
104
|
# p.errors.add(:name, "can't be blank")
|
109
105
|
# p.errors.each do |attribute, errors_array|
|
110
106
|
# # Will yield :name and "can't be blank"
|
111
107
|
# end
|
112
|
-
#
|
108
|
+
#
|
113
109
|
# p.errors.add(:name, "must be specified")
|
114
110
|
# p.errors.each do |attribute, errors_array|
|
115
111
|
# # Will yield :name and "can't be blank"
|
@@ -122,29 +118,29 @@ module ActiveModel
|
|
122
118
|
end
|
123
119
|
|
124
120
|
# Returns the number of error messages.
|
125
|
-
#
|
121
|
+
#
|
126
122
|
# p.errors.add(:name, "can't be blank")
|
127
|
-
# p.errors.size
|
123
|
+
# p.errors.size # => 1
|
128
124
|
# p.errors.add(:name, "must be specified")
|
129
|
-
# p.errors.size
|
125
|
+
# p.errors.size # => 2
|
130
126
|
def size
|
131
127
|
values.flatten.size
|
132
128
|
end
|
133
129
|
|
134
130
|
# Returns an array of error messages, with the attribute name included
|
135
|
-
#
|
131
|
+
#
|
136
132
|
# p.errors.add(:name, "can't be blank")
|
137
133
|
# p.errors.add(:name, "must be specified")
|
138
|
-
# p.errors.to_a
|
134
|
+
# p.errors.to_a # => ["name can't be blank", "name must be specified"]
|
139
135
|
def to_a
|
140
136
|
full_messages
|
141
137
|
end
|
142
138
|
|
143
139
|
# Returns the number of error messages.
|
144
140
|
# p.errors.add(:name, "can't be blank")
|
145
|
-
# p.errors.count
|
141
|
+
# p.errors.count # => 1
|
146
142
|
# p.errors.add(:name, "must be specified")
|
147
|
-
# p.errors.count
|
143
|
+
# p.errors.count # => 2
|
148
144
|
def count
|
149
145
|
to_a.size
|
150
146
|
end
|
@@ -155,11 +151,11 @@ module ActiveModel
|
|
155
151
|
end
|
156
152
|
|
157
153
|
# Returns an xml formatted representation of the Errors hash.
|
158
|
-
#
|
154
|
+
#
|
159
155
|
# p.errors.add(:name, "can't be blank")
|
160
156
|
# p.errors.add(:name, "must be specified")
|
161
|
-
# p.errors.to_xml
|
162
|
-
#
|
157
|
+
# p.errors.to_xml
|
158
|
+
# # =>
|
163
159
|
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
164
160
|
# # <errors>
|
165
161
|
# # <error>name can't be blank</error>
|
@@ -169,16 +165,16 @@ module ActiveModel
|
|
169
165
|
to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
|
170
166
|
end
|
171
167
|
|
172
|
-
# Returns an
|
168
|
+
# Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
|
173
169
|
def as_json(options=nil)
|
174
|
-
|
170
|
+
self
|
175
171
|
end
|
176
172
|
|
177
173
|
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
|
178
174
|
# <tt>on(attribute)</tt> for the same attribute. More than one error can be added to the same
|
179
175
|
# +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
|
180
176
|
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
|
181
|
-
#
|
177
|
+
#
|
182
178
|
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
|
183
179
|
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
|
184
180
|
def add(attribute, message = nil, options = {})
|
@@ -257,19 +253,19 @@ module ActiveModel
|
|
257
253
|
full_messages
|
258
254
|
end
|
259
255
|
|
260
|
-
# Translates an error message in its default scope
|
256
|
+
# Translates an error message in its default scope
|
261
257
|
# (<tt>activemodel.errors.messages</tt>).
|
262
258
|
#
|
263
|
-
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
|
264
|
-
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not
|
265
|
-
# there also, it returns the translation of the default message
|
259
|
+
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
|
260
|
+
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not
|
261
|
+
# there also, it returns the translation of the default message
|
266
262
|
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
|
267
263
|
# translated attribute name and the value are available for interpolation.
|
268
264
|
#
|
269
265
|
# When using inheritance in your models, it will check all the inherited
|
270
266
|
# models too, but only if the model itself hasn't been found. Say you have
|
271
|
-
# <tt>class Admin < User; end</tt> and you wanted the translation for
|
272
|
-
# the <tt>:blank</tt> error +message+ for the <tt>title</tt> +attribute+,
|
267
|
+
# <tt>class Admin < User; end</tt> and you wanted the translation for
|
268
|
+
# the <tt>:blank</tt> error +message+ for the <tt>title</tt> +attribute+,
|
273
269
|
# it looks for these translations:
|
274
270
|
#
|
275
271
|
# <ol>
|
data/lib/active_model/lint.rb
CHANGED
@@ -38,6 +38,7 @@ module ActiveModel
|
|
38
38
|
# not persisted?, then to_param should always return nil.
|
39
39
|
def test_to_param
|
40
40
|
assert model.respond_to?(:to_param), "The model should respond to to_param"
|
41
|
+
def model.to_key() [1] end
|
41
42
|
def model.persisted?() false end
|
42
43
|
assert model.to_param.nil?
|
43
44
|
end
|
@@ -79,7 +80,7 @@ module ActiveModel
|
|
79
80
|
end
|
80
81
|
|
81
82
|
# == Errors Testing
|
82
|
-
#
|
83
|
+
#
|
83
84
|
# Returns an object that has :[] and :full_messages defined on it. See below
|
84
85
|
# for more details.
|
85
86
|
#
|
data/lib/active_model/naming.rb
CHANGED
@@ -17,7 +17,10 @@ module ActiveModel
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Transform the model name into a more humane format, using I18n. By default,
|
20
|
-
# it will underscore then humanize the class name
|
20
|
+
# it will underscore then humanize the class name
|
21
|
+
#
|
22
|
+
# BlogPost.model_name.human # => "Blog post"
|
23
|
+
#
|
21
24
|
# Specify +options+ with additional translating options.
|
22
25
|
def human(options={})
|
23
26
|
return @human unless @klass.respond_to?(:lookup_ancestors) &&
|
@@ -38,16 +41,16 @@ module ActiveModel
|
|
38
41
|
# == Active Model Naming
|
39
42
|
#
|
40
43
|
# Creates a +model_name+ method on your object.
|
41
|
-
#
|
44
|
+
#
|
42
45
|
# To implement, just extend ActiveModel::Naming in your object:
|
43
|
-
#
|
46
|
+
#
|
44
47
|
# class BookCover
|
45
48
|
# extend ActiveModel::Naming
|
46
49
|
# end
|
47
|
-
#
|
48
|
-
# BookCover.model_name
|
49
|
-
# BookCover.model_name.human
|
50
|
-
#
|
50
|
+
#
|
51
|
+
# BookCover.model_name # => "BookCover"
|
52
|
+
# BookCover.model_name.human # => "Book cover"
|
53
|
+
#
|
51
54
|
# Providing the functionality that ActiveModel::Naming provides in your object
|
52
55
|
# is required to pass the Active Model Lint test. So either extending the provided
|
53
56
|
# method below, or rolling your own is required..
|
@@ -87,5 +90,5 @@ module ActiveModel
|
|
87
90
|
(record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
|
88
91
|
end
|
89
92
|
end
|
90
|
-
|
93
|
+
|
91
94
|
end
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
10
10
|
|
11
11
|
module ClassMethods
|
12
12
|
# == Active Model Observers Activation
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# Activates the observers assigned. Examples:
|
15
15
|
#
|
16
16
|
# # Calls PersonObserver.instance
|
@@ -22,7 +22,7 @@ module ActiveModel
|
|
22
22
|
# # Same as above, just using explicit class references
|
23
23
|
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
24
24
|
#
|
25
|
-
# Note: Setting this does not instantiate the observers yet.
|
25
|
+
# Note: Setting this does not instantiate the observers yet.
|
26
26
|
# +instantiate_observers+ is called during startup, and before
|
27
27
|
# each development request.
|
28
28
|
def observers=(*values)
|
@@ -123,9 +123,9 @@ module ActiveModel
|
|
123
123
|
#
|
124
124
|
# Observers will by default be mapped to the class with which they share a
|
125
125
|
# name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
|
126
|
-
# to ProductManager, and so on. If you want to name your observer differently than
|
127
|
-
# the class you're interested in observing, you can use the Observer.observe class
|
128
|
-
# method which takes either the concrete class (Product) or a symbol for that
|
126
|
+
# to ProductManager, and so on. If you want to name your observer differently than
|
127
|
+
# the class you're interested in observing, you can use the Observer.observe class
|
128
|
+
# method which takes either the concrete class (Product) or a symbol for that
|
129
129
|
# class (:product):
|
130
130
|
#
|
131
131
|
# class AuditObserver < ActiveModel::Observer
|
@@ -136,7 +136,7 @@ module ActiveModel
|
|
136
136
|
# end
|
137
137
|
# end
|
138
138
|
#
|
139
|
-
# If the audit observer needs to watch more than one kind of object, this can be
|
139
|
+
# If the audit observer needs to watch more than one kind of object, this can be
|
140
140
|
# specified with multiple arguments:
|
141
141
|
#
|
142
142
|
# class AuditObserver < ActiveModel::Observer
|
@@ -147,7 +147,7 @@ module ActiveModel
|
|
147
147
|
# end
|
148
148
|
# end
|
149
149
|
#
|
150
|
-
# The AuditObserver will now act on both updates to Account and Balance by treating
|
150
|
+
# The AuditObserver will now act on both updates to Account and Balance by treating
|
151
151
|
# them both as records.
|
152
152
|
#
|
153
153
|
class Observer
|