activemodel 3.2.22.5 → 4.0.0.beta1

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -64
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +61 -24
  5. data/lib/active_model.rb +21 -11
  6. data/lib/active_model/attribute_methods.rb +150 -125
  7. data/lib/active_model/callbacks.rb +49 -34
  8. data/lib/active_model/conversion.rb +39 -19
  9. data/lib/active_model/deprecated_mass_assignment_security.rb +21 -0
  10. data/lib/active_model/dirty.rb +48 -32
  11. data/lib/active_model/errors.rb +176 -88
  12. data/lib/active_model/forbidden_attributes_protection.rb +27 -0
  13. data/lib/active_model/lint.rb +42 -55
  14. data/lib/active_model/locale/en.yml +3 -1
  15. data/lib/active_model/model.rb +97 -0
  16. data/lib/active_model/naming.rb +191 -51
  17. data/lib/active_model/railtie.rb +11 -1
  18. data/lib/active_model/secure_password.rb +55 -25
  19. data/lib/active_model/serialization.rb +51 -27
  20. data/lib/active_model/serializers/json.rb +83 -46
  21. data/lib/active_model/serializers/xml.rb +46 -12
  22. data/lib/active_model/test_case.rb +0 -12
  23. data/lib/active_model/translation.rb +9 -10
  24. data/lib/active_model/validations.rb +154 -52
  25. data/lib/active_model/validations/absence.rb +31 -0
  26. data/lib/active_model/validations/acceptance.rb +10 -22
  27. data/lib/active_model/validations/callbacks.rb +78 -25
  28. data/lib/active_model/validations/clusivity.rb +41 -0
  29. data/lib/active_model/validations/confirmation.rb +13 -23
  30. data/lib/active_model/validations/exclusion.rb +26 -55
  31. data/lib/active_model/validations/format.rb +44 -34
  32. data/lib/active_model/validations/inclusion.rb +22 -52
  33. data/lib/active_model/validations/length.rb +48 -49
  34. data/lib/active_model/validations/numericality.rb +30 -32
  35. data/lib/active_model/validations/presence.rb +12 -22
  36. data/lib/active_model/validations/validates.rb +68 -36
  37. data/lib/active_model/validations/with.rb +28 -23
  38. data/lib/active_model/validator.rb +22 -22
  39. data/lib/active_model/version.rb +4 -4
  40. metadata +23 -24
  41. data/lib/active_model/mass_assignment_security.rb +0 -237
  42. data/lib/active_model/mass_assignment_security/permission_set.rb +0 -40
  43. data/lib/active_model/mass_assignment_security/sanitizer.rb +0 -59
  44. data/lib/active_model/observer_array.rb +0 -147
  45. data/lib/active_model/observing.rb +0 -252
@@ -1,40 +0,0 @@
1
- require 'set'
2
-
3
- module ActiveModel
4
- module MassAssignmentSecurity
5
- class PermissionSet < Set
6
-
7
- def +(values)
8
- super(values.map(&:to_s))
9
- end
10
-
11
- def include?(key)
12
- super(remove_multiparameter_id(key))
13
- end
14
-
15
- def deny?(key)
16
- raise NotImplementedError, "#deny?(key) suppose to be overwritten"
17
- end
18
-
19
- protected
20
-
21
- def remove_multiparameter_id(key)
22
- key.to_s.gsub(/\(.+/m, '')
23
- end
24
- end
25
-
26
- class WhiteList < PermissionSet
27
-
28
- def deny?(key)
29
- !include?(key)
30
- end
31
- end
32
-
33
- class BlackList < PermissionSet
34
-
35
- def deny?(key)
36
- include?(key)
37
- end
38
- end
39
- end
40
- end
@@ -1,59 +0,0 @@
1
- require 'active_support/core_ext/module/delegation'
2
-
3
- module ActiveModel
4
- module MassAssignmentSecurity
5
- class Sanitizer
6
- def initialize(target=nil)
7
- end
8
-
9
- # Returns all attributes not denied by the authorizer.
10
- def sanitize(attributes, authorizer)
11
- sanitized_attributes = attributes.reject { |key, value| authorizer.deny?(key) }
12
- debug_protected_attribute_removal(attributes, sanitized_attributes)
13
- sanitized_attributes
14
- end
15
-
16
- protected
17
-
18
- def debug_protected_attribute_removal(attributes, sanitized_attributes)
19
- removed_keys = attributes.keys - sanitized_attributes.keys
20
- process_removed_attributes(removed_keys) if removed_keys.any?
21
- end
22
-
23
- def process_removed_attributes(attrs)
24
- raise NotImplementedError, "#process_removed_attributes(attrs) suppose to be overwritten"
25
- end
26
- end
27
-
28
- class LoggerSanitizer < Sanitizer
29
- delegate :logger, :to => :@target
30
-
31
- def initialize(target)
32
- @target = target
33
- super
34
- end
35
-
36
- def logger?
37
- @target.respond_to?(:logger) && @target.logger
38
- end
39
-
40
- def process_removed_attributes(attrs)
41
- logger.debug "WARNING: Can't mass-assign protected attributes: #{attrs.join(', ')}" if logger?
42
- end
43
- end
44
-
45
- class StrictSanitizer < Sanitizer
46
- def process_removed_attributes(attrs)
47
- return if (attrs - insensitive_attributes).empty?
48
- raise ActiveModel::MassAssignmentSecurity::Error, "Can't mass-assign protected attributes: #{attrs.join(', ')}"
49
- end
50
-
51
- def insensitive_attributes
52
- ['id']
53
- end
54
- end
55
-
56
- class Error < StandardError
57
- end
58
- end
59
- end
@@ -1,147 +0,0 @@
1
- require 'set'
2
-
3
- module ActiveModel
4
- # Stores the enabled/disabled state of individual observers for
5
- # a particular model class.
6
- class ObserverArray < Array
7
- attr_reader :model_class
8
- def initialize(model_class, *args)
9
- @model_class = model_class
10
- super(*args)
11
- end
12
-
13
- # Returns true if the given observer is disabled for the model class.
14
- def disabled_for?(observer)
15
- disabled_observers.include?(observer.class)
16
- end
17
-
18
- # Disables one or more observers. This supports multiple forms:
19
- #
20
- # ORM.observers.disable :user_observer
21
- # # => disables the UserObserver
22
- #
23
- # User.observers.disable AuditTrail
24
- # # => disables the AuditTrail observer for User notifications.
25
- # # Other models will still notify the AuditTrail observer.
26
- #
27
- # ORM.observers.disable :observer_1, :observer_2
28
- # # => disables Observer1 and Observer2 for all models.
29
- #
30
- # ORM.observers.disable :all
31
- # # => disables all observers for all models.
32
- #
33
- # User.observers.disable :all do
34
- # # all user observers are disabled for
35
- # # just the duration of the block
36
- # end
37
- def disable(*observers, &block)
38
- set_enablement(false, observers, &block)
39
- end
40
-
41
- # Enables one or more observers. This supports multiple forms:
42
- #
43
- # ORM.observers.enable :user_observer
44
- # # => enables the UserObserver
45
- #
46
- # User.observers.enable AuditTrail
47
- # # => enables the AuditTrail observer for User notifications.
48
- # # Other models will not be affected (i.e. they will not
49
- # # trigger notifications to AuditTrail if previously disabled)
50
- #
51
- # ORM.observers.enable :observer_1, :observer_2
52
- # # => enables Observer1 and Observer2 for all models.
53
- #
54
- # ORM.observers.enable :all
55
- # # => enables all observers for all models.
56
- #
57
- # User.observers.enable :all do
58
- # # all user observers are enabled for
59
- # # just the duration of the block
60
- # end
61
- #
62
- # Note: all observers are enabled by default. This method is only
63
- # useful when you have previously disabled one or more observers.
64
- def enable(*observers, &block)
65
- set_enablement(true, observers, &block)
66
- end
67
-
68
- protected
69
-
70
- def disabled_observers
71
- @disabled_observers ||= Set.new
72
- end
73
-
74
- def observer_class_for(observer)
75
- return observer if observer.is_a?(Class)
76
-
77
- if observer.respond_to?(:to_sym) # string/symbol
78
- observer.to_s.camelize.constantize
79
- else
80
- raise ArgumentError, "#{observer} was not a class or a " +
81
- "lowercase, underscored class name as expected."
82
- end
83
- end
84
-
85
- def start_transaction
86
- disabled_observer_stack.push(disabled_observers.dup)
87
- each_subclass_array do |array|
88
- array.start_transaction
89
- end
90
- end
91
-
92
- def disabled_observer_stack
93
- @disabled_observer_stack ||= []
94
- end
95
-
96
- def end_transaction
97
- @disabled_observers = disabled_observer_stack.pop
98
- each_subclass_array do |array|
99
- array.end_transaction
100
- end
101
- end
102
-
103
- def transaction
104
- start_transaction
105
-
106
- begin
107
- yield
108
- ensure
109
- end_transaction
110
- end
111
- end
112
-
113
- def each_subclass_array
114
- model_class.descendants.each do |subclass|
115
- yield subclass.observers
116
- end
117
- end
118
-
119
- def set_enablement(enabled, observers)
120
- if block_given?
121
- transaction do
122
- set_enablement(enabled, observers)
123
- yield
124
- end
125
- else
126
- observers = ActiveModel::Observer.descendants if observers == [:all]
127
- observers.each do |obs|
128
- klass = observer_class_for(obs)
129
-
130
- unless klass < ActiveModel::Observer
131
- raise ArgumentError.new("#{obs} does not refer to a valid observer")
132
- end
133
-
134
- if enabled
135
- disabled_observers.delete(klass)
136
- else
137
- disabled_observers << klass
138
- end
139
- end
140
-
141
- each_subclass_array do |array|
142
- array.set_enablement(enabled, observers)
143
- end
144
- end
145
- end
146
- end
147
- end
@@ -1,252 +0,0 @@
1
- require 'singleton'
2
- require 'active_model/observer_array'
3
- require 'active_support/core_ext/array/wrap'
4
- require 'active_support/core_ext/module/aliasing'
5
- require 'active_support/core_ext/module/remove_method'
6
- require 'active_support/core_ext/string/inflections'
7
- require 'active_support/core_ext/enumerable'
8
- require 'active_support/descendants_tracker'
9
-
10
- module ActiveModel
11
- module Observing
12
- extend ActiveSupport::Concern
13
-
14
- included do
15
- extend ActiveSupport::DescendantsTracker
16
- end
17
-
18
- module ClassMethods
19
- # == Active Model Observers Activation
20
- #
21
- # Activates the observers assigned. Examples:
22
- #
23
- # class ORM
24
- # include ActiveModel::Observing
25
- # end
26
- #
27
- # # Calls PersonObserver.instance
28
- # ORM.observers = :person_observer
29
- #
30
- # # Calls Cacher.instance and GarbageCollector.instance
31
- # ORM.observers = :cacher, :garbage_collector
32
- #
33
- # # Same as above, just using explicit class references
34
- # ORM.observers = Cacher, GarbageCollector
35
- #
36
- # Note: Setting this does not instantiate the observers yet.
37
- # +instantiate_observers+ is called during startup, and before
38
- # each development request.
39
- def observers=(*values)
40
- observers.replace(values.flatten)
41
- end
42
-
43
- # Gets an array of observers observing this model.
44
- # The array also provides +enable+ and +disable+ methods
45
- # that allow you to selectively enable and disable observers.
46
- # (see <tt>ActiveModel::ObserverArray.enable</tt> and
47
- # <tt>ActiveModel::ObserverArray.disable</tt> for more on this)
48
- def observers
49
- @observers ||= ObserverArray.new(self)
50
- end
51
-
52
- # Gets the current observer instances.
53
- def observer_instances
54
- @observer_instances ||= []
55
- end
56
-
57
- # Instantiate the global observers.
58
- def instantiate_observers
59
- observers.each { |o| instantiate_observer(o) }
60
- end
61
-
62
- # Add a new observer to the pool.
63
- # The new observer needs to respond to 'update', otherwise it
64
- # raises an +ArgumentError+ exception.
65
- def add_observer(observer)
66
- unless observer.respond_to? :update
67
- raise ArgumentError, "observer needs to respond to `update'"
68
- end
69
- observer_instances << observer
70
- end
71
-
72
- # Notify list of observers of a change.
73
- def notify_observers(*arg)
74
- observer_instances.each { |observer| observer.update(*arg) }
75
- end
76
-
77
- # Total number of observers.
78
- def count_observers
79
- observer_instances.size
80
- end
81
-
82
- protected
83
- def instantiate_observer(observer) #:nodoc:
84
- # string/symbol
85
- if observer.respond_to?(:to_sym)
86
- observer.to_s.camelize.constantize.instance
87
- elsif observer.respond_to?(:instance)
88
- observer.instance
89
- else
90
- raise ArgumentError,
91
- "#{observer} must be a lowercase, underscored class name (or an " +
92
- "instance of the class itself) responding to the instance " +
93
- "method. Example: Person.observers = :big_brother # calls " +
94
- "BigBrother.instance"
95
- end
96
- end
97
-
98
- # Notify observers when the observed class is subclassed.
99
- def inherited(subclass)
100
- super
101
- notify_observers :observed_class_inherited, subclass
102
- end
103
- end
104
-
105
- private
106
- # Fires notifications to model's observers
107
- #
108
- # def save
109
- # notify_observers(:before_save)
110
- # ...
111
- # notify_observers(:after_save)
112
- # end
113
- def notify_observers(method)
114
- self.class.notify_observers(method, self)
115
- end
116
- end
117
-
118
- # == Active Model Observers
119
- #
120
- # Observer classes respond to life cycle callbacks to implement trigger-like
121
- # behavior outside the original class. This is a great way to reduce the
122
- # clutter that normally comes when the model class is burdened with
123
- # functionality that doesn't pertain to the core responsibility of the
124
- # class. Example:
125
- #
126
- # class CommentObserver < ActiveModel::Observer
127
- # def after_save(comment)
128
- # Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
129
- # end
130
- # end
131
- #
132
- # This Observer sends an email when a Comment#save is finished.
133
- #
134
- # class ContactObserver < ActiveModel::Observer
135
- # def after_create(contact)
136
- # contact.logger.info('New contact added!')
137
- # end
138
- #
139
- # def after_destroy(contact)
140
- # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
141
- # end
142
- # end
143
- #
144
- # This Observer uses logger to log when specific callbacks are triggered.
145
- #
146
- # == Observing a class that can't be inferred
147
- #
148
- # Observers will by default be mapped to the class with which they share a
149
- # name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
150
- # to ProductManager, and so on. If you want to name your observer differently than
151
- # the class you're interested in observing, you can use the <tt>Observer.observe</tt>
152
- # class method which takes either the concrete class (Product) or a symbol for that
153
- # class (:product):
154
- #
155
- # class AuditObserver < ActiveModel::Observer
156
- # observe :account
157
- #
158
- # def after_update(account)
159
- # AuditTrail.new(account, "UPDATED")
160
- # end
161
- # end
162
- #
163
- # If the audit observer needs to watch more than one kind of object, this can be
164
- # specified with multiple arguments:
165
- #
166
- # class AuditObserver < ActiveModel::Observer
167
- # observe :account, :balance
168
- #
169
- # def after_update(record)
170
- # AuditTrail.new(record, "UPDATED")
171
- # end
172
- # end
173
- #
174
- # The AuditObserver will now act on both updates to Account and Balance by treating
175
- # them both as records.
176
- #
177
- # If you're using an Observer in a Rails application with Active Record, be sure to
178
- # read about the necessary configuration in the documentation for
179
- # ActiveRecord::Observer.
180
- #
181
- class Observer
182
- include Singleton
183
- extend ActiveSupport::DescendantsTracker
184
-
185
- class << self
186
- # Attaches the observer to the supplied model classes.
187
- def observe(*models)
188
- models.flatten!
189
- models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
190
- redefine_method(:observed_classes) { models }
191
- end
192
-
193
- # Returns an array of Classes to observe.
194
- #
195
- # You can override this instead of using the +observe+ helper.
196
- #
197
- # class AuditObserver < ActiveModel::Observer
198
- # def self.observed_classes
199
- # [Account, Balance]
200
- # end
201
- # end
202
- def observed_classes
203
- Array.wrap(observed_class)
204
- end
205
-
206
- # The class observed by default is inferred from the observer's class name:
207
- # assert_equal Person, PersonObserver.observed_class
208
- def observed_class
209
- if observed_class_name = name[/(.*)Observer/, 1]
210
- observed_class_name.constantize
211
- else
212
- nil
213
- end
214
- end
215
- end
216
-
217
- # Start observing the declared classes and their subclasses.
218
- def initialize
219
- observed_classes.each { |klass| add_observer!(klass) }
220
- end
221
-
222
- def observed_classes #:nodoc:
223
- self.class.observed_classes
224
- end
225
-
226
- # Send observed_method(object) if the method exists and
227
- # the observer is enabled for the given object's class.
228
- def update(observed_method, object, &block) #:nodoc:
229
- return unless respond_to?(observed_method)
230
- return if disabled_for?(object)
231
- send(observed_method, object, &block)
232
- end
233
-
234
- # Special method sent by the observed class when it is inherited.
235
- # Passes the new subclass.
236
- def observed_class_inherited(subclass) #:nodoc:
237
- self.class.observe(observed_classes + [subclass])
238
- add_observer!(subclass)
239
- end
240
-
241
- protected
242
- def add_observer!(klass) #:nodoc:
243
- klass.add_observer(self)
244
- end
245
-
246
- def disabled_for?(object)
247
- klass = object.class
248
- return false unless klass.respond_to?(:observers)
249
- klass.observers.disabled_for?(self)
250
- end
251
- end
252
- end