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