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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af421278a6c18f72f28e6ad86e214bcbaab0e77cc9e77a58db94493045be3bf7
4
- data.tar.gz: 912d29bc7b291ad682cdf7aadcfd05631aec896cc578957048f233478f5cbb2f
3
+ metadata.gz: 453a4fea42903735f1b7e502f5fb5ba323ecfd1240db7d476453dabd0e773f8d
4
+ data.tar.gz: c459598e2a9145a7ad8a4128cefc5de8ed3a018db28e1cf95a24a26b15b42883
5
5
  SHA512:
6
- metadata.gz: 0b792cee28734eeea2b632d17e45ec139b92eaeb8dc8e5d9421dd27ebf54f62dbbf63453b526d50c483406f502dfff696785ea37a31ecbc988a295333b5c728f
7
- data.tar.gz: b854aa5aa9dee535ea746ac333dcbfd222a0a11dbc36d6262e96fe2930ef8827a5c93c08afb41d38564b79079d43d03b2225d8e0b87cbe7ce63c1ac5b1705965
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
- include Enumerable
5
-
6
- def initialize
4
+ def initialize(&block)
7
5
  instance_eval(&block) if block_given?
8
6
  end
9
7
 
10
- def each(&block)
11
- stack.each(&block)
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 index(klass)
15
- stack.index {|entry| entry.klass == klass }
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 remove(klass)
19
- stack.reject! { |entry| entry.klass == klass }
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 use(klass, *args)
23
- remove(klass)
24
- stack << Entry.new(klass, args)
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 prepend(klass, *args)
29
- remove(klass)
30
- stack.insert(0, Entry.new(klass, args))
35
+ def include?(klass)
36
+ !!index(klass)
31
37
  end
32
38
 
33
- def after(after_klass, klass, *args)
34
- remove(klass)
35
- i = index(after_klass) || count - 1
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 before(before_klass, klass, *args)
40
- remove(klass)
41
- i = index(before_klass) || 0
42
- stack.insert(i, Entry.new(klass, args))
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 empty?
46
- stack.empty?
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 call(*args, &block)
54
- chain = map(&:build)
55
- traverse = proc do |*updated_args|
56
- args = updated_args unless updated_args.empty?
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 *args
81
+ yield(*args) if block_given?
59
82
  else
60
- chain.shift.call(*args, &traverse)
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
- Entry = Struct.new(:klass, :args) do
73
- def build
74
- klass.new(*args)
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
@@ -1,3 +1,3 @@
1
1
  class Meddleware
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -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.1
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-01 00:00:00.000000000 Z
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: work...within limits
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/meddleware_spec.rb
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/meddleware_spec.rb
119
+ - spec/call_meddleware_spec.rb
120
+ - spec/call_meddleware_chain_spec.rb
121
+ - spec/build_meddleware_spec.rb
122
+ - spec/examples_spec.rb
@@ -1,3 +0,0 @@
1
- describe Meddleware do
2
-
3
- end