accord 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +7 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +7 -0
  4. data/Gemfile.lock +43 -0
  5. data/accord.gemspec +20 -0
  6. data/lib/accord.rb +4 -0
  7. data/lib/accord/adapter_registry.rb +106 -0
  8. data/lib/accord/base_registry.rb +73 -0
  9. data/lib/accord/declarations.rb +131 -0
  10. data/lib/accord/exceptions.rb +22 -0
  11. data/lib/accord/extendor.rb +36 -0
  12. data/lib/accord/extendor_container.rb +43 -0
  13. data/lib/accord/interface.rb +189 -0
  14. data/lib/accord/interface_body.rb +73 -0
  15. data/lib/accord/interface_members.rb +31 -0
  16. data/lib/accord/interface_method.rb +27 -0
  17. data/lib/accord/interfaces.rb +471 -0
  18. data/lib/accord/nested_key_hash.rb +66 -0
  19. data/lib/accord/ro.rb +65 -0
  20. data/lib/accord/signature_info.rb +76 -0
  21. data/lib/accord/specification.rb +83 -0
  22. data/lib/accord/subscription_registry.rb +76 -0
  23. data/lib/accord/tags.rb +25 -0
  24. data/lib/accord/version.rb +3 -0
  25. data/spec/adapter_registry_spec.rb +296 -0
  26. data/spec/declarations_spec.rb +144 -0
  27. data/spec/extendor_container_spec.rb +101 -0
  28. data/spec/extendor_spec.rb +203 -0
  29. data/spec/integration/adaptation_spec.rb +86 -0
  30. data/spec/integration/adapter_for_class_declaration_spec.rb +22 -0
  31. data/spec/integration/adapter_hook_spec.rb +41 -0
  32. data/spec/integration/default_adapters_spec.rb +81 -0
  33. data/spec/integration/hash_adapters_spec.rb +20 -0
  34. data/spec/integration/interface_declaration_spec.rb +258 -0
  35. data/spec/integration/multi_adapters_spec.rb +83 -0
  36. data/spec/integration/named_adapters_spec.rb +54 -0
  37. data/spec/integration/single_adapters_spec.rb +93 -0
  38. data/spec/integration/subscriptions_spec.rb +245 -0
  39. data/spec/integration/verification_spec.rb +215 -0
  40. data/spec/interface_body_spec.rb +157 -0
  41. data/spec/interface_members_spec.rb +57 -0
  42. data/spec/interface_spec.rb +147 -0
  43. data/spec/nested_key_hash_spec.rb +140 -0
  44. data/spec/signature_info_spec.rb +65 -0
  45. data/spec/spec_helper.rb +2 -0
  46. data/spec/specification_spec.rb +246 -0
  47. data/spec/subscription_registry_spec.rb +206 -0
  48. data/spec/tags_spec.rb +38 -0
  49. metadata +134 -0
@@ -0,0 +1,66 @@
1
+ module Accord
2
+ class NestedKeyHash
3
+ def [](keys)
4
+ keys = keys.dup
5
+ last_key = keys.pop
6
+ last_hash = keys.inject(hash) do |partial, key|
7
+ return nil unless partial.has_key?(key)
8
+ partial[key]
9
+ end
10
+ last_hash[last_key]
11
+ end
12
+
13
+ def []=(keys, value)
14
+ keys = keys.dup
15
+ last_key = keys.pop
16
+ last_hash = keys.inject(hash) { |partial, key| partial[key] ||= {} }
17
+ last_hash[last_key] = value
18
+ end
19
+
20
+ def delete(keys)
21
+ keys = keys.dup
22
+ last_hash = {}
23
+ result = nil
24
+ while last_hash.size == 0 && keys.any?
25
+ last_key = keys.pop
26
+ last_hash = keys.inject(hash) { |partial, key| partial[key] || {} }
27
+ partial_result = last_hash.delete(last_key)
28
+ result ||= partial_result
29
+ end
30
+ result
31
+ end
32
+
33
+ def detect_expansion(keys)
34
+ keys.inject(hash) do |partial, part|
35
+ expansion = yield(part)
36
+ valid_key = expansion.detect { |key| partial.has_key?(key) }
37
+ return unless valid_key
38
+ partial[valid_key]
39
+ end
40
+ end
41
+
42
+ def select_expansions(keys, partial=hash, results=[], &block)
43
+ if keys.size == 1
44
+ expansion = block.call(keys.first)
45
+ valid_expansions = expansion.select { |key| partial.has_key?(key) }
46
+ valid_expansions.each { |key| results << partial[key] }
47
+ return results
48
+ end
49
+ keys = keys.dup
50
+ first_key = keys.shift
51
+ expansion = block.call(first_key)
52
+ valid_expansions = expansion.select { |key| partial.has_key?(key) }
53
+ return results unless valid_expansions.any?
54
+ valid_expansions.each do |key|
55
+ select_expansions(keys, partial[key], results, &block)
56
+ end
57
+ results
58
+ end
59
+
60
+ private
61
+
62
+ def hash
63
+ @hash ||= {}
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,65 @@
1
+ module Accord
2
+ class RO
3
+ CACHE_IVAR_NAME = :@_accord_ro_cache_
4
+
5
+ def initialize(item, &block)
6
+ @item = item
7
+ @block = block
8
+ end
9
+
10
+ def resolve
11
+ cached = from_cache
12
+ return cached if cached
13
+
14
+ bases = block.call(item)
15
+ bases_ros = bases.map { |b| RO.new(b, &block).resolve }
16
+ merge([[item]] + bases_ros + [bases]).tap { |ro| cache(ro) }
17
+ end
18
+
19
+ def changed
20
+ invalidate
21
+ resolve
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :item
27
+ attr_reader :block
28
+
29
+ def from_cache
30
+ item.instance_variable_get(CACHE_IVAR_NAME)
31
+ end
32
+
33
+ def cache(ro)
34
+ item.instance_variable_set(CACHE_IVAR_NAME, ro)
35
+ end
36
+
37
+ def invalidate
38
+ item.instance_variable_set(CACHE_IVAR_NAME, nil)
39
+ end
40
+
41
+ def merge(sequences)
42
+ sequences = sequences.map { |seq| seq.dup }
43
+ result = []
44
+
45
+ loop do
46
+ sequences.delete_if { |sequence| sequence.empty? }
47
+ return result unless sequences.any?
48
+
49
+ good = sequences.detect do |current|
50
+ sequences.all? { |seq| !tail(seq).include?(current.first) }
51
+ end
52
+
53
+ raise TypeError, "inconsistent hierarchy" unless good
54
+
55
+ head = good.first
56
+ sequences.each { |seq| seq.shift if seq.first == head }
57
+ result << head
58
+ end
59
+ end
60
+
61
+ def tail(array)
62
+ array[1..-1] || []
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,76 @@
1
+ module Accord
2
+ UNSPECIFIED = Object.new.tap do |o|
3
+ o.define_singleton_method(:inspect) { 'UNSPECIFIED' }
4
+ end
5
+
6
+ class SignatureInfo
7
+ def arguments
8
+ @arguments ||= []
9
+ end
10
+
11
+ def param(arg)
12
+ if arg.is_a?(Symbol) || arg.is_a?(String)
13
+ arg = { name: arg.to_sym }
14
+ elsif arg.is_a?(Hash) && arg.size == 1
15
+ arg = { name: arg.keys.first, default: arg.values.first }
16
+ else
17
+ raise ArgumentError, "bad argument: #{arg.inspect}."
18
+ end
19
+ arguments << arg
20
+ end
21
+
22
+ def splat(name)
23
+ arguments << { name: name.to_sym, splat: true }
24
+ end
25
+
26
+ def block(name=nil)
27
+ if name
28
+ @block = name.to_sym
29
+ else
30
+ @block
31
+ end
32
+ end
33
+
34
+ def match?(params)
35
+ args = normalized_arguments
36
+ match_without_all_required_args(args, params) ||
37
+ match_without_all_required_args(args, without_last_defaults(params))
38
+ end
39
+
40
+ private
41
+
42
+ def normalized_arguments
43
+ (arguments + (block ? [{ name: block, block: true }] : [])).map do |arg|
44
+ if arg[:splat]
45
+ :rest
46
+ elsif arg[:block]
47
+ :block
48
+ elsif arg.has_key?(:default)
49
+ :opt
50
+ else
51
+ :req
52
+ end
53
+ end
54
+ end
55
+
56
+ def without_last_defaults(params)
57
+ params = params.dup
58
+ params.pop if params.any? && params.last.first == :block
59
+ params.pop while params.any? && params.last.first == :opt
60
+ params
61
+ end
62
+
63
+ def match_without_all_required_args(args, params)
64
+ return false unless args.size == params.size
65
+ args.each_with_index do |arg, index|
66
+ if arg == :req && [:req, :opt].include?(params[index].first)
67
+ next
68
+ elsif params[index].first == arg
69
+ next
70
+ end
71
+ return false
72
+ end
73
+ true
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,83 @@
1
+ require 'set'
2
+ require 'accord/ro'
3
+
4
+ module Accord
5
+ class Specification
6
+ def initialize(*args)
7
+ if args.size == 2 || args.first.is_a?(String) || args.first.is_a?(Symbol)
8
+ @name = args.first.to_s
9
+ bases = (args.size == 2 ? (args[1] || []) : [])
10
+ else
11
+ @name = '?'
12
+ bases = args.first || []
13
+ end
14
+ @dependents = Set.new
15
+ @ro = Accord::RO.new(self) { |spec| spec.bases }
16
+ self.bases = bases
17
+ end
18
+
19
+ def bases
20
+ (@bases || []).dup
21
+ end
22
+
23
+ def bases= new_bases
24
+ new_bases = new_bases.uniq
25
+ new_bases.each do |base|
26
+ raise TypeError,
27
+ 'cannot use something other than Specification as a base' \
28
+ unless base.is_a?(Specification)
29
+ end
30
+
31
+ bases.each { |base| base.unsubscribe(self) }
32
+ new_bases.each { |base| base.subscribe(self) }
33
+
34
+ @bases = new_bases
35
+
36
+ changed(self)
37
+ end
38
+
39
+ def each_interface
40
+ seen = Set.new
41
+ @bases.each do |base|
42
+ base.each_interface do |interface|
43
+ next if seen.include?(interface)
44
+ seen << interface
45
+ yield(interface)
46
+ end
47
+ end
48
+ end
49
+
50
+ def interfaces
51
+ enum_for(:each_interface).to_a
52
+ end
53
+
54
+ def ancestors
55
+ @ro.resolve
56
+ end
57
+
58
+ def extends?(other)
59
+ ancestors.include?(other)
60
+ end
61
+
62
+ def inspect
63
+ "<Specification #{@name.inspect}>"
64
+ end
65
+
66
+ protected
67
+
68
+ def changed(originally_changed)
69
+ @ro.changed
70
+ @dependents.each do |dependent|
71
+ dependent.changed(originally_changed)
72
+ end
73
+ end
74
+
75
+ def subscribe(dependent)
76
+ @dependents << dependent
77
+ end
78
+
79
+ def unsubscribe(dependent)
80
+ @dependents.delete(dependent)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,76 @@
1
+ require 'accord/interface'
2
+ require 'accord/base_registry'
3
+
4
+ module Accord
5
+ class SubscriberLookup < BaseLookup
6
+ def all(required, provided)
7
+ extendor = extendors.get(provided)
8
+ return [] unless extendor
9
+ hash.select_expansions(required + [provided, '']) do |key|
10
+ if required.include?(key)
11
+ key.ancestors.reverse
12
+ elsif key.equal?(provided)
13
+ extendor.current.reverse
14
+ else
15
+ ['']
16
+ end
17
+ end.flatten
18
+ end
19
+ end
20
+
21
+ class SubscriptionRegistry < BaseRegistry
22
+ def self.lookup_class
23
+ SubscriberLookup
24
+ end
25
+
26
+ def subscribe(required, provided, &value)
27
+ raise ArgumentError, "cannot subscribe without a block" unless value
28
+ required = normalize_interfaces(required || [nil])
29
+ provided ||= Interface
30
+ key = [required, provided, '']
31
+ (registrations.by_order(required.size)[key] ||= []) << value
32
+ end
33
+
34
+ def unsubscribe(required, provided, value=nil)
35
+ required = normalize_interfaces(required || [nil])
36
+ provided ||= Interface
37
+ lookup = registrations.by_order(required.size)
38
+ key = [required, provided, '']
39
+ old = lookup[key] || []
40
+
41
+ return if old.empty?
42
+
43
+ new = value.nil?? [] : old.select { |v| !v.equal?(value) }
44
+ if new.any?
45
+ lookup[key] = new
46
+ else
47
+ lookup.delete(key)
48
+ end
49
+ end
50
+
51
+ def all(options={})
52
+ required = normalize_interfaces(options[:required] || [nil])
53
+ provided = options[:provided] || Interface
54
+ registrations.by_order(required.size)[[required, provided, '']] || []
55
+ end
56
+
57
+ def lookup(required, provided)
58
+ required = normalize_interfaces(required || [nil])
59
+ provided ||= Interface
60
+ lookup = registrations.by_order(required.size)
61
+ lookup.all(required, provided)
62
+ end
63
+
64
+ def get(objects, provided=Interface)
65
+ lookup(map_provided_by(objects), provided).map do |subscriber|
66
+ subscriber.call(*objects)
67
+ end.compact
68
+ end
69
+
70
+ def call(objects, provided=Interface)
71
+ lookup(map_provided_by(objects), provided).each do |subscriber|
72
+ subscriber.call(*objects)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,25 @@
1
+ module Accord
2
+ class Tags
3
+ def initialize
4
+ @hash = {}
5
+ end
6
+
7
+ def []=(tag, value)
8
+ @hash[tag.to_sym] = value
9
+ end
10
+
11
+ def [](tag)
12
+ @hash[tag.to_sym]
13
+ end
14
+
15
+ def fetch(tag, default=Tags.marker)
16
+ return @hash[tag] if @hash.has_key?(tag)
17
+ return default unless default.equal?(self.class.marker)
18
+ raise ArgumentError, "tag #{tag.inspect} not found."
19
+ end
20
+
21
+ def self.marker
22
+ @marker ||= Object.new
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Accord
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,296 @@
1
+ require 'spec_helper'
2
+ require 'accord/adapter_registry'
3
+
4
+ module Accord
5
+ describe AdapterRegistry do
6
+ subject { AdapterRegistry.new }
7
+
8
+ let(:adapter_factory) { Proc.new {} }
9
+ let(:other_adapter_factory) { Proc.new {} }
10
+
11
+
12
+ let(:i1) { stub_interface }
13
+ let(:i2) { stub_interface }
14
+
15
+ def stub_interface(*bases)
16
+ stub.tap do |interface|
17
+ interface.stub(
18
+ :iro => [interface],
19
+ :ancestors => [interface]
20
+ )
21
+ interface.stub(:extends?).and_return(false)
22
+ interface.stub(:extends?).with(interface).and_return(true)
23
+ bases.each do |base|
24
+ interface.iro << base
25
+ interface.ancestors << base
26
+ interface.stub(:extends?).with(base).and_return(true)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#first" do
32
+ it "returns no adapter when registry is empty" do
33
+ expect(subject.first).to be_nil
34
+ end
35
+
36
+ context "registered most simple single adapter" do
37
+ it "returns that if no arguments are passed" do
38
+ subject.register([Interface], Interface, '', &adapter_factory)
39
+ expect(subject.first).to be adapter_factory
40
+ end
41
+ end
42
+
43
+ it "hits right adapter factory if matches" do
44
+ subject.register([i1], nil, '', &adapter_factory)
45
+ expect(
46
+ subject.first(required: [i1], provided: nil, name: '')
47
+ ).to be adapter_factory
48
+ end
49
+
50
+ it "miss adapter factory if none matches" do
51
+ subject.register([i1], nil, '', &adapter_factory)
52
+ expect(
53
+ subject.first(required: [i2], provided: nil, name: '')
54
+ ).to be_nil
55
+ end
56
+ end
57
+
58
+ describe "#all" do
59
+ it "returns no adapter when empty" do
60
+ expect(subject.all).to be_empty
61
+ end
62
+
63
+ context "registered most simple single adapter" do
64
+ before do
65
+ subject.register([Interface], Interface, '', &adapter_factory)
66
+ end
67
+
68
+ it "returns that if no arguments are passed" do
69
+ expect(subject.all).to eq [['', adapter_factory]]
70
+ end
71
+ end
72
+
73
+ context "selection in registration for multiple names" do
74
+ it "returns all registered adapters" do
75
+ subject.register([nil], nil, &adapter_factory)
76
+ subject.register([nil], nil, 'other name', &other_adapter_factory)
77
+ expect(subject.all(required: [nil], provided: nil)).to eq [
78
+ ['', adapter_factory],
79
+ ['other name', other_adapter_factory]
80
+ ]
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "#register" do
86
+ it "complains if registration happens without block" do
87
+ expect {
88
+ subject.register([Interface], Interface, '')
89
+ }.to raise_error(ArgumentError)
90
+ end
91
+
92
+ it "allows registering a null adapter" do
93
+ subject.register([], i1, '', &adapter_factory)
94
+ expect(
95
+ subject.first(required: [], provided: i1, name:'')
96
+ ).to be adapter_factory
97
+ end
98
+
99
+ context "when using [nil] and nil for required and provided" do
100
+ it "defaults to most simple single adapter" do
101
+ subject.register([nil], nil, '', &adapter_factory)
102
+ expect(
103
+ subject.first(required: [Interface], provided: Interface, name: '')
104
+ ).to be adapter_factory
105
+ end
106
+ end
107
+
108
+ context "when using nil and nil for required and provided" do
109
+ it "defaults to most simple single adapter" do
110
+ subject.register(nil, nil, '', &adapter_factory)
111
+ expect(
112
+ subject.first(required: [Interface], provided: Interface, name: '')
113
+ ).to be adapter_factory
114
+ end
115
+ end
116
+
117
+ context "registration without name" do
118
+ it "defaults to empty name" do
119
+ subject.register([nil], nil, &adapter_factory)
120
+ expect(
121
+ subject.first(required: [nil], provided: nil, name: '')
122
+ ).to be adapter_factory
123
+ end
124
+ end
125
+ end
126
+
127
+ describe "#lookup" do
128
+ it "returns no adapter when registry is empty" do
129
+ expect(subject.lookup([], nil)).to be_nil
130
+ end
131
+
132
+ context "registered most simple single adapter" do
133
+ before do
134
+ subject.register([Interface], Interface,
135
+ '', &adapter_factory)
136
+ end
137
+
138
+ it "returns that if required is nil and provided is nil" do
139
+ expect(subject.lookup(nil, nil)).to be adapter_factory
140
+ end
141
+
142
+ it "returns that if required is [nil] and provided is nil" do
143
+ expect(subject.lookup([nil], nil)).to be adapter_factory
144
+ end
145
+ end
146
+
147
+ it "hits right adapter factory if matches exactly" do
148
+ subject.register([i1], nil, '', &adapter_factory)
149
+ expect(
150
+ subject.lookup([i1], nil, '')
151
+ ).to be adapter_factory
152
+ end
153
+
154
+ it "defaults name to empty string" do
155
+ subject.register([i1], nil, '', &adapter_factory)
156
+ expect(subject.lookup([i1], nil)).to be adapter_factory
157
+ end
158
+
159
+ it "hits right adapter factory if required extends some registered" do
160
+ i12 = stub_interface(i1)
161
+ subject.register([i1], nil, '', &adapter_factory)
162
+ expect(subject.lookup([i12], nil, '')).to be adapter_factory
163
+ end
164
+
165
+ it "miss adapter factory if none matches" do
166
+ subject.register([i1], nil, '', &adapter_factory)
167
+ expect(subject.lookup([i2], nil, '')).to be_nil
168
+ end
169
+
170
+ it "returns default value if missed" do
171
+ subject.register([i1], nil, '', &adapter_factory)
172
+ expect(subject.lookup([i2], nil, default: 'default')).to eq 'default'
173
+ end
174
+ end
175
+
176
+ describe "#lookup_all" do
177
+ it "returns no adapter when registry is empty" do
178
+ expect(subject.lookup_all([], nil)).to be_empty
179
+ end
180
+
181
+ context "registered most simple single adapter" do
182
+ before do
183
+ subject.register([Interface], Interface, '', &adapter_factory)
184
+ end
185
+
186
+ it "returns that if required is nil and provided is nil" do
187
+ expect(subject.lookup_all(nil, nil)).to eq({'' => adapter_factory})
188
+ end
189
+
190
+ it "returns that if required is [nil] and provided is nil" do
191
+ expect(subject.lookup_all([nil], nil)).to eq({'' => adapter_factory})
192
+ end
193
+ end
194
+
195
+ it "hits right adapter factory if matches" do
196
+ subject.register([i1], nil, '', &adapter_factory)
197
+ expect(subject.lookup_all([i1], nil)).to eq({'' => adapter_factory})
198
+ end
199
+
200
+ it "hits right adapter factory if required extends some registered" do
201
+ i12 = stub_interface(i1)
202
+ subject.register([i1], nil, '', &adapter_factory)
203
+ expect(subject.lookup_all([i12], nil)).to eq({'' => adapter_factory})
204
+ end
205
+
206
+ it "hits right adapter factory if provided is extended by some registered" do
207
+ i12 = stub_interface(i1)
208
+ subject.register([nil], i12, '', &adapter_factory)
209
+ expect(subject.lookup_all([nil], i1)).to eq({'' => adapter_factory})
210
+ end
211
+
212
+ it "in the face of ambiguity, brings the most specific for required" do
213
+ i12 = stub_interface(i1)
214
+ subject.register([i1], nil, '', &other_adapter_factory)
215
+ subject.register([i12], nil, '', &adapter_factory)
216
+ expect(subject.lookup_all([i12], nil)).to eq({'' => adapter_factory})
217
+ end
218
+
219
+ it "in the face of ambiguity, brings the most specific for provided" do
220
+ i12 = stub_interface(i1)
221
+ subject.register([nil], i1, '', &other_adapter_factory)
222
+ subject.register([nil], i12, '', &adapter_factory)
223
+ expect(subject.lookup_all([nil], i12)).to eq({'' => adapter_factory})
224
+ end
225
+
226
+ it "miss adapter factory if none matches" do
227
+ subject.register([i1], nil, '', &adapter_factory)
228
+ expect(subject.lookup_all([i2], nil)).to be_empty
229
+ end
230
+ end
231
+
232
+ describe "#unregister" do
233
+ it "doesn't complain when unregistering empty registry" do
234
+ expect { subject.unregister([nil], nil, '') }.to_not raise_error
235
+ end
236
+
237
+ it "keeps registered if required doesn't match on unregistration" do
238
+ subject.register([i1], nil, '', &adapter_factory)
239
+ subject.unregister([i2], nil, '')
240
+ expect(
241
+ subject.first(required: [i1], provided: nil, name: '')
242
+ ).to be adapter_factory
243
+ end
244
+
245
+ it "keeps registered if provided doesn't match on unregistration" do
246
+ subject.register([nil], i1, '', &adapter_factory)
247
+ subject.unregister([nil], i2, '')
248
+ expect(
249
+ subject.first(required: [nil], provided: i1, name: '')
250
+ ).to be adapter_factory
251
+ end
252
+
253
+ it "keeps registered if name doesn't match on unregistration" do
254
+ subject.register([i1], nil, '', &adapter_factory)
255
+ subject.unregister([i1], nil, 'other name')
256
+ expect(
257
+ subject.first(required: [i1], provided: nil, name: '')
258
+ ).to be adapter_factory
259
+ end
260
+
261
+ it "keeps registered if value is passed but doesn't match on "\
262
+ "unregistration" do
263
+ subject.register([i1], nil, '', &adapter_factory)
264
+ subject.unregister([i1], nil, '', stub)
265
+ expect(
266
+ subject.first(required: [i1], provided: nil, name: '')
267
+ ).to be adapter_factory
268
+ end
269
+
270
+ it "unregisters if value not passed and everything else matches "\
271
+ "previous registration" do
272
+ subject.register([i1], nil, '', &adapter_factory)
273
+ subject.unregister([i1], nil, '')
274
+ expect(
275
+ subject.first(required: [i1], provided: nil, name: '')
276
+ ).to be_nil
277
+ end
278
+
279
+ context "when using [nil] and nil for required and provided" do
280
+ it "defaults to most simple single adapter" do
281
+ subject.register([Interface], Interface, '', &adapter_factory)
282
+ subject.unregister([nil], nil, '')
283
+ expect(subject.first).to be_nil
284
+ end
285
+ end
286
+
287
+ context "when using nil and nil for required and provided" do
288
+ it "defaults to most simple single adapter" do
289
+ subject.register([Interface], Interface, '', &adapter_factory)
290
+ subject.unregister(nil, nil, '')
291
+ expect(subject.first).to be_nil
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end