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 CHANGED
@@ -1,4 +1,30 @@
1
- *Edge*
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 = metaclass
90
- sing.send :alias_method, "original_#{name}", name
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 "def #{name}; #{value.to_s.inspect}; end"
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
- def #{matcher.method_name(attr_name)}(*args)
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
- # If your object is already designed to implement all of the Active Model featurs
3
- # include this module in your Class.
4
- #
5
- # class MyClass
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
- # Returns self to the <tt>:to_model</tt> method.
10
- #
11
- # If your model does not act like an Active Model object, then you should define
12
- # <tt>:to_model</tt> yourself returning a proxy object that wraps your object
13
- # with Active Model compliant methods.
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
@@ -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
- !changed_attributes.empty?
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({}) { |h, attr| h[attr] = attribute_change(attr); h }
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
- previously_changed_attributes
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
@@ -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
@@ -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>new_record?</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 test_new_record?
33
- assert model.respond_to?(:new_record?), "The model should respond to new_record?"
34
- assert_boolean model.new_record?, "new_record?"
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
@@ -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
- # exten ActiveModel::Naming
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
- # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
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
- private
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
- end
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!
@@ -2,8 +2,9 @@ module ActiveModel
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- TINY = "0.beta"
5
+ TINY = 0
6
+ BUILD = "beta2"
6
7
 
7
- STRING = [MAJOR, MINOR, TINY].join('.')
8
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
8
9
  end
9
10
  end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.beta
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-02-03 00:00:00 -08:00
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
- type: :runtime
18
- version_requirement:
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
- version: 3.0.0.beta
24
- version:
25
- description: Extracts common modeling concerns from ActiveRecord to share between similar frameworks like ActiveResource.
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
- version: "0"
81
- version:
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.5
108
+ rubygems_version: 1.3.6
92
109
  signing_key:
93
110
  specification_version: 3
94
- summary: A toolkit for building other modeling frameworks like ActiveRecord
111
+ summary: A toolkit for building modeling frameworks (part of Rails).
95
112
  test_files: []
96
113