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