rails-observers 0.1.0
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/.gitignore +17 -0
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/README.md +102 -0
- data/Rakefile +34 -0
- data/lib/generators/active_record/observer/observer_generator.rb +17 -0
- data/lib/generators/active_record/observer/templates/observer.rb +4 -0
- data/lib/generators/rails/observer/USAGE +12 -0
- data/lib/generators/rails/observer/observer_generator.rb +7 -0
- data/lib/generators/test_unit/observer/observer_generator.rb +15 -0
- data/lib/generators/test_unit/observer/templates/unit_test.rb +9 -0
- data/lib/rails-observers.rb +30 -0
- data/lib/rails/observers/action_controller/caching.rb +12 -0
- data/lib/rails/observers/action_controller/caching/sweeping.rb +113 -0
- data/lib/rails/observers/active_model/active_model.rb +4 -0
- data/lib/rails/observers/active_model/observer_array.rb +152 -0
- data/lib/rails/observers/active_model/observing.rb +374 -0
- data/lib/rails/observers/activerecord/active_record.rb +5 -0
- data/lib/rails/observers/activerecord/base.rb +8 -0
- data/lib/rails/observers/activerecord/observer.rb +125 -0
- data/lib/rails/observers/version.rb +5 -0
- data/rails-observers.gemspec +26 -0
- data/test/configuration_test.rb +37 -0
- data/test/console_test.rb +38 -0
- data/test/fixtures/developers.yml +4 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/topics.yml +41 -0
- data/test/generators/generators_test_helper.rb +16 -0
- data/test/generators/namespaced_generators_test.rb +34 -0
- data/test/generators/observer_generator_test.rb +33 -0
- data/test/helper.rb +74 -0
- data/test/isolation/abstract_unit.rb +108 -0
- data/test/lifecycle_test.rb +249 -0
- data/test/models/observers.rb +27 -0
- data/test/observer_array_test.rb +222 -0
- data/test/observing_test.rb +183 -0
- data/test/rake_test.rb +40 -0
- data/test/sweeper_test.rb +83 -0
- data/test/transaction_callbacks_test.rb +278 -0
- metadata +216 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record-observers.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'activerecord-deprecated_finders', git: 'git://github.com/rails/activerecord-deprecated_finders'
|
7
|
+
gem 'journey', git: 'https://github.com/rails/journey.git'
|
8
|
+
|
9
|
+
gem 'rails', git: 'git://github.com/rafaelfranca/rails', branch: 'extract_observers'
|
10
|
+
|
11
|
+
gem 'mocha', require: false
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Steve Klabnik, Rafael Mendonça França
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Rails::Observers
|
2
|
+
|
3
|
+
Rails Observers (removed from core in Rails 4.0)
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rails-observers'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rails-observers
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
This gem contains two observers:
|
22
|
+
|
23
|
+
* Active Record Observer
|
24
|
+
* Action Controller Sweeper
|
25
|
+
|
26
|
+
### Active Record Observer
|
27
|
+
|
28
|
+
Observer classes respond to life cycle callbacks to implement trigger-like
|
29
|
+
behavior outside the original class. This is a great way to reduce the
|
30
|
+
clutter that normally comes when the model class is burdened with
|
31
|
+
functionality that doesn't pertain to the core responsibility of the
|
32
|
+
class. Example:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
class CommentObserver < ActiveRecord::Observer
|
36
|
+
def after_save(comment)
|
37
|
+
Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
This Observer sends an email when a Comment#save is finished.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class ContactObserver < ActiveRecord::Observer
|
46
|
+
def after_create(contact)
|
47
|
+
contact.logger.info('New contact added!')
|
48
|
+
end
|
49
|
+
|
50
|
+
def after_destroy(contact)
|
51
|
+
contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
This Observer uses logger to log when specific callbacks are triggered.
|
57
|
+
|
58
|
+
### Action Controller Sweeper
|
59
|
+
|
60
|
+
Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
|
61
|
+
They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
class ListSweeper < ActionController::Caching::Sweeper
|
65
|
+
observe List, Item
|
66
|
+
|
67
|
+
def after_save(record)
|
68
|
+
list = record.is_a?(List) ? record : record.list
|
69
|
+
expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
|
70
|
+
expire_action(:controller => "lists", :action => "all")
|
71
|
+
list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
The sweeper is assigned in the controllers that wish to have its job performed using the `cache_sweeper` class method:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class ListsController < ApplicationController
|
80
|
+
caches_action :index, :show, :public, :feed
|
81
|
+
cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
In the example above, four actions are cached and three actions are responsible for expiring those caches.
|
86
|
+
|
87
|
+
You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
class ListsController < ApplicationController
|
91
|
+
caches_action :index, :show, :public, :feed
|
92
|
+
cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
## Contributing
|
97
|
+
|
98
|
+
1. Fork it
|
99
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
100
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
101
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
102
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
Rake::TestTask.new("test:regular") do |t|
|
7
|
+
t.libs = ["test"]
|
8
|
+
t.pattern = "test/*_test.rb"
|
9
|
+
t.ruby_opts = ['-w']
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new("test:generators") do |t|
|
13
|
+
t.libs = ["test"]
|
14
|
+
t.pattern = "test/generators/*_test.rb"
|
15
|
+
t.ruby_opts = ['-w']
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => :test
|
19
|
+
task :test => ['test:regular', 'test:generators']
|
20
|
+
|
21
|
+
specname = "rails-observers.gemspec"
|
22
|
+
deps = `git ls-files`.split("\n") - [specname]
|
23
|
+
|
24
|
+
file specname => deps do
|
25
|
+
files = `git ls-files`.split("\n")
|
26
|
+
test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
|
29
|
+
require 'erb'
|
30
|
+
|
31
|
+
File.open specname, 'w:utf-8' do |f|
|
32
|
+
f.write ERB.new(File.read("#{specname}.erb")).result(binding)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Generators
|
5
|
+
class ObserverGenerator < Base
|
6
|
+
check_class_collision :suffix => "Observer"
|
7
|
+
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
def create_observer_file
|
11
|
+
template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb")
|
12
|
+
end
|
13
|
+
|
14
|
+
hook_for :test_framework
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Description:
|
2
|
+
Stubs out a new observer. Pass the observer name, either CamelCased or
|
3
|
+
under_scored, as an argument.
|
4
|
+
|
5
|
+
This generator only invokes your ORM and test framework generators.
|
6
|
+
|
7
|
+
Example:
|
8
|
+
`rails generate observer Account`
|
9
|
+
|
10
|
+
For ActiveRecord and TestUnit it creates:
|
11
|
+
Observer: app/models/account_observer.rb
|
12
|
+
TestUnit: test/unit/account_observer_test.rb
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators/test_unit'
|
2
|
+
|
3
|
+
module TestUnit
|
4
|
+
module Generators
|
5
|
+
class ObserverGenerator < Base
|
6
|
+
check_class_collision :suffix => "ObserverTest"
|
7
|
+
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
def create_test_files
|
11
|
+
template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'rails/observers/version'
|
3
|
+
|
4
|
+
module Rails
|
5
|
+
module Observers
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
initializer "active_record.observer", :before => "active_record.set_configs" do
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
9
|
+
require "rails/observers/activerecord/active_record"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer "action_controller.caching.sweppers" do
|
14
|
+
ActiveSupport.on_load(:action_controller) do
|
15
|
+
require "rails/observers/action_controller/caching"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
config.after_initialize do |app|
|
20
|
+
ActiveSupport.on_load(:active_record) do
|
21
|
+
ActiveRecord::Base.instantiate_observers
|
22
|
+
|
23
|
+
ActionDispatch::Reloader.to_prepare do
|
24
|
+
ActiveRecord::Base.instantiate_observers
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
module Caching
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
eager_autoload do
|
6
|
+
autoload :Sweeper, 'rails/observers/action_controller/caching/sweeping'
|
7
|
+
autoload :Sweeping, 'rails/observers/action_controller/caching/sweeping'
|
8
|
+
end
|
9
|
+
|
10
|
+
include Sweeping if defined?(ActiveRecord)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
module Caching
|
3
|
+
# Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
|
4
|
+
# They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
|
5
|
+
#
|
6
|
+
# class ListSweeper < ActionController::Caching::Sweeper
|
7
|
+
# observe List, Item
|
8
|
+
#
|
9
|
+
# def after_save(record)
|
10
|
+
# list = record.is_a?(List) ? record : record.list
|
11
|
+
# expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
|
12
|
+
# expire_action(:controller => "lists", :action => "all")
|
13
|
+
# list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
|
18
|
+
#
|
19
|
+
# class ListsController < ApplicationController
|
20
|
+
# caches_action :index, :show, :public, :feed
|
21
|
+
# cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
|
25
|
+
#
|
26
|
+
# You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
|
27
|
+
#
|
28
|
+
# class ListsController < ApplicationController
|
29
|
+
# caches_action :index, :show, :public, :feed
|
30
|
+
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
|
31
|
+
# end
|
32
|
+
module Sweeping
|
33
|
+
extend ActiveSupport::Concern
|
34
|
+
|
35
|
+
module ClassMethods #:nodoc:
|
36
|
+
def cache_sweeper(*sweepers)
|
37
|
+
configuration = sweepers.extract_options!
|
38
|
+
|
39
|
+
sweepers.each do |sweeper|
|
40
|
+
ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
|
41
|
+
sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
|
42
|
+
|
43
|
+
if sweeper_instance.is_a?(Sweeper)
|
44
|
+
around_filter(sweeper_instance, :only => configuration[:only])
|
45
|
+
else
|
46
|
+
after_filter(sweeper_instance, :only => configuration[:only])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
|
54
|
+
class Sweeper < ActiveRecord::Observer #:nodoc:
|
55
|
+
attr_accessor :controller
|
56
|
+
|
57
|
+
def initialize(*args)
|
58
|
+
super
|
59
|
+
@controller = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def before(controller)
|
63
|
+
self.controller = controller
|
64
|
+
callback(:before) if controller.perform_caching
|
65
|
+
true # before method from sweeper should always return true
|
66
|
+
end
|
67
|
+
|
68
|
+
def after(controller)
|
69
|
+
self.controller = controller
|
70
|
+
callback(:after) if controller.perform_caching
|
71
|
+
end
|
72
|
+
|
73
|
+
def around(controller)
|
74
|
+
before(controller)
|
75
|
+
yield
|
76
|
+
after(controller)
|
77
|
+
ensure
|
78
|
+
clean_up
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
# gets the action cache path for the given options.
|
83
|
+
def action_path_for(options)
|
84
|
+
Actions::ActionCachePath.new(controller, options).path
|
85
|
+
end
|
86
|
+
|
87
|
+
# Retrieve instance variables set in the controller.
|
88
|
+
def assigns(key)
|
89
|
+
controller.instance_variable_get("@#{key}")
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def clean_up
|
94
|
+
# Clean up, so that the controller can be collected after this request
|
95
|
+
self.controller = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def callback(timing)
|
99
|
+
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
|
100
|
+
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
|
101
|
+
|
102
|
+
__send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
|
103
|
+
__send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
|
104
|
+
end
|
105
|
+
|
106
|
+
def method_missing(method, *arguments, &block)
|
107
|
+
return super unless @controller
|
108
|
+
@controller.__send__(method, *arguments, &block)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,152 @@
|
|
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) #:nodoc:
|
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
|
+
# +false+ otherwise.
|
15
|
+
def disabled_for?(observer) #:nodoc:
|
16
|
+
disabled_observers.include?(observer.class)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Disables one or more observers. This supports multiple forms:
|
20
|
+
#
|
21
|
+
# ORM.observers.disable :all
|
22
|
+
# # => disables all observers for all models subclassed from
|
23
|
+
# # an ORM base class that includes ActiveModel::Observing
|
24
|
+
# # e.g. ActiveRecord::Base
|
25
|
+
#
|
26
|
+
# ORM.observers.disable :user_observer
|
27
|
+
# # => disables the UserObserver
|
28
|
+
#
|
29
|
+
# User.observers.disable AuditTrail
|
30
|
+
# # => disables the AuditTrail observer for User notifications.
|
31
|
+
# # Other models will still notify the AuditTrail observer.
|
32
|
+
#
|
33
|
+
# ORM.observers.disable :observer_1, :observer_2
|
34
|
+
# # => disables Observer1 and Observer2 for all models.
|
35
|
+
#
|
36
|
+
# User.observers.disable :all do
|
37
|
+
# # all user observers are disabled for
|
38
|
+
# # just the duration of the block
|
39
|
+
# end
|
40
|
+
def disable(*observers, &block)
|
41
|
+
set_enablement(false, observers, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Enables one or more observers. This supports multiple forms:
|
45
|
+
#
|
46
|
+
# ORM.observers.enable :all
|
47
|
+
# # => enables all observers for all models subclassed from
|
48
|
+
# # an ORM base class that includes ActiveModel::Observing
|
49
|
+
# # e.g. ActiveRecord::Base
|
50
|
+
#
|
51
|
+
# ORM.observers.enable :user_observer
|
52
|
+
# # => enables the UserObserver
|
53
|
+
#
|
54
|
+
# User.observers.enable AuditTrail
|
55
|
+
# # => enables the AuditTrail observer for User notifications.
|
56
|
+
# # Other models will not be affected (i.e. they will not
|
57
|
+
# # trigger notifications to AuditTrail if previously disabled)
|
58
|
+
#
|
59
|
+
# ORM.observers.enable :observer_1, :observer_2
|
60
|
+
# # => enables Observer1 and Observer2 for all models.
|
61
|
+
#
|
62
|
+
# User.observers.enable :all do
|
63
|
+
# # all user observers are enabled for
|
64
|
+
# # just the duration of the block
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Note: all observers are enabled by default. This method is only
|
68
|
+
# useful when you have previously disabled one or more observers.
|
69
|
+
def enable(*observers, &block)
|
70
|
+
set_enablement(true, observers, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
def disabled_observers #:nodoc:
|
76
|
+
@disabled_observers ||= Set.new
|
77
|
+
end
|
78
|
+
|
79
|
+
def observer_class_for(observer) #:nodoc:
|
80
|
+
return observer if observer.is_a?(Class)
|
81
|
+
|
82
|
+
if observer.respond_to?(:to_sym) # string/symbol
|
83
|
+
observer.to_s.camelize.constantize
|
84
|
+
else
|
85
|
+
raise ArgumentError, "#{observer} was not a class or a " +
|
86
|
+
"lowercase, underscored class name as expected."
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def start_transaction #:nodoc:
|
91
|
+
disabled_observer_stack.push(disabled_observers.dup)
|
92
|
+
each_subclass_array do |array|
|
93
|
+
array.start_transaction
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def disabled_observer_stack #:nodoc:
|
98
|
+
@disabled_observer_stack ||= []
|
99
|
+
end
|
100
|
+
|
101
|
+
def end_transaction #:nodoc:
|
102
|
+
@disabled_observers = disabled_observer_stack.pop
|
103
|
+
each_subclass_array do |array|
|
104
|
+
array.end_transaction
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def transaction #:nodoc:
|
109
|
+
start_transaction
|
110
|
+
|
111
|
+
begin
|
112
|
+
yield
|
113
|
+
ensure
|
114
|
+
end_transaction
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def each_subclass_array #:nodoc:
|
119
|
+
model_class.descendants.each do |subclass|
|
120
|
+
yield subclass.observers
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def set_enablement(enabled, observers) #:nodoc:
|
125
|
+
if block_given?
|
126
|
+
transaction do
|
127
|
+
set_enablement(enabled, observers)
|
128
|
+
yield
|
129
|
+
end
|
130
|
+
else
|
131
|
+
observers = ActiveModel::Observer.descendants if observers == [:all]
|
132
|
+
observers.each do |obs|
|
133
|
+
klass = observer_class_for(obs)
|
134
|
+
|
135
|
+
unless klass < ActiveModel::Observer
|
136
|
+
raise ArgumentError.new("#{obs} does not refer to a valid observer")
|
137
|
+
end
|
138
|
+
|
139
|
+
if enabled
|
140
|
+
disabled_observers.delete(klass)
|
141
|
+
else
|
142
|
+
disabled_observers << klass
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
each_subclass_array do |array|
|
147
|
+
array.set_enablement(enabled, observers)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|