accord 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.
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