aspectory 0.1.0

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