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