meddleware 0.0.1 → 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.
- checksums.yaml +4 -4
- data/lib/meddleware.rb +116 -35
- data/lib/meddleware/version.rb +1 -1
- data/spec/build_meddleware_spec.rb +384 -0
- data/spec/call_meddleware_chain_spec.rb +74 -0
- data/spec/call_meddleware_spec.rb +259 -0
- data/spec/examples_spec.rb +49 -0
- metadata +11 -5
- data/spec/meddleware_spec.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 453a4fea42903735f1b7e502f5fb5ba323ecfd1240db7d476453dabd0e773f8d
|
4
|
+
data.tar.gz: c459598e2a9145a7ad8a4128cefc5de8ed3a018db28e1cf95a24a26b15b42883
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94db19316854023dece52b027bd87f0c423936c3ccf4fba424b85eaa798672aa936820ecb0b5e427c5832c29fe7deb6dce876563c70c3869b15b1b137d26b511
|
7
|
+
data.tar.gz: 57188689d33ca8a987e4cdcde3792da430b86946c6c0ec99ace62b6e7be05c9658eacbc83f6e3cc26c20d3c8e06a47ef53fa4937919d7d4c1e49a556469cc0c3
|
data/lib/meddleware.rb
CHANGED
@@ -1,77 +1,158 @@
|
|
1
1
|
require 'meddleware/version'
|
2
2
|
|
3
3
|
class Meddleware
|
4
|
-
|
5
|
-
|
6
|
-
def initialize
|
4
|
+
def initialize(&block)
|
7
5
|
instance_eval(&block) if block_given?
|
8
6
|
end
|
9
7
|
|
10
|
-
def
|
11
|
-
|
8
|
+
def use(*klass_and_args, &block)
|
9
|
+
entry = create_entry(klass_and_args, block)
|
10
|
+
stack << entry
|
11
|
+
self
|
12
12
|
end
|
13
|
+
alias append use
|
13
14
|
|
14
|
-
def
|
15
|
-
|
15
|
+
def prepend(*klass_and_args, &block)
|
16
|
+
entry = create_entry(klass_and_args, block)
|
17
|
+
stack.insert(0, entry)
|
18
|
+
self
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
|
21
|
+
def after(after_klass, *klass_and_args, &block)
|
22
|
+
entry = create_entry(klass_and_args, block)
|
23
|
+
i = index(after_klass) || count - 1
|
24
|
+
stack.insert(i + 1, entry)
|
25
|
+
self
|
20
26
|
end
|
21
27
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
28
|
+
def before(before_klass, *klass_and_args, &block)
|
29
|
+
entry = create_entry(klass_and_args, block)
|
30
|
+
i = index(before_klass) || 0
|
31
|
+
stack.insert(i, entry)
|
32
|
+
self
|
25
33
|
end
|
26
|
-
alias append use
|
27
34
|
|
28
|
-
def
|
29
|
-
|
30
|
-
stack.insert(0, Entry.new(klass, args))
|
35
|
+
def include?(klass)
|
36
|
+
!!index(klass)
|
31
37
|
end
|
32
38
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
stack.insert(i + 1, Entry.new(klass, args))
|
39
|
+
def remove(klass)
|
40
|
+
stack.reject! { |entry| entry[0] == klass }
|
41
|
+
self
|
37
42
|
end
|
38
43
|
|
39
|
-
def
|
40
|
-
|
41
|
-
i = index(
|
42
|
-
|
44
|
+
def replace(old_klass, *klass_and_args, &block)
|
45
|
+
entry = create_entry(klass_and_args, block)
|
46
|
+
i = index(old_klass)
|
47
|
+
|
48
|
+
unless i
|
49
|
+
raise RuntimeError, "middleware not present: #{old_klass}"
|
50
|
+
end
|
51
|
+
|
52
|
+
stack[i] = entry
|
53
|
+
self
|
43
54
|
end
|
44
55
|
|
45
|
-
def
|
46
|
-
stack.
|
56
|
+
def count
|
57
|
+
stack.count
|
47
58
|
end
|
59
|
+
alias size count
|
48
60
|
|
49
61
|
def clear
|
50
62
|
stack.clear
|
51
63
|
end
|
52
64
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
65
|
+
def empty?
|
66
|
+
stack.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
def call(*args)
|
70
|
+
chain = build_chain
|
71
|
+
default_args = args
|
72
|
+
|
73
|
+
traverse = proc do |*args|
|
74
|
+
if args.empty?
|
75
|
+
args = default_args
|
76
|
+
else
|
77
|
+
default_args = args
|
78
|
+
end
|
79
|
+
|
57
80
|
if chain.empty?
|
58
|
-
yield
|
81
|
+
yield(*args) if block_given?
|
59
82
|
else
|
60
|
-
chain.shift
|
83
|
+
middleware = chain.shift
|
84
|
+
|
85
|
+
if middleware.is_a?(Proc) && !middleware.lambda?
|
86
|
+
middleware.call(*args)
|
87
|
+
|
88
|
+
# implicit yield
|
89
|
+
traverse.call(*args)
|
90
|
+
else
|
91
|
+
middleware.call(*args, &traverse)
|
92
|
+
end
|
61
93
|
end
|
62
94
|
end
|
63
95
|
traverse.call(*args)
|
64
96
|
end
|
65
97
|
|
98
|
+
|
66
99
|
private
|
67
100
|
|
68
101
|
def stack
|
69
102
|
@stack ||= []
|
70
103
|
end
|
71
104
|
|
72
|
-
|
73
|
-
|
74
|
-
|
105
|
+
def index(klass)
|
106
|
+
stack.index {|entry| entry[0] == klass }
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_entry(klass_and_args, block)
|
110
|
+
klass, *args = klass_and_args
|
111
|
+
|
112
|
+
if [ klass, block ].compact.count == 0
|
113
|
+
raise ArgumentError, 'either a middleware or block must be provided'
|
114
|
+
end
|
115
|
+
|
116
|
+
# dedup
|
117
|
+
remove(klass || block)
|
118
|
+
|
119
|
+
if klass
|
120
|
+
# validate
|
121
|
+
if klass.is_a? Class
|
122
|
+
unless klass.method_defined?(:call)
|
123
|
+
raise ArgumentError, "middleware must implement `.call`: #{klass}"
|
124
|
+
end
|
125
|
+
else
|
126
|
+
unless klass.respond_to?(:call)
|
127
|
+
raise ArgumentError, "middleware must respond to `.call`: #{klass}"
|
128
|
+
end
|
129
|
+
|
130
|
+
unless block.nil?
|
131
|
+
raise ArgumentError, 'can not supply middleware instance and block'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
[ klass, args, block ]
|
136
|
+
else
|
137
|
+
[ block ]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def build_chain
|
142
|
+
# build the middleware stack
|
143
|
+
stack.map do |klass, args, block|
|
144
|
+
if klass.is_a? Class
|
145
|
+
klass.new(*args, &block)
|
146
|
+
else
|
147
|
+
if args.nil? || args.empty?
|
148
|
+
klass
|
149
|
+
else
|
150
|
+
# curry args
|
151
|
+
->(*more_args, &block) do
|
152
|
+
klass.call(*args, *more_args, &block)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
75
156
|
end
|
76
157
|
end
|
77
158
|
end
|
data/lib/meddleware/version.rb
CHANGED
@@ -0,0 +1,384 @@
|
|
1
|
+
A = Class.new(Meddler)
|
2
|
+
B = Class.new(Meddler)
|
3
|
+
C = Class.new(Meddler)
|
4
|
+
|
5
|
+
|
6
|
+
describe Meddleware do
|
7
|
+
subject { described_class.new }
|
8
|
+
|
9
|
+
def stack
|
10
|
+
subject.send(:build_chain).map &:class
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples 'a middleware adder method' do |name|
|
14
|
+
let(:function) do
|
15
|
+
subject.method(name).yield_self do |fn|
|
16
|
+
if fn.arity < -1
|
17
|
+
# needs prefix arg, eg. :before/:after
|
18
|
+
proc {|*args, &block| fn.call(nil, *args, &block) }
|
19
|
+
else
|
20
|
+
fn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'adds middleware to the stack' do
|
26
|
+
function.call(A)
|
27
|
+
expect(stack).to eq [ A ]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'does not add duplicates' do
|
31
|
+
3.times { function.call(A) }
|
32
|
+
expect(stack).to eq [ A ]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns self' do
|
36
|
+
expect(function.call(A)).to be subject
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'is idempotent' do
|
40
|
+
subject.use A
|
41
|
+
function.call(B)
|
42
|
+
res = stack
|
43
|
+
|
44
|
+
function.call(B)
|
45
|
+
expect(stack).to eq res
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with arguments' do
|
49
|
+
after { subject.send :build_chain }
|
50
|
+
|
51
|
+
it 'accepts an argument' do
|
52
|
+
expect(A).to receive(:new).with(:abc)
|
53
|
+
function.call(A, :abc)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'accepts multiple arguments' do
|
57
|
+
expect(A).to receive(:new).with(:abc, :xyz)
|
58
|
+
function.call(A, :abc, :xyz)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'accepts kwargs' do
|
62
|
+
expect(A).to receive(:new).with(a: 1, b: 2)
|
63
|
+
function.call(A, a: 1, b: 2)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'works with all forms of kwargs' do
|
67
|
+
expect(A).to receive(:new).with(a: 1, b: 2)
|
68
|
+
function.call(A, { a: 1, b: 2 })
|
69
|
+
|
70
|
+
expect(B).to receive(:new).with({ a: 1, b: 2 })
|
71
|
+
function.call(B, { a: 1, b: 2 })
|
72
|
+
|
73
|
+
expect(C).to receive(:new).with({ a: 1, b: 2 })
|
74
|
+
function.call(C, a: 1, b: 2)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'works with both args and kwargs' do
|
78
|
+
expect(A).to receive(:new).with(:abc, a: 1, b: 2)
|
79
|
+
function.call(A, :abc, a: 1, b: 2)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when middleware is an instance' do
|
84
|
+
it 'adds middleware to the stack' do
|
85
|
+
function.call(A.new)
|
86
|
+
expect(stack).to eq [ A ]
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'will add multiple instances of the same class' do
|
90
|
+
function.call(A.new)
|
91
|
+
function.call(A.new)
|
92
|
+
expect(stack).to eq [ A, A ]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when middleware is a block' do
|
97
|
+
it 'accepts procs' do
|
98
|
+
function.call(Proc.new {})
|
99
|
+
expect(stack).to eq [ Proc ]
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'accepts lambdas' do
|
103
|
+
function.call(-> {})
|
104
|
+
expect(stack).to eq [ Proc ]
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'accepts inline blocks' do
|
108
|
+
function.call {}
|
109
|
+
expect(stack).to eq [ Proc ]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when middleware is invalid' do
|
114
|
+
it 'rejects classes that do not implement `.call`' do
|
115
|
+
expect {
|
116
|
+
function.call(Class.new)
|
117
|
+
}.to raise_error(ArgumentError)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'rejects instances that do not respond to `.call`' do
|
121
|
+
expect {
|
122
|
+
function.call(123)
|
123
|
+
}.to raise_error(ArgumentError)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'fails when both instance and block are passed' do
|
127
|
+
expect {
|
128
|
+
function.call(A.new) {}
|
129
|
+
}.to raise_error(ArgumentError)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'rejects nil' do
|
133
|
+
expect {
|
134
|
+
function.call(nil)
|
135
|
+
}.to raise_error(ArgumentError)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#use' do
|
141
|
+
it_behaves_like 'a middleware adder method', :use
|
142
|
+
|
143
|
+
it 'appends middleware in order' do
|
144
|
+
subject.use A
|
145
|
+
subject.use B
|
146
|
+
expect(stack).to eq [ A, B ]
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'reorders duplicates' do
|
150
|
+
subject.use A
|
151
|
+
subject.use B
|
152
|
+
subject.use A
|
153
|
+
expect(stack).to eq [ B, A ]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#prepend' do
|
158
|
+
it_behaves_like 'a middleware adder method', :prepend
|
159
|
+
|
160
|
+
it 'prepends middleware in order' do
|
161
|
+
subject.prepend A
|
162
|
+
subject.prepend B
|
163
|
+
expect(stack).to eq [ B, A ]
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'reorders duplicates' do
|
167
|
+
subject.prepend A
|
168
|
+
subject.prepend B
|
169
|
+
subject.prepend A
|
170
|
+
expect(stack).to eq [ A, B ]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe '#after' do
|
175
|
+
it_behaves_like 'a middleware adder method', :after
|
176
|
+
|
177
|
+
it 'adds middleware where specified' do
|
178
|
+
subject.use A
|
179
|
+
subject.use B
|
180
|
+
subject.after A, C
|
181
|
+
expect(stack).to eq [ A, C, B ]
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'maintans order for duplicates' do
|
185
|
+
subject.use A
|
186
|
+
subject.use B
|
187
|
+
subject.after A, C
|
188
|
+
subject.after A, C
|
189
|
+
expect(stack).to eq [ A, C, B ]
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'works when target is missing' do
|
193
|
+
subject.use A
|
194
|
+
subject.after B, C
|
195
|
+
expect(stack).to eq [ A, C ]
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'works when target is nil' do
|
199
|
+
subject.use A
|
200
|
+
subject.after nil, C
|
201
|
+
expect(stack).to eq [ A, C ]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe '#before' do
|
206
|
+
it_behaves_like 'a middleware adder method', :before
|
207
|
+
|
208
|
+
it 'adds middleware where specified' do
|
209
|
+
subject.use A
|
210
|
+
subject.use B
|
211
|
+
subject.before B, C
|
212
|
+
expect(stack).to eq [ A, C, B ]
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'maintans order for duplicates' do
|
216
|
+
subject.use A
|
217
|
+
subject.use B
|
218
|
+
subject.before B, C
|
219
|
+
subject.before B, C
|
220
|
+
expect(stack).to eq [ A, C, B ]
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'works when target is missing' do
|
224
|
+
subject.use A
|
225
|
+
subject.before B, C
|
226
|
+
expect(stack).to eq [ C, A ]
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'works when target is nil' do
|
230
|
+
subject.use A
|
231
|
+
subject.before nil, C
|
232
|
+
expect(stack).to eq [ C, A ]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe '#empty?' do
|
237
|
+
it 'works with empty middleware' do
|
238
|
+
expect(subject).to be_empty
|
239
|
+
expect(subject.empty?).to be true
|
240
|
+
expect(stack.empty?).to be true
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'works with middleware' do
|
244
|
+
subject.use A
|
245
|
+
expect(subject).not_to be_empty
|
246
|
+
expect(subject.empty?).to be false
|
247
|
+
expect(stack.empty?).to be false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe '#clear' do
|
252
|
+
it 'works' do
|
253
|
+
subject.use A
|
254
|
+
subject.clear
|
255
|
+
expect(subject).to be_empty
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'works with an empty stack' do
|
259
|
+
subject.clear
|
260
|
+
expect(subject).to be_empty
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '#count' do
|
265
|
+
it 'works' do
|
266
|
+
expect(subject.count).to be 0
|
267
|
+
|
268
|
+
subject.use A
|
269
|
+
expect(subject.count).to be 1
|
270
|
+
|
271
|
+
subject.use B
|
272
|
+
expect(subject.count).to be 2
|
273
|
+
|
274
|
+
subject.remove B
|
275
|
+
expect(subject.count).to be 1
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe '#include?' do
|
280
|
+
it 'works' do
|
281
|
+
expect(subject.include?(A)).to be false
|
282
|
+
|
283
|
+
subject.use A
|
284
|
+
expect(subject.include?(A)).to be true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe '#remove' do
|
289
|
+
before do
|
290
|
+
subject.use A
|
291
|
+
subject.use B
|
292
|
+
subject.use C
|
293
|
+
expect(stack).to eq [ A, B, C ]
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'removes middleware' do
|
297
|
+
subject.remove(B)
|
298
|
+
expect(stack).to eq [ A, C ]
|
299
|
+
subject.remove(A)
|
300
|
+
expect(stack).to eq [ C ]
|
301
|
+
subject.remove(C)
|
302
|
+
expect(subject).to be_empty
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'is idempotent' do
|
306
|
+
3.times { subject.remove(A) }
|
307
|
+
expect(stack).to eq [ B, C ]
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'works with nil' do
|
311
|
+
subject.remove(nil)
|
312
|
+
expect(stack).to eq [ A, B, C ]
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe '#replace' do
|
317
|
+
before do
|
318
|
+
subject.use A
|
319
|
+
subject.use B
|
320
|
+
|
321
|
+
expect(stack).to eq [ A, B ]
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'replaces middleware' do
|
325
|
+
subject.replace(A, C)
|
326
|
+
expect(stack).to eq [ C, B ]
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'works with middleware instances' do
|
330
|
+
instance = A.new
|
331
|
+
subject.replace(A, instance)
|
332
|
+
|
333
|
+
expect(subject).to include instance
|
334
|
+
expect(subject).not_to include A
|
335
|
+
|
336
|
+
subject.replace(instance, B)
|
337
|
+
expect(stack).to eq [ B ]
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'fails when target middleware is missing' do
|
341
|
+
expect {
|
342
|
+
subject.replace(C, C)
|
343
|
+
}.to raise_error(RuntimeError)
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'fails when middleware is invalid' do
|
347
|
+
expect {
|
348
|
+
subject.replace(A, nil)
|
349
|
+
}.to raise_error(ArgumentError)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe '.new' do
|
354
|
+
it 'supports block mode' do
|
355
|
+
instance = described_class.new do
|
356
|
+
use A
|
357
|
+
prepend B
|
358
|
+
end
|
359
|
+
|
360
|
+
expect(instance).to include A
|
361
|
+
expect(instance).to include B
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
describe '#index' do
|
366
|
+
before do
|
367
|
+
subject.use A
|
368
|
+
subject.use B
|
369
|
+
end
|
370
|
+
|
371
|
+
it do
|
372
|
+
expect(subject.send(:index, A)).to be 0
|
373
|
+
expect(subject.send(:index, B)).to be 1
|
374
|
+
expect(subject.send(:index, C)).to be nil
|
375
|
+
expect(subject.send(:index, nil)).to be nil
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'is a private method' do
|
379
|
+
expect {
|
380
|
+
subject.index
|
381
|
+
}.to raise_error(NoMethodError, /private/)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
describe 'Meddleware#call' do
|
2
|
+
subject { Meddleware.new }
|
3
|
+
|
4
|
+
let(:middleware_one) { Meddler.new }
|
5
|
+
let(:middleware_two) { Meddler.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
subject.use middleware_one
|
9
|
+
subject.use middleware_two
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'calls the whole chain' do
|
13
|
+
expect(middleware_one).to receive(:call).and_yield
|
14
|
+
expect(middleware_two).to receive(:call).and_yield
|
15
|
+
|
16
|
+
expect {|b| subject.call(&b) }.to yield_control
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'stops propagation if a middleware does not yield' do
|
20
|
+
expect(middleware_one).to receive(:call).and_yield
|
21
|
+
expect(middleware_two).to receive(:call)
|
22
|
+
|
23
|
+
expect {|b| subject.call(&b) }.not_to yield_control
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'recurses' do
|
27
|
+
order = []
|
28
|
+
|
29
|
+
expect(middleware_one).to receive(:call) do |&block|
|
30
|
+
order << 1
|
31
|
+
block.call
|
32
|
+
order << 5
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(middleware_two).to receive(:call) do |&block|
|
36
|
+
order << 2
|
37
|
+
block.call
|
38
|
+
order << 4
|
39
|
+
end
|
40
|
+
|
41
|
+
subject.call { order << 3 }
|
42
|
+
expect(order).to eq((1..5).to_a)
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when arguments are altered' do
|
46
|
+
before do
|
47
|
+
expect(middleware_one).to receive(:call) do |x, &block|
|
48
|
+
block.call(x * 2)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'propagates them' do
|
53
|
+
expect(middleware_two).to receive(:call) do |x, &block|
|
54
|
+
expect(x).to be 2
|
55
|
+
block.call(x * 3)
|
56
|
+
end
|
57
|
+
|
58
|
+
subject.call(1) do |x|
|
59
|
+
expect(x).to be 6
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'propagates them as default arguments' do
|
64
|
+
expect(middleware_two).to receive(:call) do |x, &block|
|
65
|
+
expect(x).to be 2
|
66
|
+
block.call
|
67
|
+
end
|
68
|
+
|
69
|
+
subject.call(1) do |x|
|
70
|
+
expect(x).to be 2
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
describe 'Meddleware#call' do
|
2
|
+
subject { Meddleware.new }
|
3
|
+
|
4
|
+
let(:middleware) { Meddler.new }
|
5
|
+
|
6
|
+
it 'works with no block, no stack' do
|
7
|
+
expect(subject.call).to be nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns the blocks value' do
|
11
|
+
res = subject.call { 123 }
|
12
|
+
expect(res).to be 123
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'works with args' do
|
16
|
+
res = subject.call(:abc) { 123 }
|
17
|
+
expect(res).to be 123
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'passes args through to block' do
|
21
|
+
subject.call(:abc, x: :yz) do |*args|
|
22
|
+
expect(args).to eq [ :abc, { x: :yz } ]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with a middleware class' do
|
27
|
+
it 'instantiates and calls the middleware' do
|
28
|
+
expect(Meddler).to receive(:new).and_return(middleware)
|
29
|
+
expect(middleware).to receive(:call)
|
30
|
+
|
31
|
+
subject.use Meddler
|
32
|
+
subject.call
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with a middleware instance' do
|
37
|
+
before do
|
38
|
+
subject.use middleware
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'calls middleware' do
|
42
|
+
expect(middleware).to receive(:call)
|
43
|
+
subject.call
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'calls middleware with args' do
|
47
|
+
expect(middleware).to receive(:call).with(:abc)
|
48
|
+
subject.call(:abc)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'calls middleware with block' do
|
52
|
+
expect(middleware).to receive(:call) do |&block|
|
53
|
+
expect(block).to be_a Proc
|
54
|
+
end
|
55
|
+
|
56
|
+
subject.call {}
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'calls middleware with args and block' do
|
60
|
+
expect(middleware).to receive(:call) do |a, b, &block|
|
61
|
+
expect(a).to be :a
|
62
|
+
expect(b).to eq({ b: :c })
|
63
|
+
expect(block).to be_a Proc
|
64
|
+
end
|
65
|
+
|
66
|
+
subject.call(:a, b: :c) {}
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'can return a value' do
|
70
|
+
expect(middleware).to receive(:call) { 123 }
|
71
|
+
|
72
|
+
expect(subject.call).to be 123
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when middleware calls block without explicit arguments' do
|
76
|
+
before do
|
77
|
+
expect(middleware).to receive(:call) do |&block|
|
78
|
+
block.call
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'implicitly passes along original arguments' do
|
83
|
+
subject.call(:abc) do |arg|
|
84
|
+
expect(arg).to be :abc
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when middleware calls block with explicit arguments' do
|
90
|
+
it 'can add arguments' do
|
91
|
+
expect(middleware).to receive(:call) do |&block|
|
92
|
+
block.call(:abc)
|
93
|
+
end
|
94
|
+
|
95
|
+
subject.call do |arg|
|
96
|
+
expect(arg).to be :abc
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'can override the arguments passed on' do
|
101
|
+
expect(middleware).to receive(:call) do |&block|
|
102
|
+
block.call(:abc)
|
103
|
+
end
|
104
|
+
|
105
|
+
subject.call(123) do |arg|
|
106
|
+
expect(arg).to be :abc
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'can remove arguments' do
|
111
|
+
expect(middleware).to receive(:call) do |&block|
|
112
|
+
block.call(nil)
|
113
|
+
end
|
114
|
+
|
115
|
+
subject.call(:abc, :xyz) do |a, x|
|
116
|
+
expect(a).to be nil
|
117
|
+
expect(x).to be nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when middleware meddles with pass-by-ref arguments' do
|
123
|
+
before do
|
124
|
+
expect(middleware).to receive(:call) do |arg, &block|
|
125
|
+
arg[:abc] = 123
|
126
|
+
block.call
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'alters the value for the block' do
|
131
|
+
subject.call({}) do |info|
|
132
|
+
expect(info).to eq({ abc: 123 })
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'alters the value for the caller' do
|
137
|
+
info = {}
|
138
|
+
subject.call(info)
|
139
|
+
expect(info).to eq({ abc: 123 })
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when middleware meddles with pass-by-value arguments' do
|
144
|
+
before do
|
145
|
+
expect(middleware).to receive(:call) do |arg, &block|
|
146
|
+
arg = 123
|
147
|
+
block.call
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'has no effect...unfortunately' do
|
152
|
+
subject.call(:abc) do |arg|
|
153
|
+
expect(arg).to be :abc
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'with a middleware instance and arguments' do
|
160
|
+
before do
|
161
|
+
subject.use middleware, :abc
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'curries the arguments' do
|
165
|
+
expect(middleware).to receive(:call) do |*args, &block|
|
166
|
+
expect(args).to eq([ :abc ])
|
167
|
+
end
|
168
|
+
|
169
|
+
subject.call
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'curries and appends extra arguments' do
|
173
|
+
expect(middleware).to receive(:call) do |*args, &block|
|
174
|
+
expect(args).to eq([ :abc, :xyz ])
|
175
|
+
end
|
176
|
+
|
177
|
+
subject.call(:xyz)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'curries and appends, without yielding implicitly' do
|
181
|
+
res = subject.call(:xyz) { 123 }
|
182
|
+
expect(res).to be nil
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'curries and appends, and can yield explicitly' do
|
186
|
+
expect(middleware).to receive(:call) do |*args, &block|
|
187
|
+
block.call
|
188
|
+
end
|
189
|
+
|
190
|
+
res = subject.call(:xyz) { 123 }
|
191
|
+
expect(res).to be 123
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'with a middleware Proc' do
|
196
|
+
it 'passes arguments to the Proc' do
|
197
|
+
fn = proc {|arg| expect(arg).to be :abc }
|
198
|
+
expect(fn).to receive(:call).and_call_original
|
199
|
+
|
200
|
+
subject.use fn
|
201
|
+
subject.call(:abc)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'curries arguments' do
|
205
|
+
fn = proc {|*args| expect(args).to eq [ :xyz, :abc ] }
|
206
|
+
expect(fn).to receive(:call).and_call_original
|
207
|
+
|
208
|
+
subject.use fn, :xyz
|
209
|
+
subject.call(:abc)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'can alter arguments' do
|
213
|
+
fn = proc {|data| data[:abc] = 123 }
|
214
|
+
subject.use fn
|
215
|
+
|
216
|
+
data = {}
|
217
|
+
subject.call(data)
|
218
|
+
expect(data).to eq({ abc: 123 })
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'can not abort middleware chain...unfortunately' do
|
222
|
+
fn = proc { return }
|
223
|
+
|
224
|
+
subject.use fn
|
225
|
+
expect {
|
226
|
+
subject.call
|
227
|
+
}.to raise_error(LocalJumpError)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'calls yield implicitly' do
|
231
|
+
fn = proc {}
|
232
|
+
expect(fn).to receive(:call).and_call_original
|
233
|
+
|
234
|
+
subject.use fn
|
235
|
+
res = subject.call { 123 }
|
236
|
+
expect(res).to be 123
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context 'with a middleware Lambda' do
|
241
|
+
it 'does not explicitly call yield' do
|
242
|
+
fn = ->{}
|
243
|
+
expect(fn).to receive(:call).and_call_original
|
244
|
+
|
245
|
+
subject.use fn
|
246
|
+
res = subject.call { 123 }
|
247
|
+
expect(res).to be nil
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'does can explicitly yield' do
|
251
|
+
fn = ->(&block) { block.call }
|
252
|
+
expect(fn).to receive(:call).and_call_original
|
253
|
+
|
254
|
+
subject.use fn
|
255
|
+
res = subject.call { 123 }
|
256
|
+
expect(res).to be 123
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module MyList
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def middleware(&block)
|
5
|
+
(@middleware ||= Meddleware.new).tap do
|
6
|
+
@middleware.instance_eval(&block) if block_given?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def generate(n)
|
11
|
+
middleware.call(n) {|n| (1..n).to_a }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class OneExtra
|
16
|
+
def call(n)
|
17
|
+
yield(n + 1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Doubler
|
22
|
+
def call(*)
|
23
|
+
yield.map {|x| x * 2 }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
describe MyList do
|
29
|
+
before do
|
30
|
+
MyList.middleware do
|
31
|
+
use OneExtra
|
32
|
+
use Doubler
|
33
|
+
|
34
|
+
# loggers
|
35
|
+
prepend {|x| puts "n starts as #{x}" }
|
36
|
+
append {|x| puts "n ends as #{x}" }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'calls middleware chain and generates a list' do
|
41
|
+
res = nil
|
42
|
+
|
43
|
+
expect {
|
44
|
+
res = MyList.generate(2)
|
45
|
+
}.to output("n starts as 2\nn ends as 3\n").to_stdout
|
46
|
+
|
47
|
+
expect(res).to eq [ 2, 4, 6 ]
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meddleware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Pepper
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
83
|
+
description: A middleware framework to make meddling easy.
|
84
84
|
email:
|
85
85
|
executables: []
|
86
86
|
extensions: []
|
@@ -88,7 +88,10 @@ extra_rdoc_files: []
|
|
88
88
|
files:
|
89
89
|
- lib/meddleware.rb
|
90
90
|
- lib/meddleware/version.rb
|
91
|
-
- spec/
|
91
|
+
- spec/build_meddleware_spec.rb
|
92
|
+
- spec/call_meddleware_chain_spec.rb
|
93
|
+
- spec/call_meddleware_spec.rb
|
94
|
+
- spec/examples_spec.rb
|
92
95
|
homepage: https://github.com/dpep/meddleware_rb
|
93
96
|
licenses:
|
94
97
|
- MIT
|
@@ -113,4 +116,7 @@ signing_key:
|
|
113
116
|
specification_version: 4
|
114
117
|
summary: Meddleware
|
115
118
|
test_files:
|
116
|
-
- spec/
|
119
|
+
- spec/call_meddleware_spec.rb
|
120
|
+
- spec/call_meddleware_chain_spec.rb
|
121
|
+
- spec/build_meddleware_spec.rb
|
122
|
+
- spec/examples_spec.rb
|
data/spec/meddleware_spec.rb
DELETED