rails-observers-hp 0.1.3

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