active_model_listener 0.2.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/LICENSE +20 -0
- data/README.md +96 -0
- data/spec/active_model_listener_spec.rb +90 -0
- data/spec/spec_helper.rb +10 -0
- metadata +58 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Jeff Dean
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# ActiveModelListener
|
|
2
|
+
|
|
3
|
+
Simple, global ActiveRecord event observers, using a middleware architecture, that can easily be turned on and off. Designed for audit trails, activity feeds and other application-level event handlers.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
First, require Active Model Listener above your rails initializer:
|
|
8
|
+
|
|
9
|
+
# environment.rb
|
|
10
|
+
require 'active_model_listener'
|
|
11
|
+
Rails::Initializer.run do |config|
|
|
12
|
+
# ...
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Add the listeners to the ActiveModelListener in an initializer:
|
|
16
|
+
|
|
17
|
+
# config/initializers/active_model_listener.rb
|
|
18
|
+
ActiveModelListener.listeners << ActivityFeedListener
|
|
19
|
+
|
|
20
|
+
Then, create a listener class that defines methods for after_create, after_update and after_destroy:
|
|
21
|
+
|
|
22
|
+
class ActivityFeedListener
|
|
23
|
+
class << self
|
|
24
|
+
def after_create(record)
|
|
25
|
+
description = "#{record.class.name} was created"
|
|
26
|
+
publish_activity_feed_items record, description
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def after_update(record)
|
|
30
|
+
description = "#{record.class.name} was updated"
|
|
31
|
+
publish_activity_feed_items record, description
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def after_destroy(record)
|
|
35
|
+
description = "#{record.class.name} was deleted"
|
|
36
|
+
publish_activity_feed_items record, description
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def publish_activity_feed_items(record, description)
|
|
40
|
+
record.activity_feed_item_subscribers.each do |subscriber|
|
|
41
|
+
ActivityFeedItem.create :user => subscriber, :description => description
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private :publish_activity_feed_items
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
## Turning off listeners in specs
|
|
50
|
+
|
|
51
|
+
When unit testing if your listeners are all firing your unit tests become integration tests. To avoid this, you can easily turn off listeners for all specs all the time:
|
|
52
|
+
|
|
53
|
+
Spec::Runner.configure do |config|
|
|
54
|
+
config.before(:each) do
|
|
55
|
+
ActiveModelListener.listeners.clear
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Then, when you want them back on again, you can either turn them back on for a spec:
|
|
60
|
+
|
|
61
|
+
describe "Integrating with listeners" do
|
|
62
|
+
before do
|
|
63
|
+
ActiveModelListener.listeners << FooListener
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
## Specifying a subset of listeners to use
|
|
68
|
+
|
|
69
|
+
When doing data imports, migrations or certain actions that need to only use certain listeners, you can easily specify which ones you'd like to use:
|
|
70
|
+
|
|
71
|
+
ActiveModelListener.with_listeners AuditListener, ActivityListener do
|
|
72
|
+
Article.create! :title => "foo"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
After the block runs, the original listeners are restored.
|
|
76
|
+
|
|
77
|
+
If you want to run some code with no listeners, you can do so with:
|
|
78
|
+
|
|
79
|
+
ActiveModelListener.without_listeners do
|
|
80
|
+
Article.create! :title => "foo"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
## Um. Don't observers already do this?
|
|
84
|
+
|
|
85
|
+
ActiveRecord Observers are:
|
|
86
|
+
|
|
87
|
+
* Hard to apply to large numbers of models (you have to explicitly declare every one)
|
|
88
|
+
* Hard to turn off in tests
|
|
89
|
+
* Hard to selectively enable / disable
|
|
90
|
+
|
|
91
|
+
ActiveModelListener applies to all ActiveRecord models anywhere in your app, all the time. ActiveModelListener listeners are very easy to turn off during unit tests as well.
|
|
92
|
+
|
|
93
|
+
## Copyright
|
|
94
|
+
|
|
95
|
+
Copyright (c) 2009 Jeff Dean. See LICENSE for details.
|
|
96
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
class GenericListener
|
|
4
|
+
def self.after_create(object)
|
|
5
|
+
end
|
|
6
|
+
def self.after_update(object)
|
|
7
|
+
end
|
|
8
|
+
def self.after_destroy(object)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class FooListener < GenericListener
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class BarListener < GenericListener
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class BazListener < GenericListener
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe ActiveModelListener do
|
|
22
|
+
before do
|
|
23
|
+
build_model :articles do
|
|
24
|
+
string :title
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
ActiveModelListener.listeners.clear
|
|
28
|
+
ActiveModelListener.listeners << FooListener
|
|
29
|
+
ActiveModelListener.listeners << BarListener
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "with_listeners" do
|
|
33
|
+
it "only fires those listeners that are present" do
|
|
34
|
+
FooListener.should_receive(:after_create)
|
|
35
|
+
BarListener.should_not_receive(:after_create)
|
|
36
|
+
|
|
37
|
+
ActiveModelListener.with_listeners([FooListener]) do
|
|
38
|
+
Article.create! :title => "foo"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "splats the args" do
|
|
43
|
+
FooListener.should_receive(:after_create)
|
|
44
|
+
BarListener.should_receive(:after_create)
|
|
45
|
+
BarListener.should_not_receive(:after_create)
|
|
46
|
+
|
|
47
|
+
ActiveModelListener.with_listeners(FooListener, BarListener) do
|
|
48
|
+
Article.create! :title => "foo"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "without_listeners" do
|
|
54
|
+
it "fires no listeners" do
|
|
55
|
+
FooListener.should_not_receive(:after_create)
|
|
56
|
+
BarListener.should_not_receive(:after_create)
|
|
57
|
+
|
|
58
|
+
ActiveModelListener.without_listeners do
|
|
59
|
+
Article.create! :title => "foo"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "after create" do
|
|
65
|
+
it "should fire off all wired up events" do
|
|
66
|
+
FooListener.should_receive(:after_create)
|
|
67
|
+
BarListener.should_receive(:after_create)
|
|
68
|
+
Article.create! :title => "foo"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "after update" do
|
|
73
|
+
it "should fire off all wired up events" do
|
|
74
|
+
article = Article.create
|
|
75
|
+
FooListener.should_receive(:after_update).with(article)
|
|
76
|
+
BarListener.should_receive(:after_update).with(article)
|
|
77
|
+
article.save
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "after destroy" do
|
|
82
|
+
it "should fire off all wired up events" do
|
|
83
|
+
article = Article.create
|
|
84
|
+
FooListener.should_receive(:after_destroy).with(article)
|
|
85
|
+
BarListener.should_receive(:after_destroy).with(article)
|
|
86
|
+
article.destroy
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: active_model_listener
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jeff Dean
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-10-03 00:00:00 -04:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description:
|
|
17
|
+
email: jeff@zilkey.com
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files:
|
|
23
|
+
- LICENSE
|
|
24
|
+
- README.md
|
|
25
|
+
files:
|
|
26
|
+
- LICENSE
|
|
27
|
+
- README.md
|
|
28
|
+
has_rdoc: true
|
|
29
|
+
homepage: http://github.com/zilkey/active_model_listener
|
|
30
|
+
licenses: []
|
|
31
|
+
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options:
|
|
34
|
+
- --charset=UTF-8
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: "0"
|
|
42
|
+
version:
|
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: "0"
|
|
48
|
+
version:
|
|
49
|
+
requirements: []
|
|
50
|
+
|
|
51
|
+
rubyforge_project:
|
|
52
|
+
rubygems_version: 1.3.3
|
|
53
|
+
signing_key:
|
|
54
|
+
specification_version: 3
|
|
55
|
+
summary: Simple, global ActiveRecord event observers, using a middleware architecture, that can easily be turned on and off. Designed for audit trails, activity feeds and other app-level event handlers
|
|
56
|
+
test_files:
|
|
57
|
+
- spec/active_model_listener_spec.rb
|
|
58
|
+
- spec/spec_helper.rb
|