dry-container 0.7.2 → 0.10.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.
@@ -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