rails-observers-hp 0.1.3

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d9196f9e74b3a3508e653ea8934bd6b5e5ab01d
4
+ data.tar.gz: f2da542d96593554912bbb6f08a85790f400405f
5
+ SHA512:
6
+ metadata.gz: 7b6b2a02a1867f6c881cfb0da6b1871f0992c8eff6ec50a772c52f49650ba98998a78bf9baf7f86f65a3c83bc4e57138eefb89ba6a0210ea472a091df914618a
7
+ data.tar.gz: 2f02793a706d58f9178e6a4ba9cdacd5c4ae7dfd0c4c33c68a5385508b0b6516deb97814dbd664a782c9e0594522853b5c60371c681456c979a1294289a1b7a7
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012-2016 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,130 @@
1
+ [![Build Status](https://secure.travis-ci.org/rails/rails-observers.png)](https://travis-ci.org/rails/rails-observers)
2
+ # Rails::Observers
3
+
4
+ Rails Observers (removed from core in Rails 4.0)
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'rails-observers'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install rails-observers
19
+
20
+ ## Usage
21
+
22
+ This gem contains two observers:
23
+
24
+ * Active Record Observer
25
+ * Action Controller Sweeper
26
+
27
+ ### Active Record Observer
28
+
29
+ Observer classes respond to life cycle callbacks to implement trigger-like
30
+ behavior outside the original class. This is a great way to reduce the
31
+ clutter that normally comes when the model class is burdened with
32
+ functionality that doesn't pertain to the core responsibility of the
33
+ class. Observers are put in `app/models` (e.g.
34
+ `app/models/comment_observer.rb`). Example:
35
+
36
+ ```ruby
37
+ class CommentObserver < ActiveRecord::Observer
38
+ def after_save(comment)
39
+ Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
40
+ end
41
+ end
42
+ ```
43
+
44
+ This Observer sends an email when a Comment#save is finished.
45
+
46
+ ```ruby
47
+ class ContactObserver < ActiveRecord::Observer
48
+ def after_create(contact)
49
+ contact.logger.info('New contact added!')
50
+ end
51
+
52
+ def after_destroy(contact)
53
+ contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
54
+ end
55
+ end
56
+ ```
57
+
58
+ This Observer uses logger to log when specific callbacks are triggered.
59
+
60
+ The convention is to name observers after the class they observe. If you
61
+ absolutely need to override this, or want to use one observer for several
62
+ classes, use `observe`:
63
+
64
+ ```ruby
65
+ class NotificationsObserver < ActiveRecord::Observer
66
+ observe :comment, :like
67
+
68
+ def after_create(record)
69
+ # notifiy users of new comment or like
70
+ end
71
+
72
+ end
73
+ ```
74
+
75
+ Please note that observers are called in the order that they are defined. This means that callbacks in an observer
76
+ will always be called *after* callbacks defined in the model itself. Likewise, `has_one` and `has_many`
77
+ use callbacks to enforce `:dependent => :destroy`. Therefore, associated records will be destroyed before
78
+ the observer's `before_destroy` is called.
79
+
80
+ For an observer to be active, it must be registered first. This can be done by adding the following line into the `application.rb`:
81
+
82
+ config.active_record.observers = :contact_observer
83
+
84
+ Observers can also be registered on an environment-specific basis by simply using the corresponding environment's configuration file instead of `application.rb`.
85
+
86
+ ### Action Controller Sweeper
87
+
88
+ Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
89
+ They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
90
+
91
+ ```ruby
92
+ class ListSweeper < ActionController::Caching::Sweeper
93
+ observe List, Item
94
+
95
+ def after_save(record)
96
+ list = record.is_a?(List) ? record : record.list
97
+ expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
98
+ expire_action(:controller => "lists", :action => "all")
99
+ list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
100
+ end
101
+ end
102
+ ```
103
+
104
+ The sweeper is assigned in the controllers that wish to have its job performed using the `cache_sweeper` class method:
105
+
106
+ ```ruby
107
+ class ListsController < ApplicationController
108
+ caches_action :index, :show, :public, :feed
109
+ cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
110
+ end
111
+ ```
112
+
113
+ In the example above, four actions are cached and three actions are responsible for expiring those caches.
114
+
115
+ You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
116
+
117
+ ```ruby
118
+ class ListsController < ApplicationController
119
+ caches_action :index, :show, :public, :feed
120
+ cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
121
+ end
122
+ ```
123
+
124
+ ## Contributing
125
+
126
+ 1. Fork it
127
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
128
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
129
+ 4. Push to the branch (`git push origin my-new-feature`)
130
+ 5. Create new Pull Request
@@ -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,4 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Observer < ActiveRecord::Observer
3
+ end
4
+ <% 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,7 @@
1
+ module Rails
2
+ module Generators
3
+ class ObserverGenerator < NamedBase #metagenerator
4
+ hook_for :orm, :required => true
5
+ end
6
+ end
7
+ end
@@ -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,9 @@
1
+ require 'test_helper'
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %>ObserverTest < ActiveSupport::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ <% end -%>
@@ -0,0 +1,2 @@
1
+ require 'rails/observers/railtie' if defined? Rails
2
+ require 'rails/observers/version'
@@ -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/sweeper'
7
+ autoload :Sweeping, 'rails/observers/action_controller/caching/sweeping'
8
+ end
9
+
10
+ ActionController::Base.extend Sweeping::ClassMethods if defined?(ActiveRecord)
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ module ActionController #:nodoc:
2
+ module Caching
3
+ class Sweeper < ActiveRecord::Observer #:nodoc:
4
+ attr_accessor :controller
5
+
6
+ def initialize(*args)
7
+ super
8
+ @controller = nil
9
+ end
10
+
11
+ def before(controller)
12
+ self.controller = controller
13
+ callback(:before) if controller.perform_caching
14
+ true # before method from sweeper should always return true
15
+ end
16
+
17
+ def after(controller)
18
+ self.controller = controller
19
+ callback(:after) if controller.perform_caching
20
+ end
21
+
22
+ def around(controller)
23
+ before(controller)
24
+ yield
25
+ after(controller)
26
+ ensure
27
+ clean_up
28
+ end
29
+
30
+ protected
31
+ # gets the action cache path for the given options.
32
+ def action_path_for(options)
33
+ Actions::ActionCachePath.new(controller, options).path
34
+ end
35
+
36
+ # Retrieve instance variables set in the controller.
37
+ def assigns(key)
38
+ controller.instance_variable_get("@#{key}")
39
+ end
40
+
41
+ private
42
+ def clean_up
43
+ # Clean up, so that the controller can be collected after this request
44
+ self.controller = nil
45
+ end
46
+
47
+ def callback(timing)
48
+ controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
49
+ action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
50
+
51
+ __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
52
+ __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
53
+ end
54
+
55
+ def method_missing(method, *arguments, &block)
56
+ return super unless @controller
57
+ @controller.__send__(method, *arguments, &block)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,55 @@
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
+ module ClassMethods #:nodoc:
34
+ def cache_sweeper(*sweepers)
35
+ configuration = sweepers.extract_options!
36
+
37
+ sweepers.each do |sweeper|
38
+ ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
39
+ sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
40
+
41
+ if sweeper_instance.is_a?(Sweeper)
42
+ around_action(sweeper_instance, :only => configuration[:only])
43
+ else
44
+ after_action(sweeper_instance, :only => configuration[:only])
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
52
+ require 'rails/observers/action_controller/caching/sweeper'
53
+ end
54
+ end
55
+ end
@@ -0,0 +1 @@
1
+ require 'rails/observers/active_model/active_model'
@@ -0,0 +1,4 @@
1
+ module ActiveModel
2
+ autoload :Observer, 'rails/observers/active_model/observing'
3
+ autoload :Observing, 'rails/observers/active_model/observing'
4
+ 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