sentinel 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +9 -3
- data/VERSION +1 -1
- data/lib/sentinel.rb +17 -3
- data/spec/fixtures/sentinel_subject.rb +2 -14
- data/spec/sentinel_spec.rb +74 -45
- metadata +3 -3
data/README.textile
CHANGED
@@ -2,6 +2,8 @@ h1. Sentinel
|
|
2
2
|
|
3
3
|
Transparent (unobtrusive) Observers for your Rubies.
|
4
4
|
|
5
|
+
Does Sentinel helps your daily work with Ruby? So, "please recommend me in Work With Rails":http://workingwithrails.com/recommendation/new/person/9370-lucas-h-ngaro and thanks for your kindness! :)
|
6
|
+
|
5
7
|
h2. Why?
|
6
8
|
|
7
9
|
Developed for an specific need we had at "Busk":http://www.busk.com, Sentinel is a very small library that provides a way to add what we call "Transparent Observers" to your Ruby code. This means that you do not need to modify the observed methods (following the most common implementation of Observers), just use a mixin and declare your observers.
|
@@ -49,16 +51,20 @@ class MyClass
|
|
49
51
|
end
|
50
52
|
</pre>
|
51
53
|
|
52
|
-
And... that's it! Every time the subject (in this case, MyClass) method is called, the specified observer method will be called
|
54
|
+
And... that's it! Every time the subject (in this case, MyClass) method is called, the specified observer method will be called too. By default, the observer method is called *before* the subject method, but you can call it *after* the execution (will see it in the next example).
|
55
|
+
|
56
|
+
The parameters passed to the subject method are passed to the observer method via the *args array. The options hash contains the key :subject, which contains the actual subject object and the key :result, which contains the return of the observed method in case you intercepted the call after its execution.
|
57
|
+
|
58
|
+
Below is a more "advanced" use of the observers:
|
53
59
|
|
54
60
|
<pre>
|
55
61
|
class MyObserver
|
56
62
|
include Sentinel
|
57
63
|
|
58
|
-
observe MyClass, :instance_method
|
64
|
+
observe MyClass, :instance_method, :intercept => :after
|
59
65
|
|
60
66
|
def self.notify(options, *args)
|
61
|
-
puts "Called from #{options[:subject]} with arguments #{args.inspect}"
|
67
|
+
puts "Called from #{options[:subject]} with arguments #{args.inspect} and returned #{options[:result]}"
|
62
68
|
end
|
63
69
|
end
|
64
70
|
</pre>
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/sentinel.rb
CHANGED
@@ -10,14 +10,28 @@ module Sentinel
|
|
10
10
|
sentinel = self
|
11
11
|
|
12
12
|
options = {
|
13
|
-
:method_to_notify => :notify
|
13
|
+
:method_to_notify => :notify,
|
14
|
+
:intercept => :before
|
14
15
|
}.merge(options)
|
15
16
|
|
17
|
+
if options[:intercept] == :before
|
18
|
+
calls = <<-CODE
|
19
|
+
sentinel.send("#{options[:method_to_notify]}", observer_opt, *args)
|
20
|
+
self.send("#{method_name}_without_observer", *args)
|
21
|
+
CODE
|
22
|
+
else
|
23
|
+
calls = <<-CODE
|
24
|
+
result = self.send("#{method_name}_without_observer", *args)
|
25
|
+
observer_opt[:result] = result
|
26
|
+
sentinel.send("#{options[:method_to_notify]}", observer_opt, *args)
|
27
|
+
result
|
28
|
+
CODE
|
29
|
+
end
|
30
|
+
|
16
31
|
body = <<-CODE
|
17
32
|
define_method "#{method_name}_with_observer" do |*args|
|
18
33
|
observer_opt = {:subject => self}
|
19
|
-
|
20
|
-
self.send("#{method_name}_without_observer", *args)
|
34
|
+
#{calls}
|
21
35
|
end
|
22
36
|
|
23
37
|
alias_method "#{method_name}_without_observer", method_name
|
@@ -1,14 +1,10 @@
|
|
1
1
|
class SentinelSubject
|
2
2
|
def instance_method
|
3
|
-
|
4
|
-
end
|
5
|
-
|
6
|
-
def new_method
|
7
|
-
"hi from new method!"
|
3
|
+
42
|
8
4
|
end
|
9
5
|
|
10
6
|
def self.class_method
|
11
|
-
|
7
|
+
42
|
12
8
|
end
|
13
9
|
|
14
10
|
def instance_method_with_params(*params)
|
@@ -18,12 +14,4 @@ class SentinelSubject
|
|
18
14
|
def self.class_method_with_params(*params)
|
19
15
|
"hi from class method with params!"
|
20
16
|
end
|
21
|
-
|
22
|
-
def instance_returning_something
|
23
|
-
42
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.class_returning_something
|
27
|
-
42
|
28
|
-
end
|
29
17
|
end
|
data/spec/sentinel_spec.rb
CHANGED
@@ -2,74 +2,103 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/fixtures/my_observer')
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/fixtures/sentinel_subject')
|
4
4
|
|
5
|
-
describe Sentinel
|
6
|
-
before(:
|
5
|
+
describe Sentinel do
|
6
|
+
before(:each) do
|
7
7
|
MyObserver.send(:include, Sentinel)
|
8
8
|
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
# reload the file to undefine the observer methods (would cause 'stack level too deep' errors otherwise)
|
12
|
+
load File.expand_path(File.dirname(__FILE__) + '/fixtures/sentinel_subject.rb')
|
13
|
+
end
|
9
14
|
|
10
|
-
context "
|
11
|
-
it "should
|
15
|
+
context "basic premises" do
|
16
|
+
it "should not modify the subject method output when called before it" do
|
17
|
+
MyObserver.send(:observe, SentinelSubject, :class_method, :class_method => true)
|
12
18
|
MyObserver.send(:observe, SentinelSubject, :instance_method)
|
13
|
-
MyObserver.expects(:notify)
|
14
|
-
|
15
|
-
SentinelSubject.new.instance_method
|
16
|
-
end
|
17
19
|
|
18
|
-
|
19
|
-
MyObserver.send(:observe, SentinelSubject, :class_method, :class_method => true)
|
20
|
-
MyObserver.expects(:notify)
|
20
|
+
MyObserver.expects(:notify).twice
|
21
21
|
|
22
|
-
SentinelSubject.class_method
|
22
|
+
SentinelSubject.class_method.should == 42
|
23
|
+
SentinelSubject.new.instance_method.should == 42
|
23
24
|
end
|
24
|
-
|
25
|
-
it "should not modify the
|
26
|
-
MyObserver.send(:observe, SentinelSubject, :
|
27
|
-
MyObserver.send(:observe, SentinelSubject, :
|
25
|
+
|
26
|
+
it "should not modify the subject method output when called after it" do
|
27
|
+
MyObserver.send(:observe, SentinelSubject, :class_method, :class_method => true, :intercept => :after)
|
28
|
+
MyObserver.send(:observe, SentinelSubject, :instance_method, :intercept => :after)
|
28
29
|
|
29
30
|
MyObserver.expects(:notify).twice
|
30
31
|
|
31
|
-
SentinelSubject.
|
32
|
-
SentinelSubject.new.
|
32
|
+
SentinelSubject.class_method.should == 42
|
33
|
+
SentinelSubject.new.instance_method.should == 42
|
33
34
|
end
|
35
|
+
|
36
|
+
it "should pass all parameters of the observed methods to the observer" do
|
37
|
+
MyObserver.send(:observe, SentinelSubject, :class_method_with_params, :class_method => true)
|
38
|
+
MyObserver.send(:observe, SentinelSubject, :instance_method_with_params)
|
34
39
|
|
35
|
-
|
36
|
-
it "should pass all parameters of the observed methods to the Observer" do
|
37
|
-
MyObserver.send(:observe, SentinelSubject, :class_method_with_params, :class_method => true)
|
38
|
-
MyObserver.send(:observe, SentinelSubject, :instance_method_with_params)
|
40
|
+
subject = SentinelSubject.new
|
39
41
|
|
40
|
-
|
42
|
+
MyObserver.expects(:notify).once.with({:subject => SentinelSubject}, "texto", 1)
|
43
|
+
MyObserver.expects(:notify).once.with({:subject => subject}, "texto", 1)
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
SentinelSubject.class_method_with_params("texto", 1)
|
46
|
-
subject.instance_method_with_params("texto", 1)
|
47
|
-
end
|
45
|
+
SentinelSubject.class_method_with_params("texto", 1)
|
46
|
+
subject.instance_method_with_params("texto", 1)
|
48
47
|
end
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
context "with the default options" do
|
51
|
+
it "should notify the specified observer when calling an instance method" do
|
52
|
+
MyObserver.send(:observe, SentinelSubject, :instance_method)
|
53
|
+
MyObserver.expects(:notify)
|
54
54
|
|
55
|
-
|
56
|
-
end
|
55
|
+
SentinelSubject.new.instance_method
|
57
56
|
end
|
58
|
-
end
|
59
57
|
|
60
|
-
|
61
|
-
|
62
|
-
MyObserver.expects(:notify)
|
63
|
-
SentinelSubject.expects(:observe).raises(ArgumentError)
|
58
|
+
it "should notify the specified observer when calling a class method" do
|
59
|
+
MyObserver.send(:observe, SentinelSubject, :class_method, :class_method => true)
|
60
|
+
MyObserver.expects(:notify)
|
64
61
|
|
65
|
-
SentinelSubject.
|
62
|
+
SentinelSubject.class_method
|
66
63
|
end
|
64
|
+
|
65
|
+
it "should be called before the method execution" do
|
66
|
+
MyObserver.send(:observe, SentinelSubject, :instance_method)
|
67
|
+
subject = SentinelSubject.new
|
68
|
+
|
69
|
+
MyObserver.expects(:notify).once.with({:subject => subject})
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
+
subject.instance_method
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "with user-specified options" do
|
76
|
+
context "when observing methods using a different observer method" do
|
77
|
+
it "should call the specified observer method when the subject method is called" do
|
78
|
+
MyObserver.send(:observe, SentinelSubject, :instance_method, :method_to_notify => :another_method)
|
79
|
+
MyObserver.send(:observe, SentinelSubject, :class_method, :method_to_notify => :another_method, :class_method => true)
|
80
|
+
|
81
|
+
MyObserver.expects(:another_method).twice
|
82
|
+
|
83
|
+
SentinelSubject.new.instance_method
|
84
|
+
SentinelSubject.class_method
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when intercepting the call after the method execution" do
|
89
|
+
it "should pass the result of the execution to the observer" do
|
90
|
+
MyObserver.send(:observe, SentinelSubject, :instance_method, :intercept => :after)
|
91
|
+
MyObserver.send(:observe, SentinelSubject, :class_method, :intercept => :after, :class_method => true)
|
92
|
+
|
93
|
+
subject = SentinelSubject.new
|
94
|
+
|
95
|
+
MyObserver.expects(:notify).once.with({:subject => subject, :result => 42})
|
96
|
+
MyObserver.expects(:notify).once.with({:subject => SentinelSubject, :result => 42})
|
71
97
|
|
72
|
-
|
98
|
+
subject.instance_method
|
99
|
+
SentinelSubject.class_method
|
100
|
+
end
|
73
101
|
end
|
74
102
|
end
|
103
|
+
|
75
104
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 5
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.5.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- "Lucas H\xC3\xBAngaro"
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-26 00:00:00 -03:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|