dry-container 0.7.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,664 +0,0 @@
1
- RSpec.shared_examples 'a container' do
2
- describe 'configuration' do
3
- describe 'registry' do
4
- describe 'default' do
5
- it { expect(klass.config.registry).to be_a(Dry::Container::Registry) }
6
- end
7
-
8
- describe 'custom' do
9
- let(:custom_registry) { double('Registry') }
10
- let(:key) { :key }
11
- let(:item) { :item }
12
- let(:options) { {} }
13
-
14
- before do
15
- klass.configure do |config|
16
- config.registry = custom_registry
17
- end
18
-
19
- allow(custom_registry).to receive(:call)
20
- end
21
-
22
- after do
23
- # HACK: Have to reset the configuration so that it doesn't
24
- # interfere with other specs
25
- klass.configure do |config|
26
- config.registry = Dry::Container::Registry.new
27
- end
28
- end
29
-
30
- subject! { container.register(key, item, options) }
31
-
32
- it do
33
- expect(custom_registry).to have_received(:call).with(
34
- container._container,
35
- key,
36
- item,
37
- options
38
- )
39
- end
40
- end
41
- end
42
-
43
- describe 'resolver' do
44
- describe 'default' do
45
- it { expect(klass.config.resolver).to be_a(Dry::Container::Resolver) }
46
- end
47
-
48
- describe 'custom' do
49
- let(:custom_resolver) { double('Resolver') }
50
- let(:item) { double('Item') }
51
- let(:key) { :key }
52
-
53
- before do
54
- klass.configure do |config|
55
- config.resolver = custom_resolver
56
- end
57
-
58
- allow(custom_resolver).to receive(:call).and_return(item)
59
- end
60
-
61
- after do
62
- # HACK: Have to reset the configuration so that it doesn't
63
- # interfere with other specs
64
- klass.configure do |config|
65
- config.resolver = Dry::Container::Resolver.new
66
- end
67
- end
68
-
69
- subject! { container.resolve(key) }
70
-
71
- it { expect(custom_resolver).to have_received(:call).with(container._container, key) }
72
- it { is_expected.to eq(item) }
73
- end
74
- end
75
-
76
- describe 'namespace_separator' do
77
- describe 'default' do
78
- it { expect(klass.config.namespace_separator).to eq('.') }
79
- end
80
-
81
- describe 'custom' do
82
- let(:custom_registry) { double('Registry') }
83
- let(:key) { 'key' }
84
- let(:namespace_separator) { '-' }
85
- let(:namespace) { 'one' }
86
-
87
- before do
88
- klass.configure do |config|
89
- config.namespace_separator = namespace_separator
90
- end
91
-
92
- container.namespace(namespace) do
93
- register('key', 'item')
94
- end
95
- end
96
-
97
- after do
98
- # HACK: Have to reset the configuration so that it doesn't
99
- # interfere with other specs
100
- klass.configure do |config|
101
- config.namespace_separator = '.'
102
- end
103
- end
104
-
105
- subject! { container.resolve([namespace, key].join(namespace_separator)) }
106
-
107
- it { is_expected.to eq('item') }
108
- end
109
- end
110
- end
111
-
112
- context 'with default configuration' do
113
- describe 'registering a block' do
114
- context 'without options' do
115
- context 'without arguments' do
116
- it 'registers and resolves an object' do
117
- container.register(:item) { 'item' }
118
-
119
- expect(container.keys).to eq(['item'])
120
- expect(container.key?(:item)).to be true
121
- expect(container.resolve(:item)).to eq('item')
122
- end
123
- end
124
-
125
- context 'with arguments' do
126
- it 'registers and resolves a proc' do
127
- container.register(:item) { |item| item }
128
-
129
- expect(container.resolve(:item).call('item')).to eq('item')
130
- end
131
-
132
- it 'does not call a proc on resolving if one accepts an arbitrary number of keyword arguments' do
133
- container.register(:item) { |*| 'item' }
134
-
135
- expect(container.resolve(:item)).to be_a_kind_of Proc
136
- expect(container.resolve(:item).call).to eq('item')
137
- end
138
- end
139
- end
140
-
141
- context 'with option call: false' do
142
- it 'registers and resolves a proc' do
143
- container.register(:item, call: false) { 'item' }
144
-
145
- expect(container.keys).to eq(['item'])
146
- expect(container.key?(:item)).to be true
147
- expect(container.resolve(:item).call).to eq('item')
148
- expect(container[:item].call).to eq('item')
149
- end
150
- end
151
- end
152
-
153
- describe 'registering a proc' do
154
- context 'without options' do
155
- context 'without arguments' do
156
- it 'registers and resolves an object' do
157
- container.register(:item, proc { 'item' })
158
-
159
- expect(container.keys).to eq(['item'])
160
- expect(container.key?(:item)).to be true
161
- expect(container.resolve(:item)).to eq('item')
162
- expect(container[:item]).to eq('item')
163
- end
164
- end
165
-
166
- context 'with arguments' do
167
- it 'registers and resolves a proc' do
168
- container.register(:item, proc { |item| item })
169
-
170
- expect(container.keys).to eq(['item'])
171
- expect(container.key?(:item)).to be true
172
- expect(container.resolve(:item).call('item')).to eq('item')
173
- expect(container[:item].call('item')).to eq('item')
174
- end
175
- end
176
- end
177
-
178
- context 'with option call: false' do
179
- it 'registers and resolves a proc' do
180
- container.register(:item, proc { 'item' }, call: false)
181
-
182
- expect(container.keys).to eq(['item'])
183
- expect(container.key?(:item)).to be true
184
- expect(container.resolve(:item).call).to eq('item')
185
- expect(container[:item].call).to eq('item')
186
- end
187
- end
188
-
189
- context 'with option memoize: true' do
190
- it 'registers and resolves a proc' do
191
- container.register(:item, proc { 'item' }, memoize: true)
192
-
193
- expect(container[:item]).to be container[:item]
194
- expect(container.keys).to eq(['item'])
195
- expect(container.key?(:item)).to be true
196
- expect(container.resolve(:item)).to eq('item')
197
- expect(container[:item]).to eq('item')
198
- end
199
-
200
- it 'only resolves the proc once' do
201
- resolved_times = 0
202
-
203
- container.register(:item, proc { resolved_times += 1 }, memoize: true)
204
-
205
- expect(container.resolve(:item)).to be 1
206
- expect(container.resolve(:item)).to be 1
207
- end
208
-
209
- context 'when receiving something other than a proc' do
210
- it do
211
- expect { container.register(:item, 'Hello!', memoize: true) }.to raise_error(Dry::Container::Error)
212
- end
213
- end
214
- end
215
- end
216
-
217
- describe 'registering an object' do
218
- context 'without options' do
219
- it 'registers and resolves the object' do
220
- item = 'item'
221
- container.register(:item, item)
222
-
223
- expect(container.keys).to eq(['item'])
224
- expect(container.key?(:item)).to be true
225
- expect(container.resolve(:item)).to be(item)
226
- expect(container[:item]).to be(item)
227
- end
228
- end
229
-
230
- context 'with option call: false' do
231
- it 'registers and resolves an object' do
232
- item = -> { 'test' }
233
- container.register(:item, item, call: false)
234
-
235
- expect(container.keys).to eq(['item'])
236
- expect(container.key?(:item)).to be true
237
- expect(container.resolve(:item)).to eq(item)
238
- expect(container[:item]).to eq(item)
239
- end
240
- end
241
- end
242
-
243
- describe 'registering with the same key multiple times' do
244
- it do
245
- container.register(:item, proc { 'item' })
246
-
247
- expect { container.register(:item, proc { 'item' }) }.to raise_error(Dry::Container::Error)
248
- end
249
- end
250
-
251
- describe 'resolving with a key that has not been registered' do
252
- it do
253
- expect(container.key?(:item)).to be false
254
- expect { container.resolve(:item) }.to raise_error(Dry::Container::Error)
255
- end
256
- end
257
-
258
- describe 'mixing Strings and Symbols' do
259
- it do
260
- container.register(:item, 'item')
261
- expect(container.resolve('item')).to eql('item')
262
- end
263
- end
264
-
265
- describe '#merge' do
266
- let(:key) { :key }
267
- let(:other) { Dry::Container.new }
268
-
269
- before do
270
- other.register(key) { :item }
271
- end
272
-
273
- context 'without namespace argument' do
274
- subject! { container.merge(other) }
275
-
276
- it { expect(container.resolve(key)).to be(:item) }
277
- it { expect(container[key]).to be(:item) }
278
- end
279
-
280
- context 'with namespace argument' do
281
- subject! { container.merge(other, namespace: namespace) }
282
-
283
- context 'when namespace is nil' do
284
- let(:namespace) { nil }
285
-
286
- it { expect(container.resolve(key)).to be(:item) }
287
- it { expect(container[key]).to be(:item) }
288
- end
289
-
290
- context 'when namespace is not nil' do
291
- let(:namespace) { 'namespace' }
292
-
293
- it { expect(container.resolve("#{namespace}.#{key}")).to be(:item) }
294
- it { expect(container["#{namespace}.#{key}"]).to be(:item) }
295
- end
296
- end
297
- end
298
-
299
- describe '#key?' do
300
- let(:key) { :key }
301
-
302
- before do
303
- container.register(key) { :item }
304
- end
305
-
306
- subject! { container.key?(resolve_key) }
307
-
308
- context 'when key exists in container' do
309
- let(:resolve_key) { key }
310
-
311
- it { is_expected.to be true }
312
- end
313
-
314
- context 'when key does not exist in container' do
315
- let(:resolve_key) { :random }
316
-
317
- it { is_expected.to be false }
318
- end
319
- end
320
-
321
- describe '#keys' do
322
- let(:keys) { [:key_1, :key_2] }
323
- let(:expected_keys) { ['key_1', 'key_2'] }
324
-
325
- before do
326
- keys.each do |key|
327
- container.register(key) { :item }
328
- end
329
- end
330
-
331
- subject! { container.keys }
332
-
333
- it 'returns stringified versions of all registered keys' do
334
- is_expected.to match_array(expected_keys)
335
- end
336
- end
337
-
338
- describe '#each_key' do
339
- let(:keys) { [:key_1, :key_2] }
340
- let(:expected_keys) { ['key_1', 'key_2'] }
341
- let!(:yielded_keys) { [] }
342
-
343
- before do
344
- keys.each do |key|
345
- container.register(key) { :item }
346
- end
347
- end
348
-
349
- subject! do
350
- container.each_key { |key| yielded_keys << key }
351
- end
352
-
353
- it 'yields stringified versions of all registered keys to the block' do
354
- expect(yielded_keys).to match_array(expected_keys)
355
- end
356
-
357
- it 'returns the container' do
358
- is_expected.to eq(container)
359
- end
360
- end
361
-
362
- describe '#each' do
363
- let(:keys) { [:key_1, :key_2] }
364
- let(:expected_key_value_pairs) { [['key_1', 'value_for_key_1'], ['key_2', 'value_for_key_2']] }
365
- let!(:yielded_key_value_pairs) { [] }
366
-
367
- before do
368
- keys.each do |key|
369
- container.register(key) { "value_for_#{key}" }
370
- end
371
- end
372
-
373
- subject! do
374
- container.each { |key, value| yielded_key_value_pairs << [key, value] }
375
- end
376
-
377
- it 'yields stringified versions of all registered keys to the block' do
378
- expect(yielded_key_value_pairs).to match_array(expected_key_value_pairs)
379
- end
380
-
381
- it 'returns the container' do
382
- is_expected.to eq(expected_key_value_pairs)
383
- end
384
- end
385
-
386
- describe '#decorate' do
387
- require 'delegate'
388
-
389
- let(:key) { :key }
390
- let(:decorated_class_spy) { spy(:decorated_class_spy) }
391
- let(:decorated_class) { Class.new }
392
-
393
- context 'for callable item' do
394
- before do
395
- allow(decorated_class_spy).to receive(:new) { decorated_class.new }
396
- container.register(key, memoize: memoize) { decorated_class_spy.new }
397
- container.decorate(key, with: SimpleDelegator)
398
- end
399
-
400
- context 'memoize false' do
401
- let(:memoize) { false }
402
-
403
- it 'does not call the block until the key is resolved' do
404
- expect(decorated_class_spy).not_to have_received(:new)
405
- container.resolve(key)
406
- expect(decorated_class_spy).to have_received(:new)
407
- end
408
-
409
- specify do
410
- expect(container[key]).to be_instance_of(SimpleDelegator)
411
- expect(container[key].__getobj__).to be_instance_of(decorated_class)
412
- expect(container[key]).not_to be(container[key])
413
- expect(container[key].__getobj__).not_to be(container[key].__getobj__)
414
- end
415
- end
416
-
417
- context 'memoize true' do
418
- let(:memoize) { true }
419
-
420
- specify do
421
- expect(container[key]).to be_instance_of(SimpleDelegator)
422
- expect(container[key].__getobj__).to be_instance_of(decorated_class)
423
- expect(container[key]).to be(container[key])
424
- end
425
- end
426
- end
427
-
428
- context 'for not callable item' do
429
- describe 'wrapping' do
430
- before do
431
- container.register(key, call: false) { "value" }
432
- container.decorate(key, with: SimpleDelegator)
433
- end
434
-
435
- it 'expected to be an instance of SimpleDelegator' do
436
- expect(container.resolve(key)).to be_instance_of(SimpleDelegator)
437
- expect(container.resolve(key).__getobj__.call).to eql("value")
438
- end
439
- end
440
-
441
- describe 'memoization' do
442
- before do
443
- @called = 0
444
- container.register(key, 'value')
445
-
446
- container.decorate(key) do |value|
447
- @called += 1
448
- "<#{value}>"
449
- end
450
- end
451
-
452
- it 'decorates static value only once' do
453
- expect(container.resolve(key)).to eql('<value>')
454
- expect(container.resolve(key)).to eql('<value>')
455
- expect(@called).to be(1)
456
- end
457
- end
458
- end
459
-
460
- context 'with an instance as a decorator' do
461
- let(:decorator) do
462
- double.tap do |decorator|
463
- allow(decorator).to receive(:call) { |input| "decorated #{input}" }
464
- end
465
- end
466
-
467
- before do
468
- container.register(key) { "value" }
469
- container.decorate(key, with: decorator)
470
- end
471
-
472
- it 'expected to pass original value to decorator#call method' do
473
- expect(container.resolve(key)).to eq("decorated value")
474
- end
475
- end
476
- end
477
-
478
- describe 'namespace' do
479
- context 'when block does not take arguments' do
480
- before do
481
- container.namespace('one') do
482
- register('two', 2)
483
- end
484
- end
485
-
486
- subject! { container.resolve('one.two') }
487
-
488
- it 'registers items under the given namespace' do
489
- is_expected.to eq(2)
490
- end
491
- end
492
-
493
- context 'when block takes arguments' do
494
- before do
495
- container.namespace('one') do |c|
496
- c.register('two', 2)
497
- end
498
- end
499
-
500
- subject! { container.resolve('one.two') }
501
-
502
- it 'registers items under the given namespace' do
503
- is_expected.to eq(2)
504
- end
505
- end
506
-
507
- context 'with nesting' do
508
- before do
509
- container.namespace('one') do
510
- namespace('two') do
511
- register('three', 3)
512
- end
513
- end
514
- end
515
-
516
- subject! { container.resolve('one.two.three') }
517
-
518
- it 'registers items under the given namespaces' do
519
- is_expected.to eq(3)
520
- end
521
- end
522
-
523
- context 'with nesting and when block takes arguments' do
524
- before do
525
- container.namespace('one') do |c|
526
- c.register('two', 2)
527
- c.register('three', c.resolve('two'))
528
- end
529
- end
530
-
531
- subject! { container.resolve('one.three') }
532
-
533
- it 'resolves items relative to the namespace' do
534
- is_expected.to eq(2)
535
- end
536
- end
537
- end
538
-
539
- describe 'import' do
540
- it 'allows importing of namespaces' do
541
- ns = Dry::Container::Namespace.new('one') do
542
- register('two', 2)
543
- end
544
-
545
- container.import(ns)
546
-
547
- expect(container.resolve('one.two')).to eq(2)
548
- end
549
-
550
- it 'allows importing of nested namespaces' do
551
- ns = Dry::Container::Namespace.new('two') do
552
- register('three', 3)
553
- end
554
-
555
- container.namespace('one') do
556
- import(ns)
557
- end
558
-
559
- expect(container.resolve('one.two.three')).to eq(3)
560
- end
561
- end
562
- end
563
-
564
- describe 'stubbing' do
565
- before do
566
- container.enable_stubs!
567
-
568
- container.register(:item, 'item')
569
- container.register(:foo, 'bar')
570
- end
571
-
572
- after do
573
- container.unstub
574
- end
575
-
576
- it 'keys can be stubbed' do
577
- container.stub(:item, 'stub')
578
- expect(container.resolve(:item)).to eql('stub')
579
- expect(container[:item]).to eql('stub')
580
- end
581
-
582
- it 'only other keys remain accesible' do
583
- container.stub(:item, 'stub')
584
- expect(container.resolve(:foo)).to eql('bar')
585
- expect(container[:foo]).to eql('bar')
586
- end
587
-
588
- it 'keys can be reverted back to their original value' do
589
- container.stub(:item, 'stub')
590
- container.unstub(:item)
591
-
592
- expect(container.resolve(:item)).to eql('item')
593
- expect(container[:item]).to eql('item')
594
- end
595
-
596
- describe 'with block argument' do
597
- it 'executes the block with the given stubs' do
598
- expect { |b| container.stub(:item, 'stub', &b) }.to yield_control
599
- end
600
-
601
- it 'keys are stubbed only while inside the block' do
602
- container.stub(:item, 'stub') do
603
- expect(container.resolve(:item)).to eql('stub')
604
- end
605
-
606
- expect(container.resolve(:item)).to eql('item')
607
- end
608
- end
609
-
610
- describe 'mixing Strings and Symbols' do
611
- it do
612
- container.stub(:item, 'stub')
613
- expect(container.resolve('item')).to eql('stub')
614
- end
615
- end
616
-
617
- it 'raises an error when key is missing' do
618
- expect { container.stub(:non_existing, 'something') }.
619
- to raise_error(ArgumentError, 'cannot stub "non_existing" - no such key in container')
620
- end
621
- end
622
-
623
- describe '.freeze' do
624
- before do
625
- container.register(:foo, 'bar')
626
- end
627
-
628
- it 'allows to freeze a container so that nothing can be registered later' do
629
- container.freeze
630
- error = RUBY_VERSION >= '2.5' ? FrozenError : RuntimeError
631
- expect { container.register(:baz, 'quux') }.to raise_error(error)
632
- expect(container).to be_frozen
633
- end
634
-
635
- it 'returns self back' do
636
- expect(container.freeze).to be(container)
637
- end
638
- end
639
-
640
- describe '.dup' do
641
- it "returns a copy that doesn't share registered keys with the parent" do
642
- container.dup.register(:foo, 'bar')
643
- expect(container.key?(:foo)).to be false
644
- end
645
- end
646
-
647
- describe '.clone' do
648
- it "returns a copy that doesn't share registered keys with the parent" do
649
- container.clone.register(:foo, 'bar')
650
- expect(container.key?(:foo)).to be false
651
- end
652
-
653
- it 're-uses frozen container' do
654
- expect(container.freeze.clone).to be_frozen
655
- expect(container.clone._container).to be(container._container)
656
- end
657
- end
658
-
659
- describe '.resolve' do
660
- it 'accepts a fallback block' do
661
- expect(container.resolve('missing') { :fallback }).to be(:fallback)
662
- end
663
- end
664
- end