main
1module Spank
2 describe Container do
3 subject { Container.new }
4
5 describe "when resolving an item that has been registered" do
6 let(:registered_item) { Object.new }
7
8 before :each do
9 subject.register(:item) do
10 registered_item
11 end
12 end
13
14 it "returns the registered item" do
15 expect(subject.resolve(:item)).to eql(registered_item)
16 end
17 end
18
19 describe "when resolving the container" do
20 it "returns itself" do
21 expect(subject.resolve(:container)).to eql(subject)
22 end
23 end
24
25 describe "when multiple items are registered with the same key" do
26 let(:jeans) { double("jeans") }
27 let(:dress_pants) { double("dress pants") }
28
29 before :each do
30 subject.register(:pants) { jeans }
31 subject.register(:pants) { dress_pants }
32 end
33
34 context "when resolving a single item" do
35 it "returns the first one registered" do
36 expect(subject.resolve(:pants)).to eql(jeans)
37 end
38 end
39
40 context "when resolving all items" do
41 it "returns them all" do
42 results = subject.resolve_all(:pants)
43 expect(results).to match_array([jeans, dress_pants])
44 end
45 end
46
47 context "when resolving all items for an unknown key" do
48 it "returns an empty array" do
49 expect(subject.resolve_all(:shirts)).to be_empty
50 end
51 end
52 end
53
54 context "when a component is registered as a singleton" do
55 before :each do
56 subject.register(:singleton) { Object.new }.as_singleton
57 end
58
59 it "returns the same instance of the component every time" do
60 expect(subject.resolve(:singleton)).to eql(subject.resolve(:singleton))
61 end
62 end
63
64 context "when invoking the factory method" do
65 it "passes the container through to the block" do
66 result = nil
67 subject.register(:item) { |item| result = item }
68 subject.resolve(:item)
69 expect(result).to eql(subject)
70 end
71 end
72
73 context "when automatically resolving dependencies" do
74 class Child
75 attr_reader :mom, :dad
76
77 def initialize(mom,dad)
78 @mom = mom
79 @dad = dad
80 end
81 def greeting(message)
82 end
83 end
84
85 context "when the dependencies have been registered" do
86 let(:mom) { double("mom") }
87 let(:dad) { double("dad") }
88
89 before :each do
90 subject.register(:mom) { mom }
91 subject.register(:dad) { dad }
92 end
93
94 it "glues the pieces together automatically" do
95 child = subject.build(Child)
96 expect(child).to be_a_kind_of(Child)
97 expect(child.mom).to eql(mom)
98 expect(child.dad).to eql(dad)
99 end
100 end
101
102 context "when a component cannot automatically be constructed" do
103 it "raises an error" do
104 expect { subject.build(Child) }.to raise_error(ContainerError)
105 end
106 end
107 end
108
109 context "when registering interceptors" do
110 class TestInterceptor
111 attr_reader :called, :name
112 def initialize(name)
113 @name = name
114 @called = false
115 end
116 def intercept(invocation)
117 @called = true
118 invocation.proceed
119 end
120 end
121
122 class TestCommand
123 attr_reader :called, :received
124 def run(input)
125 @called = true
126 @received = input
127 end
128 end
129
130 let(:command) { TestCommand.new }
131 let(:interceptor) { TestInterceptor.new("first") }
132 let(:other_interceptor) { TestInterceptor.new("second") }
133
134 before :each do
135 subject.
136 register(:command) { command }.
137 intercept(:run).with(interceptor).and(other_interceptor)
138 subject.
139 register(:single_command) { command }.
140 intercept(:run).with(interceptor)
141 subject.resolve(:command).run("hi")
142 end
143
144 it "allows the first interceptor to intercept calls to the target" do
145 expect(interceptor.called).to be_truthy
146 end
147
148 it "allows the second interceptor to intercept calls to the target" do
149 expect(other_interceptor.called).to be_truthy
150 end
151
152 it "forwards the args to the command" do
153 expect(command.called).to be_truthy
154 expect(command.received).to match_array(["hi"])
155 end
156 end
157 end
158end