activemodel 3.0.0.beta → 3.0.0.beta2
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 +27 -1
- data/README +37 -48
- data/lib/active_model/attribute_methods.rb +23 -8
- data/lib/active_model/callbacks.rb +4 -3
- data/lib/active_model/conversion.rb +35 -10
- data/lib/active_model/dirty.rb +19 -19
- data/lib/active_model/errors.rb +3 -1
- data/lib/active_model/lint.rb +31 -9
- data/lib/active_model/naming.rb +3 -1
- data/lib/active_model/serializers/xml.rb +4 -3
- data/lib/active_model/validations.rb +21 -6
- data/lib/active_model/validations/inclusion.rb +1 -1
- data/lib/active_model/validations/with.rb +9 -0
- data/lib/active_model/validator.rb +21 -1
- data/lib/active_model/version.rb +3 -2
- metadata +30 -13
data/CHANGELOG
CHANGED
@@ -1,4 +1,30 @@
|
|
1
|
-
*
|
1
|
+
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
|
2
|
+
|
3
|
+
* #new_record? and #destroyed? were removed from ActiveModel::Lint. Use
|
4
|
+
persisted? instead. A model is persisted if it's not a new_record? and it was
|
5
|
+
not destroyed? [MG]
|
6
|
+
|
7
|
+
* Added validations reflection in ActiveModel::Validations [JV]
|
8
|
+
|
9
|
+
Model.validators
|
10
|
+
Model.validators_on(:field)
|
11
|
+
|
12
|
+
* #to_key was added to ActiveModel::Lint so we can generate DOM IDs for
|
13
|
+
AMo objects with composite keys [MG]
|
14
|
+
|
15
|
+
|
16
|
+
*Rails 3.0.0 [beta 1] (February 4, 2010)*
|
17
|
+
|
18
|
+
* ActiveModel::Observer#add_observer!
|
19
|
+
|
20
|
+
It has a custom hook to define after_find that should really be in a
|
21
|
+
ActiveRecord::Observer subclass:
|
22
|
+
|
23
|
+
def add_observer!(klass)
|
24
|
+
klass.add_observer(self)
|
25
|
+
klass.class_eval 'def after_find() end' unless
|
26
|
+
klass.respond_to?(:after_find)
|
27
|
+
end
|
2
28
|
|
3
29
|
* Change the ActiveModel::Base.include_root_in_json default to true for Rails 3 [DHH]
|
4
30
|
|
data/README
CHANGED
@@ -11,9 +11,9 @@ Active Model is a solution for this problem.
|
|
11
11
|
Active Model provides a known set of interfaces that your objects can implement
|
12
12
|
to then present a common interface to the Action Pack helpers. You can include
|
13
13
|
functionality from the following modules:
|
14
|
-
|
14
|
+
|
15
15
|
* Adding attribute magic to your objects
|
16
|
-
|
16
|
+
|
17
17
|
Add prefixes and suffixes to defined attribute methods...
|
18
18
|
|
19
19
|
class Person
|
@@ -34,7 +34,7 @@ functionality from the following modules:
|
|
34
34
|
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
|
35
35
|
|
36
36
|
* Adding callbacks to your objects
|
37
|
-
|
37
|
+
|
38
38
|
class Person
|
39
39
|
extend ActiveModel::Callbacks
|
40
40
|
define_model_callbacks :create
|
@@ -50,19 +50,19 @@ functionality from the following modules:
|
|
50
50
|
wrap your create method.
|
51
51
|
|
52
52
|
{Learn more}[link:classes/ActiveModel/CallBacks.html]
|
53
|
-
|
53
|
+
|
54
54
|
* For classes that already look like an Active Record object
|
55
|
-
|
55
|
+
|
56
56
|
class Person
|
57
57
|
include ActiveModel::Conversion
|
58
58
|
end
|
59
59
|
|
60
60
|
...returns the class itself when sent :to_model
|
61
|
-
|
61
|
+
|
62
62
|
{Learn more}[link:classes/ActiveModel/Conversion.html]
|
63
|
-
|
63
|
+
|
64
64
|
* Tracking changes in your object
|
65
|
-
|
65
|
+
|
66
66
|
Provides all the value tracking features implemented by ActiveRecord...
|
67
67
|
|
68
68
|
person = Person.new
|
@@ -77,29 +77,29 @@ functionality from the following modules:
|
|
77
77
|
person.previous_changes # => {'name' => ['bob, 'robert']}
|
78
78
|
|
79
79
|
{Learn more}[link:classes/ActiveModel/Dirty.html]
|
80
|
-
|
80
|
+
|
81
81
|
* Adding +errors+ support to your object
|
82
|
-
|
82
|
+
|
83
83
|
Provides the error messages to allow your object to interact with Action Pack
|
84
84
|
helpers seamlessly...
|
85
85
|
|
86
86
|
class Person
|
87
|
-
|
87
|
+
|
88
88
|
def initialize
|
89
89
|
@errors = ActiveModel::Errors.new(self)
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
attr_accessor :name
|
93
93
|
attr_reader :errors
|
94
|
-
|
94
|
+
|
95
95
|
def validate!
|
96
96
|
errors.add(:name, "can not be nil") if name == nil
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
def ErrorsPerson.human_attribute_name(attr, options = {})
|
100
100
|
"Name"
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
end
|
104
104
|
|
105
105
|
... gives you...
|
@@ -108,18 +108,18 @@ functionality from the following modules:
|
|
108
108
|
# => ["Name Can not be nil"]
|
109
109
|
person.errors.full_messages
|
110
110
|
# => ["Name Can not be nil"]
|
111
|
-
|
111
|
+
|
112
112
|
{Learn more}[link:classes/ActiveModel/Errors.html]
|
113
|
-
|
113
|
+
|
114
114
|
* Testing the compliance of your object
|
115
|
-
|
115
|
+
|
116
116
|
Use ActiveModel::Lint to test the compliance of your object to the
|
117
117
|
basic ActiveModel API...
|
118
118
|
|
119
119
|
{Learn more}[link:classes/ActiveModel/Lint/Tests.html]
|
120
|
-
|
120
|
+
|
121
121
|
* Providing a human face to your object
|
122
|
-
|
122
|
+
|
123
123
|
ActiveModel::Naming provides your model with the model_name convention
|
124
124
|
and a human_name attribute...
|
125
125
|
|
@@ -131,19 +131,19 @@ functionality from the following modules:
|
|
131
131
|
|
132
132
|
NamedPerson.model_name #=> "NamedPerson"
|
133
133
|
NamedPerson.model_name.human #=> "Named person"
|
134
|
-
|
134
|
+
|
135
135
|
{Learn more}[link:classes/ActiveModel/Naming.html]
|
136
|
-
|
136
|
+
|
137
137
|
* Adding observer support to your objects
|
138
|
-
|
138
|
+
|
139
139
|
ActiveModel::Observers allows your object to implement the Observer
|
140
140
|
pattern in a Rails App and take advantage of all the standard observer
|
141
141
|
functions.
|
142
142
|
|
143
143
|
{Learn more}[link:classes/ActiveModel/Observer.html]
|
144
|
-
|
144
|
+
|
145
145
|
* Making your object serializable
|
146
|
-
|
146
|
+
|
147
147
|
ActiveModel::Serialization provides a standard interface for your object
|
148
148
|
to provide to_json or to_xml serialization...
|
149
149
|
|
@@ -153,48 +153,37 @@ functionality from the following modules:
|
|
153
153
|
s.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
154
154
|
|
155
155
|
{Learn more}[link:classes/ActiveModel/Serialization.html]
|
156
|
-
|
157
|
-
|
158
|
-
* Turning your object into a finite State Machine
|
159
|
-
|
160
|
-
ActiveModel::StateMachine provides a clean way to include all the methods
|
161
|
-
you need to transform your object into a finite State Machine...
|
162
|
-
|
163
|
-
light = TrafficLight.new
|
164
|
-
light.current_state #=> :red
|
165
|
-
light.change_color! #=> true
|
166
|
-
light.current_state #=> :green
|
167
|
-
|
168
|
-
{Learn more}[link:classes/ActiveModel/StateMachine.html]
|
169
|
-
|
156
|
+
|
170
157
|
* Integrating with Rail's internationalization (i18n) handling through
|
171
158
|
ActiveModel::Translations...
|
172
|
-
|
159
|
+
|
173
160
|
class Person
|
174
161
|
extend ActiveModel::Translation
|
175
162
|
end
|
176
163
|
|
177
164
|
{Learn more}[link:classes/ActiveModel/Translation.html]
|
178
|
-
|
165
|
+
|
179
166
|
* Providing a full Validation stack for your objects...
|
180
|
-
|
167
|
+
|
181
168
|
class Person
|
182
169
|
include ActiveModel::Validations
|
183
|
-
|
170
|
+
|
184
171
|
attr_accessor :first_name, :last_name
|
185
|
-
|
172
|
+
|
173
|
+
|
186
174
|
validates_each :first_name, :last_name do |record, attr, value|
|
187
175
|
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
|
188
176
|
end
|
189
177
|
end
|
190
|
-
|
178
|
+
|
179
|
+
|
191
180
|
person = Person.new(:first_name => 'zoolander')
|
192
181
|
person.valid? #=> false
|
193
|
-
|
182
|
+
|
194
183
|
{Learn more}[link:classes/ActiveModel/Validations.html]
|
195
184
|
|
196
185
|
* Make custom validators
|
197
|
-
|
186
|
+
|
198
187
|
class Person
|
199
188
|
include ActiveModel::Validations
|
200
189
|
validates_with HasNameValidator
|
@@ -212,5 +201,5 @@ functionality from the following modules:
|
|
212
201
|
p.errors.full_messages #=> ["Name must exist"]
|
213
202
|
p.name = "Bob"
|
214
203
|
p.valid? #=> true
|
215
|
-
|
204
|
+
|
216
205
|
{Learn more}[link:classes/ActiveModel/Validator.html]
|
@@ -21,7 +21,6 @@ module ActiveModel
|
|
21
21
|
# A minimal implementation could be:
|
22
22
|
#
|
23
23
|
# class Person
|
24
|
-
#
|
25
24
|
# include ActiveModel::AttributeMethods
|
26
25
|
#
|
27
26
|
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
|
@@ -44,13 +43,16 @@ module ActiveModel
|
|
44
43
|
# def reset_attribute_to_default!(attr)
|
45
44
|
# send("#{attr}=", "Default Name")
|
46
45
|
# end
|
47
|
-
#
|
48
46
|
# end
|
49
|
-
#
|
47
|
+
#
|
48
|
+
# Please notice that whenever you include ActiveModel::AtributeMethods in your class,
|
49
|
+
# it requires you to implement a <tt>attributes</tt> methods which returns a hash with
|
50
|
+
# each attribute name in your model as hash key and the attribute value as hash value.
|
51
|
+
# Hash keys must be a string.
|
52
|
+
#
|
50
53
|
module AttributeMethods
|
51
54
|
extend ActiveSupport::Concern
|
52
55
|
|
53
|
-
# Declare and check for suffixed attribute methods.
|
54
56
|
module ClassMethods
|
55
57
|
# Defines an "attribute" method (like +inheritance_column+ or
|
56
58
|
# +table_name+). A new (class) method will be created with the
|
@@ -86,14 +88,21 @@ module ActiveModel
|
|
86
88
|
# AttributePerson.inheritance_column
|
87
89
|
# # => 'address_id'
|
88
90
|
def define_attr_method(name, value=nil, &block)
|
89
|
-
sing =
|
90
|
-
sing.
|
91
|
+
sing = singleton_class
|
92
|
+
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
|
93
|
+
if method_defined?(:original_#{name})
|
94
|
+
undef :original_#{name}
|
95
|
+
end
|
96
|
+
alias_method :original_#{name}, :#{name}
|
97
|
+
eorb
|
91
98
|
if block_given?
|
92
99
|
sing.send :define_method, name, &block
|
93
100
|
else
|
94
101
|
# use eval instead of a block to work around a memory leak in dev
|
95
102
|
# mode in fcgi
|
96
|
-
sing.class_eval
|
103
|
+
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
|
104
|
+
def #{name}; #{value.to_s.inspect}; end
|
105
|
+
eorb
|
97
106
|
end
|
98
107
|
end
|
99
108
|
|
@@ -254,8 +263,13 @@ module ActiveModel
|
|
254
263
|
if respond_to?(generate_method)
|
255
264
|
send(generate_method, attr_name)
|
256
265
|
else
|
266
|
+
method_name = matcher.method_name(attr_name)
|
267
|
+
|
257
268
|
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__+1
|
258
|
-
|
269
|
+
if method_defined?(:#{method_name})
|
270
|
+
undef :#{method_name}
|
271
|
+
end
|
272
|
+
def #{method_name}(*args)
|
259
273
|
send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
|
260
274
|
end
|
261
275
|
STR
|
@@ -283,6 +297,7 @@ module ActiveModel
|
|
283
297
|
end
|
284
298
|
end
|
285
299
|
|
300
|
+
# Returns true if the attribute methods defined have been generated.
|
286
301
|
def attribute_methods_generated?
|
287
302
|
@attribute_methods_generated ||= nil
|
288
303
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
1
2
|
require 'active_support/callbacks'
|
2
3
|
|
3
4
|
module ActiveModel
|
@@ -91,7 +92,7 @@ module ActiveModel
|
|
91
92
|
options = callbacks.extract_options!
|
92
93
|
options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)
|
93
94
|
|
94
|
-
types = Array(options.delete(:only))
|
95
|
+
types = Array.wrap(options.delete(:only))
|
95
96
|
types = [:before, :around, :after] if types.empty?
|
96
97
|
|
97
98
|
callbacks.each do |callback|
|
@@ -124,10 +125,10 @@ module ActiveModel
|
|
124
125
|
def self.after_#{callback}(*args, &block)
|
125
126
|
options = args.extract_options!
|
126
127
|
options[:prepend] = true
|
127
|
-
options[:if] = Array(options[:if]) << "!halted && value != false"
|
128
|
+
options[:if] = Array.wrap(options[:if]) << "!halted && value != false"
|
128
129
|
set_callback(:#{callback}, :after, *(args << options), &block)
|
129
130
|
end
|
130
131
|
CALLBACK
|
131
132
|
end
|
132
133
|
end
|
133
|
-
end
|
134
|
+
end
|
@@ -1,19 +1,44 @@
|
|
1
1
|
module ActiveModel
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
2
|
+
# Handle default conversions: to_model, to_key and to_param.
|
3
|
+
#
|
4
|
+
# == Example
|
5
|
+
#
|
6
|
+
# Let's take for example this non persisted object.
|
7
|
+
#
|
8
|
+
# class ContactMessage
|
6
9
|
# include ActiveModel::Conversion
|
10
|
+
#
|
11
|
+
# # ContactMessage are never persisted in the DB
|
12
|
+
# def persisted?
|
13
|
+
# false
|
14
|
+
# end
|
7
15
|
# end
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
16
|
+
#
|
17
|
+
# cm = ContactMessage.new
|
18
|
+
# cm.to_model == self #=> true
|
19
|
+
# cm.to_key #=> nil
|
20
|
+
# cm.to_param #=> nil
|
21
|
+
#
|
14
22
|
module Conversion
|
23
|
+
# If your object is already designed to implement all of the Active Model you can use
|
24
|
+
# the default to_model implementation, which simply returns self.
|
25
|
+
#
|
26
|
+
# If your model does not act like an Active Model object, then you should define
|
27
|
+
# <tt>:to_model</tt> yourself returning a proxy object that wraps your object
|
28
|
+
# with Active Model compliant methods.
|
15
29
|
def to_model
|
16
30
|
self
|
17
31
|
end
|
32
|
+
|
33
|
+
# Returns an Enumerable of all (primary) key attributes or nil if persisted? is fakse
|
34
|
+
def to_key
|
35
|
+
persisted? ? [id] : nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a string representing the object's key suitable for use in URLs,
|
39
|
+
# or nil if persisted? is false
|
40
|
+
def to_param
|
41
|
+
to_key ? to_key.join('-') : nil
|
42
|
+
end
|
18
43
|
end
|
19
44
|
end
|
data/lib/active_model/dirty.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
require 'active_model/attribute_methods'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/hash_with_indifferent_access'
|
4
|
+
require 'active_support/core_ext/object/duplicable'
|
5
|
+
|
1
6
|
module ActiveModel
|
2
7
|
# <tt>ActiveModel::Dirty</tt> provides a way to track changes in your
|
3
8
|
# object in the same way as ActiveRecord does.
|
@@ -86,12 +91,17 @@ module ActiveModel
|
|
86
91
|
attribute_method_affix :prefix => 'reset_', :suffix => '!'
|
87
92
|
end
|
88
93
|
|
94
|
+
def initialize(*)
|
95
|
+
@changed_attributes = {}
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
89
99
|
# Do any attributes have unsaved changes?
|
90
100
|
# person.changed? # => false
|
91
101
|
# person.name = 'bob'
|
92
102
|
# person.changed? # => true
|
93
103
|
def changed?
|
94
|
-
|
104
|
+
!@changed_attributes.empty?
|
95
105
|
end
|
96
106
|
|
97
107
|
# List of attributes with unsaved changes.
|
@@ -99,7 +109,7 @@ module ActiveModel
|
|
99
109
|
# person.name = 'bob'
|
100
110
|
# person.changed # => ['name']
|
101
111
|
def changed
|
102
|
-
changed_attributes.keys
|
112
|
+
@changed_attributes.keys
|
103
113
|
end
|
104
114
|
|
105
115
|
# Map of changed attrs => [original value, new value].
|
@@ -107,7 +117,7 @@ module ActiveModel
|
|
107
117
|
# person.name = 'bob'
|
108
118
|
# person.changes # => { 'name' => ['bill', 'bob'] }
|
109
119
|
def changes
|
110
|
-
changed.inject(
|
120
|
+
changed.inject(HashWithIndifferentAccess.new){ |h, attr| h[attr] = attribute_change(attr); h }
|
111
121
|
end
|
112
122
|
|
113
123
|
# Map of attributes that were changed when the model was saved.
|
@@ -116,33 +126,23 @@ module ActiveModel
|
|
116
126
|
# person.save
|
117
127
|
# person.previous_changes # => {'name' => ['bob, 'robert']}
|
118
128
|
def previous_changes
|
119
|
-
|
129
|
+
@previously_changed
|
120
130
|
end
|
121
131
|
|
122
132
|
private
|
123
|
-
# Map of change <tt>attr => original value</tt>.
|
124
|
-
def changed_attributes
|
125
|
-
@changed_attributes ||= {}
|
126
|
-
end
|
127
|
-
|
128
|
-
# Map of fields that were changed when the model was saved
|
129
|
-
def previously_changed_attributes
|
130
|
-
@previously_changed || {}
|
131
|
-
end
|
132
|
-
|
133
133
|
# Handle <tt>*_changed?</tt> for +method_missing+.
|
134
134
|
def attribute_changed?(attr)
|
135
|
-
changed_attributes.include?(attr)
|
135
|
+
@changed_attributes.include?(attr)
|
136
136
|
end
|
137
137
|
|
138
138
|
# Handle <tt>*_change</tt> for +method_missing+.
|
139
139
|
def attribute_change(attr)
|
140
|
-
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
|
140
|
+
[@changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
|
141
141
|
end
|
142
142
|
|
143
143
|
# Handle <tt>*_was</tt> for +method_missing+.
|
144
144
|
def attribute_was(attr)
|
145
|
-
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
|
145
|
+
attribute_changed?(attr) ? @changed_attributes[attr] : __send__(attr)
|
146
146
|
end
|
147
147
|
|
148
148
|
# Handle <tt>*_will_change!</tt> for +method_missing+.
|
@@ -153,12 +153,12 @@ module ActiveModel
|
|
153
153
|
rescue TypeError, NoMethodError
|
154
154
|
end
|
155
155
|
|
156
|
-
changed_attributes[attr] = value
|
156
|
+
@changed_attributes[attr] = value
|
157
157
|
end
|
158
158
|
|
159
159
|
# Handle <tt>reset_*!</tt> for +method_missing+.
|
160
160
|
def reset_attribute!(attr)
|
161
|
-
__send__("#{attr}=", changed_attributes[attr]) if attribute_changed?(attr)
|
161
|
+
__send__("#{attr}=", @changed_attributes[attr]) if attribute_changed?(attr)
|
162
162
|
end
|
163
163
|
end
|
164
164
|
end
|
data/lib/active_model/errors.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
1
2
|
require 'active_support/core_ext/string/inflections'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
2
4
|
require 'active_support/ordered_hash'
|
3
5
|
|
4
6
|
module ActiveModel
|
@@ -206,7 +208,7 @@ module ActiveModel
|
|
206
208
|
full_messages = []
|
207
209
|
|
208
210
|
each do |attribute, messages|
|
209
|
-
messages = Array(messages)
|
211
|
+
messages = Array.wrap(messages)
|
210
212
|
next if messages.empty?
|
211
213
|
|
212
214
|
if attribute == :base
|
data/lib/active_model/lint.rb
CHANGED
@@ -13,6 +13,33 @@
|
|
13
13
|
module ActiveModel
|
14
14
|
module Lint
|
15
15
|
module Tests
|
16
|
+
|
17
|
+
# == Responds to <tt>to_key</tt>
|
18
|
+
#
|
19
|
+
# Returns an Enumerable of all (primary) key attributes
|
20
|
+
# or nil if model.persisted? is false
|
21
|
+
def test_to_key
|
22
|
+
assert model.respond_to?(:to_key), "The model should respond to to_key"
|
23
|
+
def model.persisted?() false end
|
24
|
+
assert model.to_key.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
# == Responds to <tt>to_param</tt>
|
28
|
+
#
|
29
|
+
# Returns a string representing the object's key suitable for use in URLs
|
30
|
+
# or nil if model.persisted? is false.
|
31
|
+
#
|
32
|
+
# Implementers can decide to either raise an exception or provide a default
|
33
|
+
# in case the record uses a composite primary key. There are no tests for this
|
34
|
+
# behavior in lint because it doesn't make sense to force any of the possible
|
35
|
+
# implementation strategies on the implementer. However, if the resource is
|
36
|
+
# not persisted?, then to_param should always return nil.
|
37
|
+
def test_to_param
|
38
|
+
assert model.respond_to?(:to_param), "The model should respond to to_param"
|
39
|
+
def model.persisted?() false end
|
40
|
+
assert model.to_param.nil?
|
41
|
+
end
|
42
|
+
|
16
43
|
# == Responds to <tt>valid?</tt>
|
17
44
|
#
|
18
45
|
# Returns a boolean that specifies whether the object is in a valid or invalid
|
@@ -22,21 +49,16 @@ module ActiveModel
|
|
22
49
|
assert_boolean model.valid?, "valid?"
|
23
50
|
end
|
24
51
|
|
25
|
-
# == Responds to <tt>
|
52
|
+
# == Responds to <tt>persisted?</tt>
|
26
53
|
#
|
27
54
|
# Returns a boolean that specifies whether the object has been persisted yet.
|
28
55
|
# This is used when calculating the URL for an object. If the object is
|
29
56
|
# not persisted, a form for that object, for instance, will be POSTed to the
|
30
57
|
# collection. If it is persisted, a form for the object will put PUTed to the
|
31
58
|
# URL for the object.
|
32
|
-
def
|
33
|
-
assert model.respond_to?(:
|
34
|
-
assert_boolean model.
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_destroyed?
|
38
|
-
assert model.respond_to?(:destroyed?), "The model should respond to destroyed?"
|
39
|
-
assert_boolean model.destroyed?, "destroyed?"
|
59
|
+
def test_persisted?
|
60
|
+
assert model.respond_to?(:persisted?), "The model should respond to persisted?"
|
61
|
+
assert_boolean model.persisted?, "persisted?"
|
40
62
|
end
|
41
63
|
|
42
64
|
# == Naming
|
data/lib/active_model/naming.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
3
|
module ActiveModel
|
4
|
+
|
4
5
|
class Name < String
|
5
6
|
attr_reader :singular, :plural, :element, :collection, :partial_path
|
6
7
|
alias_method :cache_key, :collection
|
@@ -41,7 +42,7 @@ module ActiveModel
|
|
41
42
|
# To implement, just extend ActiveModel::Naming in your object:
|
42
43
|
#
|
43
44
|
# class BookCover
|
44
|
-
#
|
45
|
+
# extend ActiveModel::Naming
|
45
46
|
# end
|
46
47
|
#
|
47
48
|
# BookCover.model_name #=> "BookCover"
|
@@ -57,4 +58,5 @@ module ActiveModel
|
|
57
58
|
@_model_name ||= ActiveModel::Name.new(self)
|
58
59
|
end
|
59
60
|
end
|
61
|
+
|
60
62
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
1
2
|
require 'active_support/core_ext/class/attribute_accessors'
|
2
3
|
require 'active_support/core_ext/hash/conversions'
|
3
4
|
|
@@ -85,8 +86,8 @@ module ActiveModel
|
|
85
86
|
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
|
86
87
|
end
|
87
88
|
|
88
|
-
# To replicate the behavior in ActiveRecord#attributes,
|
89
|
-
#
|
89
|
+
# To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
|
90
|
+
# takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
|
90
91
|
# for a N level model but is set for the N+1 level models,
|
91
92
|
# then because <tt>:except</tt> is set to a default value, the second
|
92
93
|
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
|
@@ -108,7 +109,7 @@ module ActiveModel
|
|
108
109
|
end
|
109
110
|
|
110
111
|
def serializable_method_attributes
|
111
|
-
Array(options[:methods]).inject([]) do |methods, name|
|
112
|
+
Array.wrap(options[:methods]).inject([]) do |methods, name|
|
112
113
|
methods << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
|
113
114
|
methods
|
114
115
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/array/extract_options'
|
2
|
+
require 'active_support/core_ext/array/wrap'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
2
4
|
require 'active_support/core_ext/hash/keys'
|
3
5
|
require 'active_model/errors'
|
4
6
|
|
@@ -45,6 +47,9 @@ module ActiveModel
|
|
45
47
|
included do
|
46
48
|
extend ActiveModel::Translation
|
47
49
|
define_callbacks :validate, :scope => :name
|
50
|
+
|
51
|
+
class_attribute :_validators
|
52
|
+
self._validators = Hash.new { |h,k| h[k] = [] }
|
48
53
|
end
|
49
54
|
|
50
55
|
module ClassMethods
|
@@ -108,21 +113,31 @@ module ActiveModel
|
|
108
113
|
# end
|
109
114
|
# end
|
110
115
|
#
|
111
|
-
# This usage applies to +validate_on_create+ and +validate_on_update as well+.
|
112
116
|
def validate(*args, &block)
|
113
117
|
options = args.last
|
114
118
|
if options.is_a?(Hash) && options.key?(:on)
|
115
|
-
options[:if] = Array(options[:if])
|
119
|
+
options[:if] = Array.wrap(options[:if])
|
116
120
|
options[:if] << "@_on_validate == :#{options[:on]}"
|
117
121
|
end
|
118
122
|
set_callback(:validate, *args, &block)
|
119
123
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
124
|
+
|
125
|
+
# List all validators that being used to validate the model using +validates_with+
|
126
|
+
# method.
|
127
|
+
def validators
|
128
|
+
_validators.values.flatten.uniq
|
129
|
+
end
|
130
|
+
|
131
|
+
# List all validators that being used to validate a specific attribute.
|
132
|
+
def validators_on(attribute)
|
133
|
+
_validators[attribute.to_sym]
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
123
138
|
def _merge_attributes(attr_names)
|
124
139
|
options = attr_names.extract_options!
|
125
|
-
options.merge(:attributes => attr_names)
|
140
|
+
options.merge(:attributes => attr_names.flatten)
|
126
141
|
end
|
127
142
|
end
|
128
143
|
|
@@ -4,7 +4,7 @@ module ActiveModel
|
|
4
4
|
def check_validity!
|
5
5
|
raise ArgumentError, "An object with the method include? is required must be supplied as the " <<
|
6
6
|
":in option of the configuration hash" unless options[:in].respond_to?(:include?)
|
7
|
-
|
7
|
+
end
|
8
8
|
|
9
9
|
def validate_each(record, attribute, value)
|
10
10
|
return if options[:in].include?(value)
|
@@ -62,6 +62,15 @@ module ActiveModel
|
|
62
62
|
args.each do |klass|
|
63
63
|
validator = klass.new(options, &block)
|
64
64
|
validator.setup(self) if validator.respond_to?(:setup)
|
65
|
+
|
66
|
+
if validator.respond_to?(:attributes) && !validator.attributes.empty?
|
67
|
+
validator.attributes.each do |attribute|
|
68
|
+
_validators[attribute.to_sym] << validator
|
69
|
+
end
|
70
|
+
else
|
71
|
+
_validators[nil] << validator
|
72
|
+
end
|
73
|
+
|
65
74
|
validate(validator, options)
|
66
75
|
end
|
67
76
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
require "active_support/core_ext/module/anonymous"
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
|
1
5
|
module ActiveModel #:nodoc:
|
2
6
|
# A simple base class that can be used along with
|
3
7
|
# +ActiveModel::Validations::ClassMethods.validates_with+
|
@@ -88,11 +92,27 @@ module ActiveModel #:nodoc:
|
|
88
92
|
class Validator
|
89
93
|
attr_reader :options
|
90
94
|
|
95
|
+
# Returns the kind of the validator.
|
96
|
+
#
|
97
|
+
# == Examples
|
98
|
+
#
|
99
|
+
# PresenceValidator.kind #=> :presence
|
100
|
+
# UniquenessValidator.kind #=> :uniqueness
|
101
|
+
#
|
102
|
+
def self.kind
|
103
|
+
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
|
104
|
+
end
|
105
|
+
|
91
106
|
# Accepts options that will be made availible through the +options+ reader.
|
92
107
|
def initialize(options)
|
93
108
|
@options = options
|
94
109
|
end
|
95
110
|
|
111
|
+
# Return the kind for this validator.
|
112
|
+
def kind
|
113
|
+
self.class.kind
|
114
|
+
end
|
115
|
+
|
96
116
|
# Override this method in subclasses with validation logic, adding errors
|
97
117
|
# to the records +errors+ array where necessary.
|
98
118
|
def validate(record)
|
@@ -112,7 +132,7 @@ module ActiveModel #:nodoc:
|
|
112
132
|
# +options+ reader, however the <tt>:attributes</tt> option will be removed
|
113
133
|
# and instead be made available through the +attributes+ reader.
|
114
134
|
def initialize(options)
|
115
|
-
@attributes = Array(options.delete(:attributes))
|
135
|
+
@attributes = Array.wrap(options.delete(:attributes))
|
116
136
|
raise ":attributes cannot be blank" if @attributes.empty?
|
117
137
|
super
|
118
138
|
check_validity!
|
data/lib/active_model/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activemodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 3
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- beta2
|
10
|
+
version: 3.0.0.beta2
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- David Heinemeier Hansson
|
@@ -9,20 +15,25 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-04-01 00:00:00 -07:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: activesupport
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
25
|
requirements:
|
21
26
|
- - "="
|
22
27
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
segments:
|
29
|
+
- 3
|
30
|
+
- 0
|
31
|
+
- 0
|
32
|
+
- beta2
|
33
|
+
version: 3.0.0.beta2
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
description: A toolkit for building modeling frameworks like Active Record and Active Resource. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.
|
26
37
|
email: david@loudthinking.com
|
27
38
|
executables: []
|
28
39
|
|
@@ -77,20 +88,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
88
|
requirements:
|
78
89
|
- - ">="
|
79
90
|
- !ruby/object:Gem::Version
|
80
|
-
|
81
|
-
|
91
|
+
segments:
|
92
|
+
- 1
|
93
|
+
- 8
|
94
|
+
- 7
|
95
|
+
version: 1.8.7
|
82
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
97
|
requirements:
|
84
98
|
- - ">"
|
85
99
|
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 1
|
102
|
+
- 3
|
103
|
+
- 1
|
86
104
|
version: 1.3.1
|
87
|
-
version:
|
88
105
|
requirements: []
|
89
106
|
|
90
107
|
rubyforge_project: activemodel
|
91
|
-
rubygems_version: 1.3.
|
108
|
+
rubygems_version: 1.3.6
|
92
109
|
signing_key:
|
93
110
|
specification_version: 3
|
94
|
-
summary: A toolkit for building
|
111
|
+
summary: A toolkit for building modeling frameworks (part of Rails).
|
95
112
|
test_files: []
|
96
113
|
|