aspectory 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/README.textile +206 -0
- data/Rakefile +9 -0
- data/lib/aspectory.rb +56 -0
- data/lib/aspectory/callbacker.rb +112 -0
- data/lib/aspectory/hook.rb +51 -0
- data/lib/aspectory/introspector.rb +59 -0
- data/lib/aspectory/observed_method.rb +36 -0
- data/lib/core_ext/method.rb +5 -0
- data/spec/booty_call_spec.rb +7 -0
- data/spec/callbacker_spec.rb +655 -0
- data/spec/hook_spec.rb +233 -0
- data/spec/introspector_spec.rb +280 -0
- data/spec/spec_helper.rb +8 -0
- metadata +67 -0
data/spec/hook_spec.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe Aspectory::Hook do
|
4
|
+
attr_reader :klass, :object
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@klass = Class.new do
|
8
|
+
include Aspectory
|
9
|
+
|
10
|
+
after :bar, :call!
|
11
|
+
before :foo, :call!
|
12
|
+
around(:fizz) { |fn| call!; fn.call }
|
13
|
+
callback :before, :fizz, :call!
|
14
|
+
|
15
|
+
def foo; :foo end
|
16
|
+
def bar; :bar end
|
17
|
+
def fizz; :fizz end
|
18
|
+
def buzz; :buzz end
|
19
|
+
def call!; @called = true end
|
20
|
+
def called?; @called end
|
21
|
+
def round(fn); @called = fn.call end
|
22
|
+
end
|
23
|
+
|
24
|
+
@object = klass.new
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#before" do
|
28
|
+
it "allows callbacks to be defined before methods are" do
|
29
|
+
object.should_not be_called
|
30
|
+
object.foo
|
31
|
+
object.should be_called
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows callbacks to be defined after methods are" do
|
35
|
+
klass.before(:buzz, :call!)
|
36
|
+
|
37
|
+
object.should_not be_called
|
38
|
+
object.buzz
|
39
|
+
object.should be_called
|
40
|
+
end
|
41
|
+
|
42
|
+
it "allows regex declarations of callback behavior for defined methods" do
|
43
|
+
klass.class_eval { before(/foo|bar/, :regexd!) }
|
44
|
+
mock(object).regexd!.twice
|
45
|
+
object.foo
|
46
|
+
object.bar
|
47
|
+
end
|
48
|
+
|
49
|
+
it "allows regex declarations of callback behavior for undefined methods" do
|
50
|
+
klass.before(/ping/) { pinged! } #, :pinged!)
|
51
|
+
klass.before(/pong/) { ponged! }
|
52
|
+
|
53
|
+
klass.class_eval do
|
54
|
+
def ping; :ping end
|
55
|
+
def pong; :pong end
|
56
|
+
def pung; :pung end
|
57
|
+
end
|
58
|
+
|
59
|
+
mock(object).pinged!
|
60
|
+
mock(object).ponged!
|
61
|
+
mock(object).call!.never
|
62
|
+
object.ping
|
63
|
+
object.pong
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#after" do
|
68
|
+
it "allows callbacks to be defined before methods are" do
|
69
|
+
object.should_not be_called
|
70
|
+
object.bar
|
71
|
+
object.should be_called
|
72
|
+
end
|
73
|
+
|
74
|
+
it "allows callbacks to be defined after methods are" do
|
75
|
+
klass.after(:buzz, :call!)
|
76
|
+
|
77
|
+
object.should_not be_called
|
78
|
+
object.buzz
|
79
|
+
object.should be_called
|
80
|
+
end
|
81
|
+
|
82
|
+
it "allows regex declarations of callback behavior for defined methods" do
|
83
|
+
klass.class_eval { after(/foo|bar/, :regexd!) }
|
84
|
+
mock(object).regexd!(:foo)
|
85
|
+
mock(object).regexd!(:bar)
|
86
|
+
object.foo
|
87
|
+
object.bar
|
88
|
+
end
|
89
|
+
|
90
|
+
it "allows regex declarations of callback behavior for undefined methods" do
|
91
|
+
klass.class_eval { after(/ping|pong/, :regexd!) }
|
92
|
+
|
93
|
+
klass.class_eval { def ping; :ping end }
|
94
|
+
klass.class_eval { def pong; :pong end }
|
95
|
+
|
96
|
+
mock(object).regexd!(:ping)
|
97
|
+
mock(object).regexd!(:pong)
|
98
|
+
object.ping
|
99
|
+
object.pong
|
100
|
+
end
|
101
|
+
|
102
|
+
it "allows regex declarations of callback behavior for undefined methods" do
|
103
|
+
klass.after(/ping/) { pinged! } #, :pinged!)
|
104
|
+
klass.after(/pong/) { ponged! }
|
105
|
+
|
106
|
+
klass.class_eval do
|
107
|
+
def ping; :ping end
|
108
|
+
def pong; :pong end
|
109
|
+
def pung; :pung end
|
110
|
+
end
|
111
|
+
|
112
|
+
mock(object).pinged!
|
113
|
+
mock(object).ponged!
|
114
|
+
mock(object).call!.never
|
115
|
+
object.ping
|
116
|
+
object.pong
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#around" do
|
121
|
+
it "allows callbacks to be defined before methods are" do
|
122
|
+
object.should_not be_called
|
123
|
+
object.fizz.should == :fizz
|
124
|
+
object.should be_called
|
125
|
+
end
|
126
|
+
|
127
|
+
it "allows callbacks to be defined after methods are" do
|
128
|
+
klass.around(:buzz) { |fn| call!; fn.call }
|
129
|
+
|
130
|
+
object.should_not be_called
|
131
|
+
object.buzz
|
132
|
+
object.should be_called
|
133
|
+
end
|
134
|
+
|
135
|
+
it "allows regex declarations of callback behavior for defined methods" do
|
136
|
+
klass.class_eval do
|
137
|
+
around(/foo|bar/) { |f| regexd! f.call }
|
138
|
+
end
|
139
|
+
mock(object).regexd!(:foo)
|
140
|
+
mock(object).regexd!(:bar)
|
141
|
+
object.foo
|
142
|
+
object.bar
|
143
|
+
end
|
144
|
+
|
145
|
+
it "allows regex declarations of callback behavior for undefined methods" do
|
146
|
+
klass.class_eval do
|
147
|
+
around(/ping|pong/) { |f| regexd! f.call }
|
148
|
+
end
|
149
|
+
|
150
|
+
klass.class_eval { def ping; :ping end }
|
151
|
+
klass.class_eval { def pong; :pong end }
|
152
|
+
|
153
|
+
mock(object).regexd!(:ping)
|
154
|
+
mock(object).regexd!(:pong)
|
155
|
+
object.ping
|
156
|
+
object.pong
|
157
|
+
end
|
158
|
+
|
159
|
+
it "allows regex declarations of callback behavior for undefined methods" do
|
160
|
+
klass.around(/ping/) { |fn| fn.call pinged! } #, :pinged!)
|
161
|
+
klass.around(/pong/) { |fn| fn.call ponged! }
|
162
|
+
|
163
|
+
klass.class_eval do
|
164
|
+
def ping; :ping end
|
165
|
+
def pong; :pong end
|
166
|
+
def pung; :pung end
|
167
|
+
end
|
168
|
+
|
169
|
+
mock(object).pinged!
|
170
|
+
mock(object).ponged!
|
171
|
+
mock(object).call!.never
|
172
|
+
object.ping
|
173
|
+
object.pong
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#callback" do
|
178
|
+
it "allows callbacks to be defined before methods are" do
|
179
|
+
object.should_not be_called
|
180
|
+
object.fizz
|
181
|
+
object.should be_called
|
182
|
+
end
|
183
|
+
|
184
|
+
it "allows callbacks to be defined after methods are" do
|
185
|
+
klass.callback(:before, :buzz, :call!)
|
186
|
+
|
187
|
+
object.should_not be_called
|
188
|
+
object.buzz
|
189
|
+
object.should be_called
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#observe" do
|
194
|
+
it "allows method definitions to be observed" do
|
195
|
+
called = false
|
196
|
+
klass.observe(:boom) { called = true }
|
197
|
+
klass.class_eval { def boom; :boom end }
|
198
|
+
called.should be_true
|
199
|
+
end
|
200
|
+
|
201
|
+
it "allows multiple callbacks for observed methods" do
|
202
|
+
once = twice = false
|
203
|
+
klass.observe(:boom) { once = true }
|
204
|
+
klass.observe(:boom) { twice = true }
|
205
|
+
klass.class_eval { def boom; :boom end }
|
206
|
+
once.should be_true
|
207
|
+
twice.should be_true
|
208
|
+
end
|
209
|
+
|
210
|
+
it "only runs callbacks once by default" do
|
211
|
+
called = false
|
212
|
+
klass.observe(:boom) { called = !called }
|
213
|
+
klass.class_eval { def boom; :boom end }
|
214
|
+
klass.class_eval { def boom; :boom end }
|
215
|
+
called.should be_true
|
216
|
+
end
|
217
|
+
|
218
|
+
it "allows callback limits to be specified" do
|
219
|
+
mock(callee = Object.new).call!.times(3)
|
220
|
+
klass.observe(:foo, :times => 3) { callee.call! }
|
221
|
+
klass.class_eval { def foo; :foo end }
|
222
|
+
klass.class_eval { def foo; :foo end }
|
223
|
+
klass.class_eval { def foo; :foo end }
|
224
|
+
klass.class_eval { def foo; :foo end }
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should allows metaclasses to be observed" do
|
228
|
+
mock(callee = Object.new).call!
|
229
|
+
klass.observe(:foo, :meta => true) { callee.call! }
|
230
|
+
klass.class_eval { def self.foo; :foo end }
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe Aspectory::Introspector do
|
4
|
+
attr_reader :klass, :introspector
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@klass = Class.new { def foo; :foo end }
|
8
|
+
@introspector = Aspectory::Introspector.new(@klass)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "knows defined methods" do
|
12
|
+
introspector.defined_methods.should == [:foo]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "knows if a method is defined" do
|
16
|
+
introspector.should have_method(:foo)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "knows if a method is not defined" do
|
20
|
+
introspector.should_not have_method(:bar)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "only observes klass once" do
|
24
|
+
mock(klass).meta_def(:method_added).once
|
25
|
+
introspector.observe_klass!
|
26
|
+
introspector.observe_klass!
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "observing a method" do
|
30
|
+
context "on a metaclass" do
|
31
|
+
attr_reader :meta_introspector
|
32
|
+
|
33
|
+
before(:each) do
|
34
|
+
@meta_introspector = Aspectory::Introspector.new(klass, :meta => true)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "is stored in #observed_methods" do
|
38
|
+
meta_introspector.observe(:bar) { }
|
39
|
+
meta_introspector.observing?(:bar).should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "ceases when method gets defined" do
|
43
|
+
meta_introspector.observe(:bar)
|
44
|
+
klass.class_eval { def self.bar; :bar end }
|
45
|
+
meta_introspector.observing?(:bar).should be_false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "allows a callback block for when methods get defined" do
|
49
|
+
called = false
|
50
|
+
meta_introspector.observe(:bar) { called = true }
|
51
|
+
klass.class_eval { def self.bar; :bar end }
|
52
|
+
called.should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "allows multiple callback blocks" do
|
56
|
+
once = twice = false
|
57
|
+
meta_introspector.observe(:bar) { once = true }
|
58
|
+
meta_introspector.observe(:bar) { twice = true }
|
59
|
+
klass.class_eval { def self.bar; :bar end }
|
60
|
+
once.should be_true
|
61
|
+
twice.should be_true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the method isn't defined" do
|
66
|
+
it "is stored in #observed_methods" do
|
67
|
+
introspector.observe(:bar) { }
|
68
|
+
introspector.observing?(:bar).should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "ceases when method gets defined" do
|
72
|
+
introspector.observe(:bar)
|
73
|
+
klass.class_eval { def bar; :bar end }
|
74
|
+
introspector.observing?(:bar).should be_false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "allows a callback block for when methods get defined" do
|
78
|
+
called = false
|
79
|
+
introspector.observe(:bar) { called = true }
|
80
|
+
klass.class_eval { def bar; :bar end }
|
81
|
+
called.should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it "allows multiple callback blocks" do
|
85
|
+
once = twice = false
|
86
|
+
introspector.observe(:bar) { once = true }
|
87
|
+
introspector.observe(:bar) { twice = true }
|
88
|
+
klass.class_eval { def bar; :bar end }
|
89
|
+
once.should be_true
|
90
|
+
twice.should be_true
|
91
|
+
end
|
92
|
+
|
93
|
+
it "only runs callbacks once" do
|
94
|
+
once = false
|
95
|
+
introspector.observe(:bar) { once = !once }
|
96
|
+
klass.class_eval { def bar; :bar end }
|
97
|
+
klass.class_eval { def bar; :bar end }
|
98
|
+
once.should be_true
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "specifying limits" do
|
102
|
+
it "allows a :times option" do
|
103
|
+
mock(callee = Object.new).call!
|
104
|
+
introspector.observe(:bar, :times => 1) { callee.call! }
|
105
|
+
klass.class_eval { def bar; :bar end }
|
106
|
+
klass.class_eval { def bar; :bar end }
|
107
|
+
end
|
108
|
+
|
109
|
+
it "allows callback to be run 2 times" do
|
110
|
+
mock(callee = Object.new).call!.times(2)
|
111
|
+
introspector.observe(:bar, :times => 2) { callee.call! }
|
112
|
+
klass.class_eval { def bar; :bar end }
|
113
|
+
klass.class_eval { def bar; :bar end }
|
114
|
+
klass.class_eval { def bar; :bar end }
|
115
|
+
end
|
116
|
+
|
117
|
+
it "allows callback to be run 3 times" do
|
118
|
+
mock(callee = Object.new).call!.times(3)
|
119
|
+
introspector.observe(:bar, :times => 3) { callee.call! }
|
120
|
+
klass.class_eval { def bar; :bar end }
|
121
|
+
klass.class_eval { def bar; :bar end }
|
122
|
+
klass.class_eval { def bar; :bar end }
|
123
|
+
klass.class_eval { def bar; :bar end }
|
124
|
+
end
|
125
|
+
|
126
|
+
it "allows callback to be run every time" do
|
127
|
+
mock(callee = Object.new).call!.times(3)
|
128
|
+
introspector.observe(:bar, :times => :infinite) { callee.call! }
|
129
|
+
klass.class_eval { def bar; :bar end }
|
130
|
+
klass.class_eval { def bar; :bar end }
|
131
|
+
klass.class_eval { def bar; :bar end }
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should works with other times terms abbreviation" do
|
135
|
+
mock(callee = Object.new).call!.times(3)
|
136
|
+
introspector.observe(:bar, :times => :any) { callee.call! }
|
137
|
+
klass.class_eval { def bar; :bar end }
|
138
|
+
klass.class_eval { def bar; :bar end }
|
139
|
+
klass.class_eval { def bar; :bar end }
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should works with :all abbreviation" do
|
143
|
+
mock(callee = Object.new).call!.times(3)
|
144
|
+
introspector.observe(:bar, :times => :all) { callee.call! }
|
145
|
+
klass.class_eval { def bar; :bar end }
|
146
|
+
klass.class_eval { def bar; :bar end }
|
147
|
+
klass.class_eval { def bar; :bar end }
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should works with :every abbreviation" do
|
151
|
+
mock(callee = Object.new).call!.times(3)
|
152
|
+
introspector.observe(:bar, :times => :every) { callee.call! }
|
153
|
+
klass.class_eval { def bar; :bar end }
|
154
|
+
klass.class_eval { def bar; :bar end }
|
155
|
+
klass.class_eval { def bar; :bar end }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "when the method is defined" do
|
161
|
+
it "is stored in #observed_methods" do
|
162
|
+
introspector.observe(:inspect) { }
|
163
|
+
introspector.observing?(:inspect).should be_true
|
164
|
+
end
|
165
|
+
|
166
|
+
it "ceases when method gets defined" do
|
167
|
+
introspector.observe(:inspect)
|
168
|
+
klass.class_eval { def inspect; :inspect end }
|
169
|
+
introspector.observing?(:inspect).should be_false
|
170
|
+
end
|
171
|
+
|
172
|
+
it "allows a callback block for when methods get defined" do
|
173
|
+
called = false
|
174
|
+
introspector.observe(:inspect) { called = true }
|
175
|
+
klass.class_eval { def inspect; :inspect end }
|
176
|
+
called.should be_true
|
177
|
+
end
|
178
|
+
|
179
|
+
it "allows multiple callback blocks" do
|
180
|
+
once = twice = false
|
181
|
+
introspector.observe(:inspect) { once = true }
|
182
|
+
introspector.observe(:inspect) { twice = true }
|
183
|
+
klass.class_eval { def inspect; :inspect end }
|
184
|
+
once.should be_true
|
185
|
+
twice.should be_true
|
186
|
+
end
|
187
|
+
|
188
|
+
it "only runs callbacks once" do
|
189
|
+
once = false
|
190
|
+
introspector.observe(:inspect) { once = !once }
|
191
|
+
klass.class_eval { def inspect; :inspect end }
|
192
|
+
klass.class_eval { def inspect; :inspect end }
|
193
|
+
once.should be_true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "observing a regex" do
|
199
|
+
context "when the method isn't defined" do
|
200
|
+
it "is stored in #observed_methods" do
|
201
|
+
introspector.observe(/(foo|bar)/) { }
|
202
|
+
introspector.observing?(/(foo|bar)/).should be_true
|
203
|
+
end
|
204
|
+
|
205
|
+
it "ceases when a matching method gets defined" do
|
206
|
+
introspector.observe(/(foo|bar)/)
|
207
|
+
klass.class_eval { def bar; :bar end }
|
208
|
+
introspector.observing?(/(foo|bar)/).should be_false
|
209
|
+
end
|
210
|
+
|
211
|
+
it "allows a callback block for when methods get defined" do
|
212
|
+
called = false
|
213
|
+
introspector.observe(/(foo|bar)/) { called = true }
|
214
|
+
klass.class_eval { def bar; :bar end }
|
215
|
+
called.should be_true
|
216
|
+
end
|
217
|
+
|
218
|
+
it "allows multiple callback blocks" do
|
219
|
+
once = twice = false
|
220
|
+
introspector.observe(/(bar)/) { once = true }
|
221
|
+
introspector.observe(/(foo|bar)/) { twice = true }
|
222
|
+
klass.class_eval { def bar; :bar end }
|
223
|
+
once.should be_true
|
224
|
+
twice.should be_true
|
225
|
+
end
|
226
|
+
|
227
|
+
it "passes the method name" do
|
228
|
+
called = false
|
229
|
+
introspector.observe(/(foo|bar)/) { |method_id| called = method_id.eql?(:bar) }
|
230
|
+
klass.class_eval { def bar; :bar end }
|
231
|
+
called.should be_true
|
232
|
+
end
|
233
|
+
|
234
|
+
it "only runs callbacks once" do
|
235
|
+
once = false
|
236
|
+
introspector.observe(/(foo|bar)/) { once = !once }
|
237
|
+
klass.class_eval { def foo; :foo end }
|
238
|
+
klass.class_eval { def bar; :bar end }
|
239
|
+
once.should be_true
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context "when the method is defined" do
|
244
|
+
it "is stored in #observed_methods" do
|
245
|
+
introspector.observe(/inspect/) { }
|
246
|
+
introspector.observing?(/inspect/).should be_true
|
247
|
+
end
|
248
|
+
|
249
|
+
it "ceases when method gets defined" do
|
250
|
+
introspector.observe(/inspect/)
|
251
|
+
klass.class_eval { def inspect; :inspect end }
|
252
|
+
introspector.observing?(/inspect/).should be_false
|
253
|
+
end
|
254
|
+
|
255
|
+
it "allows a callback block for when methods get defined" do
|
256
|
+
called = false
|
257
|
+
introspector.observe(/inspect/) { called = true }
|
258
|
+
klass.class_eval { def inspect; :inspect end }
|
259
|
+
called.should be_true
|
260
|
+
end
|
261
|
+
|
262
|
+
it "allows multiple callback blocks" do
|
263
|
+
once = twice = false
|
264
|
+
introspector.observe(/inspect/) { once = true }
|
265
|
+
introspector.observe(/inspect/) { twice = true }
|
266
|
+
klass.class_eval { def inspect; :inspect end }
|
267
|
+
once.should be_true
|
268
|
+
twice.should be_true
|
269
|
+
end
|
270
|
+
|
271
|
+
it "only runs callbacks once" do
|
272
|
+
once = false
|
273
|
+
introspector.observe(/inspect/) { once = !once }
|
274
|
+
klass.class_eval { def inspect; :inspect end }
|
275
|
+
klass.class_eval { def inspect; :inspect end }
|
276
|
+
once.should be_true
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|