activemodel 3.2.22.5 → 4.0.0.beta1

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