fabrique 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Gemfile +4 -0
- data/README.md +2 -155
- data/fabrique.gemspec +3 -3
- data/features/bean_factory.feature +194 -3
- data/features/step_definitions/bean_factory_steps.rb +41 -3
- data/features/support/fabrique.rb +1 -0
- data/fixtures/local_only/.gitignore +9 -0
- data/fixtures/local_only/Gemfile +4 -0
- data/fixtures/local_only/README.md +36 -0
- data/fixtures/local_only/Rakefile +2 -0
- data/fixtures/local_only/bin/console +14 -0
- data/fixtures/local_only/bin/setup +8 -0
- data/fixtures/local_only/lib/local_only/version.rb +3 -0
- data/fixtures/local_only/lib/local_only.rb +7 -0
- data/fixtures/local_only/local_only.gemspec +31 -0
- data/fixtures/local_only-0.1.0.gem +0 -0
- data/fixtures/sample/.gitignore +9 -0
- data/fixtures/sample/Gemfile +4 -0
- data/fixtures/sample/README.md +36 -0
- data/fixtures/sample/Rakefile +2 -0
- data/fixtures/sample/bin/console +14 -0
- data/fixtures/sample/bin/setup +8 -0
- data/fixtures/sample/lib/sample/version.rb +3 -0
- data/fixtures/sample/lib/sample.rb +9 -0
- data/fixtures/sample/sample.gemspec +23 -0
- data/lib/fabrique/bean_definition.rb +4 -4
- data/lib/fabrique/bean_definition_registry.rb +11 -5
- data/lib/fabrique/bean_factory.rb +34 -13
- data/lib/fabrique/bean_property_reference.rb +9 -1
- data/lib/fabrique/data_bean.rb +62 -0
- data/lib/fabrique/gem_definition.rb +15 -0
- data/lib/fabrique/gem_dependency_error.rb +6 -0
- data/lib/fabrique/gem_loader.rb +25 -0
- data/lib/fabrique/test/fixtures/constructors.rb +12 -1
- data/lib/fabrique/version.rb +1 -1
- data/lib/fabrique.rb +0 -3
- data/spec/fabrique/data_bean_spec.rb +129 -0
- metadata +32 -38
- data/features/plugin_registry.feature +0 -79
- data/features/step_definitions/plugin_registry_steps.rb +0 -207
- data/lib/fabrique/argument_adaptor/keyword.rb +0 -19
- data/lib/fabrique/argument_adaptor/positional.rb +0 -76
- data/lib/fabrique/construction/as_is.rb +0 -16
- data/lib/fabrique/construction/builder_method.rb +0 -21
- data/lib/fabrique/construction/default.rb +0 -17
- data/lib/fabrique/construction/keyword_argument.rb +0 -16
- data/lib/fabrique/construction/positional_argument.rb +0 -40
- data/lib/fabrique/construction/properties_hash.rb +0 -19
- data/lib/fabrique/constructor/identity.rb +0 -10
- data/lib/fabrique/plugin_registry.rb +0 -56
- data/spec/fabrique/argument_adaptor/keyword_spec.rb +0 -50
- data/spec/fabrique/argument_adaptor/positional_spec.rb +0 -166
- data/spec/fabrique/construction/as_is_spec.rb +0 -23
- data/spec/fabrique/construction/builder_method_spec.rb +0 -29
- data/spec/fabrique/construction/default_spec.rb +0 -19
- data/spec/fabrique/construction/positional_argument_spec.rb +0 -61
- data/spec/fabrique/construction/properties_hash_spec.rb +0 -36
- data/spec/fabrique/constructor/identity_spec.rb +0 -4
- data/spec/fabrique/plugin_registry_spec.rb +0 -78
@@ -1,56 +0,0 @@
|
|
1
|
-
module Fabrique
|
2
|
-
|
3
|
-
class PluginRegistry
|
4
|
-
|
5
|
-
def initialize(name)
|
6
|
-
@name = name
|
7
|
-
@registrations = []
|
8
|
-
end
|
9
|
-
|
10
|
-
def register(id, type, constructor)
|
11
|
-
if existing = find_registration(id)
|
12
|
-
raise ArgumentError, "could not register #{type} as #{id} in #{@name}: #{existing.type} already registered as #{id}"
|
13
|
-
end
|
14
|
-
@registrations << Registration.new(id, type, constructor)
|
15
|
-
true
|
16
|
-
end
|
17
|
-
|
18
|
-
def acquire(id, properties = nil)
|
19
|
-
if registration = find_registration(id)
|
20
|
-
registration.call_constructor(properties)
|
21
|
-
else
|
22
|
-
raise ArgumentError, "#{id} not registered in #{@name}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def find_registration(id)
|
29
|
-
@registrations.detect { |r| r.id == id }
|
30
|
-
end
|
31
|
-
|
32
|
-
def unregister(id)
|
33
|
-
@registrations.delete(find_registration(id))
|
34
|
-
end
|
35
|
-
|
36
|
-
class Registration
|
37
|
-
|
38
|
-
attr_reader :id, :type, :constructor
|
39
|
-
|
40
|
-
def initialize(id, type, constructor)
|
41
|
-
@id, @type, @constructor = id, type, constructor
|
42
|
-
end
|
43
|
-
|
44
|
-
def call_constructor(properties = nil)
|
45
|
-
# TODO Push conditional into construction helpers
|
46
|
-
if properties.nil?
|
47
|
-
@constructor.call(@type)
|
48
|
-
else
|
49
|
-
@constructor.call(@type, properties)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "fabrique/test"
|
3
|
-
|
4
|
-
describe Fabrique::ArgumentAdaptor::Keyword do
|
5
|
-
|
6
|
-
let(:properties) { {size: "small", color: "red", shape: "dot"} }
|
7
|
-
|
8
|
-
describe "#adapt(*properties)" do
|
9
|
-
|
10
|
-
context "when called without properties" do
|
11
|
-
|
12
|
-
it "returns an empty array" do
|
13
|
-
expect(subject.adapt).to eql []
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
context "when called with empty properties" do
|
19
|
-
|
20
|
-
it "returns an empty hash in an empty array" do
|
21
|
-
expect(subject.adapt({})).to eql [{}]
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
context "when called with properties" do
|
27
|
-
|
28
|
-
it "returns a hash containing the properties" do
|
29
|
-
expect(subject.adapt(properties)).to eql [properties]
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
it "supports properties hash constructors" do
|
35
|
-
object = Fabrique::Test::Fixtures::Constructors::ClassWithPropertiesHashConstructor.new(*subject.adapt(properties))
|
36
|
-
expect(object.size).to eql properties[:size]
|
37
|
-
expect(object.color).to eql properties[:color]
|
38
|
-
expect(object.shape).to eql properties[:shape]
|
39
|
-
end
|
40
|
-
|
41
|
-
it "supports keyword argument constructors" do
|
42
|
-
object = Fabrique::Test::Fixtures::Constructors::ClassWithKeywordArgumentConstructor.new(*subject.adapt(properties))
|
43
|
-
expect(object.size).to eql properties[:size]
|
44
|
-
expect(object.color).to eql properties[:color]
|
45
|
-
expect(object.shape).to eql properties[:shape]
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
@@ -1,166 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Fabrique::ArgumentAdaptor::Positional do
|
4
|
-
|
5
|
-
describe "::new(*argument_specifieres)" do
|
6
|
-
|
7
|
-
it "takes symbols as required argument names" do
|
8
|
-
expect { described_class.new(:arg_name1, :arg_name2, :arg_name3) }.to_not raise_error
|
9
|
-
end
|
10
|
-
|
11
|
-
it "takes one-element arrays as optional argument names with no default value" do
|
12
|
-
expect { described_class.new(:arg_name1, :arg_name2, [:arg_name3]) }.to_not raise_error
|
13
|
-
end
|
14
|
-
|
15
|
-
it "takes two-element arrays as optional argument names with a default value" do
|
16
|
-
expect { described_class.new(:arg_name1, [:arg_name2], [:arg_name3, "default arg3"]) }.to_not raise_error
|
17
|
-
end
|
18
|
-
|
19
|
-
it "allows no argument names (useful for a default constructor)" do
|
20
|
-
expect { described_class.new }.to_not raise_error
|
21
|
-
end
|
22
|
-
|
23
|
-
context "when passed arguments that are not symbols or optional argument specifier arrays" do
|
24
|
-
|
25
|
-
it "raises an ArgumentError" do
|
26
|
-
expect { described_class.new(:arg_name1, "arg_name2") }.to raise_error(ArgumentError, /invalid argument specifier/)
|
27
|
-
expect { described_class.new(:arg_name1, []) }.to raise_error(ArgumentError, /invalid argument specifier/)
|
28
|
-
expect { described_class.new(:arg_name1, ["arg_name2"]) }.to raise_error(ArgumentError, /invalid argument specifier/)
|
29
|
-
expect { described_class.new(:arg_name1, [:arg_name2, "value", "nonsense"]) }.to raise_error(ArgumentError, /invalid argument specifier/)
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "#adapt(*properties)" do
|
37
|
-
|
38
|
-
context "when initialized with no required argument specifiers" do
|
39
|
-
context "when called without properties" do
|
40
|
-
it "returns an empty array" do
|
41
|
-
expect(subject.adapt).to eql []
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context "when called with empty properties" do
|
46
|
-
it "returns an empty array" do
|
47
|
-
expect(subject.adapt({})).to eql []
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
context "when called with properties" do
|
52
|
-
it "returns an empty array" do
|
53
|
-
expect(subject.adapt(size: "small", color: "red", shape: "dot")).to eql []
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context "when initialized with only required argument names" do
|
59
|
-
subject { described_class.new(:size, :color, :shape) }
|
60
|
-
|
61
|
-
context "when called with a property for each argument name" do
|
62
|
-
it "returns an array of the property value of each argument name in the order specified to new()" do
|
63
|
-
expect(subject.adapt(shape: "dot", size: "small", color: "red")).to eql ["small", "red", "dot"]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "when called with at least one property missing for an argument name" do
|
68
|
-
it "raises an ArgumentError" do
|
69
|
-
expect { subject.adapt(size: "small", color: "red") }.to raise_error(ArgumentError, /required argument \w+ missing from properties/)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context "when called with extraneous properties" do
|
74
|
-
it "ignores them, returning an array of the property value of each argument name in the order specified to new()" do
|
75
|
-
expect(subject.adapt(size: "small", color: "red", shape: "dot", status: "on")).to eql ["small", "red", "dot"]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
context "when initialized with only optional argument names with default values" do
|
81
|
-
subject { described_class.new([:size, "default size"], [:color, "default color"], [:shape, "default shape"]) }
|
82
|
-
|
83
|
-
context "when called with a property for each argument name" do
|
84
|
-
it "returns an array of the property value of each argument name in the order specified to new()" do
|
85
|
-
expect(subject.adapt(shape: "dot", size: "small", color: "red")).to eql ["small", "red", "dot"]
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
context "when called with at least one property missing for an argument name" do
|
90
|
-
it "returns an array of the provided property values and default values, in the order specified to new()" do
|
91
|
-
expect(subject.adapt(size: "small", color: "red")).to eql ["small", "red", "default shape"]
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context "when called with extraneous properties" do
|
96
|
-
it "ignores them" do
|
97
|
-
expect(subject.adapt(size: "small", color: "red", status: "on")).to eql ["small", "red", "default shape"]
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
context "when initialized with optional argument names with no default values, followed by required argument names" do
|
104
|
-
subject { described_class.new([:size], [:color], :shape) }
|
105
|
-
|
106
|
-
context "when called with a property for each argument name" do
|
107
|
-
it "returns an array of the property value of each argument name in the order specified to new()" do
|
108
|
-
expect(subject.adapt(shape: "dot", size: "small", color: "red")).to eql ["small", "red", "dot"]
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context "when called with at least one property missing for an optional argument" do
|
113
|
-
it "raises an ArgumentError (because the caller would be surprised by Ruby filling optional arguments from left to right)" do
|
114
|
-
expect {subject.adapt(color: "red", shape: "dot") }.to raise_error(ArgumentError, /optional argument size \(with no default\) missing from properties/)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
context "when called with at least one property missing for a required argument" do
|
119
|
-
it "raises an ArgumentError" do
|
120
|
-
expect { subject.adapt(size: "small", color: "red") }.to raise_error(ArgumentError, /required argument \w+ missing from properties/)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
context "when initialized with optional argument names with nil as the default value, followed by required argument names" do
|
126
|
-
subject { described_class.new([:size, nil], [:color, nil], :shape) }
|
127
|
-
|
128
|
-
context "when called with a property for each argument name" do
|
129
|
-
it "returns an array of the property value of each argument name in the order specified to new()" do
|
130
|
-
expect(subject.adapt(shape: "dot", size: "small", color: "red")).to eql ["small", "red", "dot"]
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
context "when called with at least one property missing for an optional argument" do
|
135
|
-
it "returns an array of the provided property values and default values, in the order specified to new()" do
|
136
|
-
expect(subject.adapt(color: "red", shape: "dot")).to eql [nil, "red", "dot"]
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
context "when called with at least one property missing for a required argument" do
|
141
|
-
it "raises an ArgumentError" do
|
142
|
-
expect { subject.adapt(size: "small", color: "red") }.to raise_error(ArgumentError, /required argument \w+ missing from properties/)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
it "supports default constructors" do
|
148
|
-
klass = Fabrique::Test::Fixtures::Constructors::ClassWithDefaultConstructor
|
149
|
-
object = klass.new(*subject.adapt())
|
150
|
-
expect(object.size).to eql klass::DEFAULT_SIZE
|
151
|
-
expect(object.color).to eql klass::DEFAULT_COLOR
|
152
|
-
expect(object.shape).to eql klass::DEFAULT_SHAPE
|
153
|
-
end
|
154
|
-
|
155
|
-
it "supports positional argument constructors" do
|
156
|
-
klass = Fabrique::Test::Fixtures::Constructors::ClassWithPositionalArgumentConstructor
|
157
|
-
subject = described_class.new(:size, :color, :shape)
|
158
|
-
object = klass.new(*subject.adapt(size: "small", color: "red", shape: "dot"))
|
159
|
-
expect(object.size).to eql "small"
|
160
|
-
expect(object.color).to eql "red"
|
161
|
-
expect(object.shape).to eql "dot"
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Fabrique::Construction::AsIs do
|
4
|
-
|
5
|
-
describe "call(type, properties = nil)" do
|
6
|
-
|
7
|
-
it "applies identity to the type" do
|
8
|
-
constructed = subject.call(type = Object.new)
|
9
|
-
expect(constructed.object_id).to eql type.object_id
|
10
|
-
end
|
11
|
-
|
12
|
-
it "accepts optional properties to support construction interface" do
|
13
|
-
constructed = subject.call(type = Object.new, {})
|
14
|
-
expect(constructed.object_id).to eql type.object_id
|
15
|
-
end
|
16
|
-
|
17
|
-
it "raises an ArgumentError if properties is specified and not empty" do
|
18
|
-
expect { subject.call(Object.new, {some: "properties"}) }.to raise_error(ArgumentError, /unexpected properties/)
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "fabrique/test"
|
3
|
-
|
4
|
-
describe Fabrique::Construction::BuilderMethod do
|
5
|
-
|
6
|
-
describe "call(type, properties = nil)" do
|
7
|
-
|
8
|
-
context "when initialized with a builder method name and builder runner block" do
|
9
|
-
|
10
|
-
subject do
|
11
|
-
described_class.new(:build) do |builder, properties|
|
12
|
-
builder.size = properties[:size]
|
13
|
-
builder.color = properties[:color]
|
14
|
-
builder.shape = properties[:shape]
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
it "calls the builder method on the type and yields the builder and the specified properties to the builder runner block" do
|
19
|
-
o = subject.call(Fabrique::Test::Fixtures::Constructors::ClassWithBuilderMethod, size: "huge", color: "black", shape: "hole")
|
20
|
-
expect(o.size).to eql "huge"
|
21
|
-
expect(o.color).to eql "black"
|
22
|
-
expect(o.shape).to eql "hole"
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Fabrique::Construction::Default do
|
4
|
-
|
5
|
-
describe "call(type, properties = nil)" do
|
6
|
-
|
7
|
-
it "calls type.new() without arguments" do
|
8
|
-
constructed = subject.call(type = Object)
|
9
|
-
expect(constructed.class).to eql type
|
10
|
-
end
|
11
|
-
|
12
|
-
it "accepts optional properties to support construction interface" do
|
13
|
-
constructed = subject.call(type = Object, {})
|
14
|
-
expect(constructed.class).to eql type
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
class PositionalArgumentFixture
|
4
|
-
attr_reader :color, :shape, :size
|
5
|
-
|
6
|
-
DEFAULT_ATTRS = {color: "red", shape: "dot", size: "small"} unless defined?(DEFAULT_ATTRS)
|
7
|
-
|
8
|
-
def initialize(color = DEFAULT_ATTRS[:color], shape = DEFAULT_ATTRS[:shape], size = DEFAULT_ATTRS[:size])
|
9
|
-
@color, @shape, @size = color, shape, size
|
10
|
-
end
|
11
|
-
|
12
|
-
def attrs
|
13
|
-
{color: @color, shape: @shape, size: @size}
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
describe Fabrique::Construction::PositionalArgument do
|
19
|
-
|
20
|
-
let(:type) { PositionalArgumentFixture }
|
21
|
-
|
22
|
-
describe "call(type, properties = nil)" do
|
23
|
-
|
24
|
-
it "applies positional argument construction to the type, in the order they were provided to new()" do
|
25
|
-
subject = described_class.new(:color, :shape, :size)
|
26
|
-
constructed = subject.call(type, {size: "tiny", color: "purple", shape: "dot"})
|
27
|
-
expect(constructed.attrs).to eql({color: "purple", shape: "dot", size: "tiny"})
|
28
|
-
end
|
29
|
-
|
30
|
-
it "calls type.new() if no arguments were specified to new()" do
|
31
|
-
subject = described_class.new()
|
32
|
-
type = spy('type')
|
33
|
-
subject.call(type, {size: "tiny", color: "purple", shape: "dot"})
|
34
|
-
expect(type).to have_received(:new).with(no_args)
|
35
|
-
end
|
36
|
-
|
37
|
-
context "when one or more optional arguments were specified to new()" do
|
38
|
-
|
39
|
-
subject = described_class.new(:color, [:shape, :size])
|
40
|
-
|
41
|
-
it "passes optional arguments provided in properties" do
|
42
|
-
type = spy('type')
|
43
|
-
subject.call(type, {color: "purple", shape: "dot", size: "tiny"})
|
44
|
-
expect(type).to have_received(:new).with("purple", "dot", "tiny")
|
45
|
-
end
|
46
|
-
|
47
|
-
it "discards optional arguments if they are not present in the properties" do
|
48
|
-
type = spy('type')
|
49
|
-
subject.call(type, {size: "tiny", color: "purple"})
|
50
|
-
expect(type).to have_received(:new).with("purple", "tiny")
|
51
|
-
end
|
52
|
-
|
53
|
-
it "raises an ArgumentError if required arguments are not present in the properties" do
|
54
|
-
expect { subject.call(Object, shape: "dot", size: "tiny") }.to raise_error(ArgumentError, /required argument color missing/)
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
class PropertiesHashFixture
|
4
|
-
attr_reader :color, :shape, :size
|
5
|
-
|
6
|
-
DEFAULT_ATTRS = {color: "red", shape: "dot", size: "small"} unless defined?(DEFAULT_ATTRS)
|
7
|
-
|
8
|
-
def initialize(properties = DEFAULT_ATTRS)
|
9
|
-
@color, @shape, @size = properties[:color], properties[:shape], properties[:size]
|
10
|
-
end
|
11
|
-
|
12
|
-
def attrs
|
13
|
-
{color: @color, shape: @shape, size: @size}
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
describe Fabrique::Construction::PropertiesHash do
|
19
|
-
|
20
|
-
describe "call(type, properties = nil)" do
|
21
|
-
|
22
|
-
let(:type) { PropertiesHashFixture }
|
23
|
-
|
24
|
-
it "calls type.new() if properties is not provided" do
|
25
|
-
o = subject.call(type)
|
26
|
-
expect(o.attrs).to eql type::DEFAULT_ATTRS
|
27
|
-
end
|
28
|
-
|
29
|
-
it "calls type.new(properties) if properties is provided" do
|
30
|
-
o = subject.call(type, properties = {color: "green", shape: "patch", size: "large"})
|
31
|
-
expect(o.attrs).to eql properties
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Fabrique::PluginRegistry do
|
4
|
-
|
5
|
-
subject { described_class.new("Test Plugin Factory") }
|
6
|
-
let(:constructor) { spy("constructor") }
|
7
|
-
|
8
|
-
describe "#register(id, type, constructor)" do
|
9
|
-
|
10
|
-
it "applies the strategy pattern to construction" do
|
11
|
-
subject.register(:my_plugin, type = Class.new, constructor)
|
12
|
-
subject.acquire(:my_plugin, {some: "properties"})
|
13
|
-
expect(constructor).to have_received(:call).with(type, {some: "properties"})
|
14
|
-
end
|
15
|
-
|
16
|
-
it "returns true on success (to avoid leaking registration internals)" do
|
17
|
-
expect(subject.register(:my_plugin, Class.new, constructor)).to be true
|
18
|
-
end
|
19
|
-
|
20
|
-
context "when the unique identity has already been registered" do
|
21
|
-
|
22
|
-
let(:existing_type) { Object.new }
|
23
|
-
before(:each) { subject.register(:existing, existing_type, constructor) }
|
24
|
-
|
25
|
-
it "raises an ArgumentError" do
|
26
|
-
expect {
|
27
|
-
subject.register(:existing, Object.new, double("constructor").as_null_object)
|
28
|
-
}.to raise_error(ArgumentError, /#{existing_type} already registered/)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "leaves the original registration intact" do
|
32
|
-
begin
|
33
|
-
subject.register(:existing, Object.new, double("constructor").as_null_object)
|
34
|
-
rescue
|
35
|
-
end
|
36
|
-
subject.acquire(:existing)
|
37
|
-
expect(constructor).to have_received(:call).with(existing_type)
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "#acquire(id, properties = {})" do
|
45
|
-
|
46
|
-
let(:type) { Object }
|
47
|
-
before(:each) { subject.register(:my_plugin, type, constructor) }
|
48
|
-
|
49
|
-
it "applies the registered constructor to the registered type with the given properties" do
|
50
|
-
subject.acquire(:my_plugin, properties = {some: "properties"})
|
51
|
-
expect(constructor).to have_received(:call).with(type, properties)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "applies the registered constructor to the registered type only if no properties are given" do
|
55
|
-
subject.acquire(:my_plugin)
|
56
|
-
expect(constructor).to have_received(:call).with(type)
|
57
|
-
end
|
58
|
-
|
59
|
-
it "returns the constructor call's return value" do
|
60
|
-
allow(constructor).to receive(:call).and_return(plugin = type.new)
|
61
|
-
expect(subject.acquire(:my_plugin)).to be plugin
|
62
|
-
end
|
63
|
-
|
64
|
-
context "when the unique identity has not yet been registered" do
|
65
|
-
|
66
|
-
before(:each) { subject.send(:unregister, :my_plugin) }
|
67
|
-
|
68
|
-
it "raises an ArgumentError" do
|
69
|
-
expect {
|
70
|
-
subject.acquire(:my_plugin)
|
71
|
-
}.to raise_error(ArgumentError, /not registered/)
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|