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.
@@ -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