encase 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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjY0NjkwNmNhNzFmNjhiY2IyZmVhYjU2YjE4OGU2MTcwMTkxZmY0ZA==
5
+ data.tar.gz: !binary |-
6
+ MjgzYTUxZjNmMTgxNTA1NTVlN2Y4MDA5MWExYmE2NjFkYmNlNGUzNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MDllY2FhYmFmZTQ2YTUxNWU4NDVlMTZlMjNmMmM4M2QwMTkzNzYzMjQ4Njk3
10
+ ZWRkZDg4OWU5YzQxNWQ4YTQ0NjliZGY1MWQzNzZhYThmZjAzNzVmZmQxMjM5
11
+ MzE1ZTY5ZGE3MjFmODRkM2Y3NjJiMzk3MDBjOTg3Y2YxODNmNmY=
12
+ data.tar.gz: !binary |-
13
+ YjIzNWQ5MmQ2YjM2Y2Y1N2M1M2JmOGNkNmMwNzQ0N2NmMmQ4MDhmZWFiNWZh
14
+ ZmIzOTdiYzkzMjg0ZDRhYjU3MWY1MjgyN2U3MDk4MWE4NjA5ZmUxMzliZTY4
15
+ ZGVkMjE4ZGExMGIwOTZhOGViMTk3ZTY5NmI2YmZkYmNjZjMwZjU=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ### 0.1.0
4
+
5
+ * Initial Release
6
+
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Darshan Sawardekar
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,291 @@
1
+ ## Encase [![Build Status][1]][2] [![Code Climate][3]][4] [![Dependency Status][5]][6]
2
+
3
+ ### Lightweight IOC Container for ruby.
4
+
5
+ Encase is a library for using dependency injection within ruby
6
+ applications. It provides a lightweight IOC Container that manages
7
+ dependencies between your classes. It was written to assist with wiring
8
+ of domain objects outside Rails applications to enable faster test
9
+ suites.
10
+
11
+ # Features
12
+
13
+ * Stores objects, factories, and singletons
14
+ * Declarative syntax for specifying dependencies
15
+ * Simple DSL for configuring the container
16
+ * Support for nested containers
17
+ * Support for lazy initialization
18
+
19
+ ## Usage
20
+
21
+ Consider a `Worker` class that has a dependency on a `Logger`. We would
22
+ like this dependency to be available to the worker when it is created.
23
+
24
+ First we create a container object and declare the dependencies.
25
+
26
+ ```ruby
27
+ require 'encase/container'
28
+
29
+ container = Container.new
30
+ container.configure do
31
+ object :logger, Logger.new
32
+ factory :worker, Worker
33
+ end
34
+ ```
35
+
36
+ Then we declare the `logger` dependency in the Worker class by including
37
+ the `Encase` module into it and using the `needs` declaration.
38
+
39
+ ```ruby
40
+ require 'encase'
41
+
42
+ class Worker
43
+ include Encase
44
+
45
+ needs :logger
46
+ end
47
+ ```
48
+
49
+ That's it! Now we can create a new `Worker` by looking up the `:worker`
50
+ key on the Container.
51
+
52
+ ```ruby
53
+ my_worker = container.lookup('worker')
54
+ my_worker.logger.is_a?(Logger) # true
55
+ ```
56
+
57
+ ## Container Configuration
58
+
59
+ Containers are configured using a mini DSL in a `configure` method.
60
+ Container items are stored with ruby `symbols` and are used to later lookup the object.
61
+
62
+ ```ruby
63
+ container.configure do
64
+ # declarations go here
65
+ end
66
+ ```
67
+
68
+ The declarations supported are listed below.
69
+
70
+ ### Object
71
+
72
+ Objects are pre existing entities that have already been created in your
73
+ system. They are stored and returned as is without any modification. To
74
+ store objects use the `object` declaration.
75
+
76
+ ```ruby
77
+ container.configure do
78
+ object :key, value
79
+ end
80
+ ```
81
+
82
+ ### Factory
83
+
84
+ A Factory can be stored with the container to create instances of a
85
+ class with it's dependencies auto-injected. On every lookup a new
86
+ instance of that class will be returned.
87
+
88
+ ```ruby
89
+ container.configure do
90
+ factory :key, TheClass
91
+ end
92
+ ```
93
+
94
+ ### Singleton
95
+
96
+ A Singleton is similar to a Factory. However it caches the instance
97
+ created on the first lookup and returns that instance on every
98
+ subsequent lookups.
99
+
100
+ ```ruby
101
+ container.configure do
102
+ singleton :key, TheSingletonClass
103
+ end
104
+ ```
105
+
106
+ ## Declaring Dependencies
107
+
108
+ To specify dependencies of a class, you use the `needs` declaration. It
109
+ takes an array of symbols corresponding to the keys of the dependencies
110
+ stored in the container. Multiple `needs` declarations are also supported.
111
+
112
+ ```ruby
113
+ require 'encase'
114
+
115
+ class Worker
116
+ include Encase
117
+
118
+ needs :a, :b, :c
119
+ needs :one
120
+ needs :two
121
+ needs :three
122
+ end
123
+ ```
124
+
125
+ ## Lazy Initialization
126
+
127
+ Encase allows storage of dependencies lazily. This can be useful if the
128
+ dependencies aren't ready at the time of container configuration. But
129
+ will ready before lookup.
130
+
131
+ Lazy initialization is done by passing a `block` to the DSL declaration
132
+ instead of a value. Here `:key`'s block will be evaluated before the
133
+ first lookup.
134
+
135
+ ```ruby
136
+ container.configure do
137
+ object :key do
138
+ value
139
+ end
140
+ end
141
+ ```
142
+
143
+ The block can optionally take an argument equal to the container object
144
+ itself. You can use this to conditionally resolve the value based on
145
+ other objects in the container or elsewhere.
146
+
147
+ ```ruby
148
+ container.configure do
149
+ object :key do |c|
150
+ value
151
+ end
152
+ end
153
+ ```
154
+
155
+ ## Nested Containers
156
+
157
+ Containers can also be nested within other containers. This allows grouping
158
+ dependencies within different contexts of your application. When looking
159
+ up keys, parent containers are queried when a key is not found in a
160
+ child container.
161
+
162
+ ```ruby
163
+ parent_container = Container.new
164
+ parent_container.configure do
165
+ object :logger, Logger.new
166
+ factory :worker, Worker
167
+ end
168
+
169
+ child_container = parent_container.child
170
+ child_container.configure do
171
+ factory :worker, CustomWorker
172
+ end
173
+
174
+ child_container.lookup(:logger) # from parent_container
175
+ child_container.lookup(:worker) # CustomWorker
176
+ ```
177
+
178
+ Here the `child_container` will use `CustomWorker` for resolving
179
+ `:worker`. While the `:logger` will be looked up from the
180
+ `parent_container`
181
+
182
+ ## Accessors
183
+
184
+ Encase creates `accessor` methods corresponding to each declared `need`
185
+ in the class. A `container` accessor is also injected into the class for
186
+ looking up other dependencies at runtime.
187
+
188
+ ```ruby
189
+ class Worker
190
+ include Encase
191
+
192
+ needs :one, :two, :three
193
+ end
194
+
195
+ worker = container.lookup('worker')
196
+ worker.one
197
+ worker.two
198
+ worker.three
199
+ worker.container
200
+ ```
201
+
202
+ ## Lifecycle
203
+
204
+ An `on_inject` event hook is provided to container items after their
205
+ dependencies are injected. Any post injection initialization can be
206
+ carried out here.
207
+
208
+ ```ruby
209
+ class Worker
210
+ include Encase
211
+
212
+ needs :logger
213
+
214
+ def on_inject
215
+ logger.log('Worker is ready')
216
+ end
217
+ end
218
+ ```
219
+
220
+ ## Testing
221
+
222
+ Encase significantly simplifies testability of objects. In the earlier
223
+ example in order to test that the logger is indeed called by the worker
224
+ we can register the worker as a `mock`. Then verify that this mock was called.
225
+
226
+ ```ruby
227
+ describe Worker do
228
+ let(:container) {
229
+ container = Container.new
230
+ container.configure do
231
+ factory :worker, Worker
232
+ end
233
+
234
+ container
235
+ }
236
+
237
+ it 'logs message to the logger' do
238
+ logger = double()
239
+ logger.should_receive(:log).with(anything())
240
+
241
+ container.object logger, double(:log => true)
242
+
243
+ worker = container.lookup(:worker)
244
+ worker.start()
245
+ end
246
+ end
247
+ ```
248
+
249
+ Using the Encase created `accessor` methods it is also possible to
250
+ manually assign the dependencies. Below `logger` is directly assigned to
251
+ the `worker` without requiring a container.
252
+
253
+ ```ruby
254
+ it 'logs message to the logger' do
255
+ logger = double()
256
+ logger.should_receive(:log).with(anything())
257
+
258
+ worker = Worker.new
259
+ worker.logger = logger
260
+ worker.start()
261
+ end
262
+ ```
263
+
264
+ ## Installation
265
+
266
+ Add this line to your application's Gemfile:
267
+
268
+ gem 'encase'
269
+
270
+ And then execute:
271
+
272
+ $ bundle install
273
+
274
+ # System Requirements
275
+
276
+ Encase has been tested to work on these platforms.
277
+
278
+ * ruby 1.9.2
279
+ * ruby 1.9.3
280
+ * ruby 2.0.0
281
+
282
+ ## Contributing
283
+
284
+ See contributing guidelines for Portkey.
285
+
286
+ [1]: https://travis-ci.org/dsawardekar/encase.png
287
+ [2]: https://travis-ci.org/dsawardekar/encase
288
+ [3]: https://codeclimate.com/github/dsawardekar/encase.png
289
+ [4]: https://codeclimate.com/github/dsawardekar/encase
290
+ [5]: https://gemnasium.com/dsawardekar/encase.png
291
+ [6]: https://gemnasium.com/dsawardekar/encase
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Run rspec'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ desc 'Default task :test'
9
+ task :default => :test
10
+
11
+ desc 'Run tests'
12
+ task :test => :spec
data/encase.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'encase/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "encase"
8
+ spec.version = Encase::VERSION
9
+ spec.authors = ["Darshan Sawardekar"]
10
+ spec.email = ["darshan@sawardekar.org"]
11
+ spec.summary = %q{Lightweight IOC Container for ruby.}
12
+ spec.description = %q{Encase is a library for using dependency injection within ruby applications. It provides a lightweight IOC Container that manages dependencies between your classes. It was written to assist with wiring of domain objects outside Rails applications to enable faster test suites.}
13
+ spec.homepage = "https://github.com/dsawardekar/encase"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec", "~> 2.14.1"
24
+ end
data/lib/encase.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'encase/version'
2
+ require 'encase/encaseable'
3
+
4
+ module Encase
5
+ def self.included(base)
6
+ base.extend Encaseable
7
+ end
8
+ end
@@ -0,0 +1,80 @@
1
+ require 'encase/container_item_factory'
2
+
3
+ module Encase
4
+ class Container
5
+ attr_accessor :parent
6
+
7
+ def initialize(parent = nil)
8
+ @parent = parent
9
+ @items = {}
10
+ end
11
+
12
+ def contains?(key)
13
+ @items.key?(key)
14
+ end
15
+
16
+ def inject(object)
17
+ klass = object.class
18
+ if klass.respond_to?(:needs_to_inject)
19
+ needs_to_inject = klass.needs_to_inject
20
+ needs_to_inject.each do |need|
21
+ object.instance_variable_set(
22
+ "@#{need}", lookup(need)
23
+ )
24
+ end
25
+
26
+ object.container = self if object.respond_to?(:container)
27
+ object.on_inject() if object.respond_to?(:on_inject)
28
+
29
+ true
30
+ else
31
+ false
32
+ end
33
+ end
34
+
35
+ def register(type, key, value, block)
36
+ item = ContainerItemFactory.build(type, self)
37
+ item.store(key, value || block)
38
+ @items[key] = item
39
+ end
40
+
41
+ def unregister(key)
42
+ @items.delete(key)
43
+ end
44
+
45
+ def clear
46
+ @items.clear
47
+ end
48
+
49
+ def object(key, value = nil, &block)
50
+ register('object', key, value, block)
51
+ end
52
+
53
+ def factory(key, value = nil, &block)
54
+ register('factory', key, value, block)
55
+ end
56
+
57
+ def singleton(key, value = nil, &block)
58
+ register('singleton', key, value, block)
59
+ end
60
+
61
+ def lookup(key)
62
+ if contains?(key)
63
+ item = @items[key]
64
+ item.instance
65
+ elsif !parent.nil?
66
+ parent.lookup(key)
67
+ else
68
+ raise KeyError.new("Key:#{key} not found in container.")
69
+ end
70
+ end
71
+
72
+ def configure(&block)
73
+ instance_exec(&block)
74
+ end
75
+
76
+ def child
77
+ Container.new(self)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,55 @@
1
+ module Encase
2
+ class ContainerItem
3
+ attr_accessor :container
4
+ attr_accessor :key
5
+ attr_accessor :value
6
+ attr_accessor :reified_value
7
+
8
+ def initialize(container)
9
+ self.container = container
10
+ end
11
+
12
+ def store(key, value)
13
+ self.key = key
14
+ self.value = value
15
+ end
16
+
17
+ def inject(object)
18
+ self.container.inject(object)
19
+ end
20
+
21
+ def reify
22
+ return false if reified?
23
+
24
+ if value.is_a? Proc
25
+ if value.arity == 1
26
+ self.reified_value = value.call(container)
27
+ else
28
+ self.reified_value = value.call
29
+ end
30
+ else
31
+ self.reified_value = value
32
+ end
33
+
34
+ true
35
+ end
36
+
37
+ def reified?
38
+ !self.reified_value.nil?
39
+ end
40
+
41
+ # returns just the reified value by default
42
+ def fetch
43
+ reified_value
44
+ end
45
+
46
+ # public api
47
+ def instance
48
+ reify unless reified?
49
+ object = fetch
50
+ inject(object)
51
+
52
+ object
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module Encase
2
+ require_relative 'object_item'
3
+ require_relative 'factory_item'
4
+ require_relative 'singleton_item'
5
+
6
+ class ContainerItemFactory
7
+ def self.build(type, container)
8
+ container_item_for(type).new(container)
9
+ end
10
+
11
+ def self.container_item_for(type)
12
+ case type
13
+ when 'object' then ObjectItem
14
+ when 'factory' then FactoryItem
15
+ when 'singleton' then SingletonItem
16
+ else
17
+ ContainerItem
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module Encase
2
+ module Encaseable
3
+ def needs(*deps)
4
+ if @needs_to_inject
5
+ @needs_to_inject.concat(deps)
6
+ else
7
+ @needs_to_inject = deps
8
+ attr_accessor :container
9
+ end
10
+
11
+ attr_accessor *deps
12
+ end
13
+
14
+ def needs_to_inject
15
+ @needs_to_inject
16
+ end
17
+
18
+ def needs_to_inject=(value)
19
+ @needs_to_inject = value
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ module Encase
2
+ require_relative 'container_item'
3
+
4
+ class FactoryItem < ContainerItem
5
+ def fetch
6
+ reified_value.new
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Encase
2
+ require_relative 'container_item'
3
+
4
+ class ObjectItem < ContainerItem
5
+ attr_accessor :injected
6
+
7
+ def inject(object)
8
+ if self.injected
9
+ false
10
+ else
11
+ self.injected = super(object)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module Encase
2
+ require_relative 'factory_item'
3
+
4
+ class SingletonItem < FactoryItem
5
+ attr_accessor :singleton
6
+
7
+ def fetch
8
+ self.singleton = super unless self.singleton
9
+ self.singleton
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module Encase
2
+ VERSION = "0.1.0"
3
+ end
data/portkey.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "lib/encase/*.rb": {
3
+ "type": "container",
4
+ "test": "spec/lib/encase/%s_spec.rb"
5
+ },
6
+ "spec/lib/encase/*_spec.rb": {
7
+ "type": "spec",
8
+ "alternate": "lib/encase/%s.rb"
9
+ }
10
+ }
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'encase/container_item_factory'
3
+ require 'encase/container_item'
4
+ require 'encase/object_item'
5
+ require 'encase/factory_item'
6
+ require 'encase/singleton_item'
7
+
8
+ module Encase
9
+ shared_examples "a container_item_factory" do
10
+ it 'finds container_item\'s constructor' do
11
+ expect(
12
+ ContainerItemFactory.container_item_for(type)
13
+ ).to eq(type_constructor)
14
+ end
15
+
16
+ it 'builds container_item' do
17
+ expect(
18
+ ContainerItemFactory.build(type, container)
19
+ ).to be_a(type_constructor)
20
+ end
21
+ end
22
+
23
+ describe ContainerItemFactory do
24
+ let(:container) { double() }
25
+
26
+ context 'with object type' do
27
+ let(:type) { 'object' }
28
+ let(:type_constructor) { ObjectItem }
29
+
30
+ it_behaves_like 'a container_item_factory'
31
+ end
32
+
33
+ context 'with factory type' do
34
+ let(:type) { 'factory' }
35
+ let(:type_constructor) { FactoryItem }
36
+
37
+ it_behaves_like 'a container_item_factory'
38
+ end
39
+
40
+ context 'with singleton type' do
41
+ let(:type) { 'singleton' }
42
+ let(:type_constructor) { SingletonItem }
43
+
44
+ it_behaves_like 'a container_item_factory'
45
+ end
46
+ end
47
+ end
48
+
49
+
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+ require 'encase/container_item'
3
+
4
+ module Encase
5
+ shared_examples 'a container_item' do
6
+ it 'stores container' do
7
+ expect(container_item.container).to eq(container)
8
+ end
9
+
10
+ it 'stores item key - value pair' do
11
+ expect(container_item.key).to eq(container_item_key)
12
+ expect(container_item.value).to eq(container_item_value)
13
+ end
14
+
15
+ it 'reifies value' do
16
+ container_item.reify()
17
+ expect(container_item.reified_value).to eq(container_item_value_reified)
18
+ end
19
+
20
+ it 'does not reify if already reified' do
21
+ container_item.reify()
22
+ expect(container_item.reified?).to be_true
23
+
24
+ expect(container_item.reify()).to be_false
25
+ expect(container_item.reified?).to be_true
26
+ end
27
+
28
+ it 'applies injection using container' do
29
+ container_item.reify()
30
+ expect(container_item.inject(container_item_value_reified)).to be_true
31
+ end
32
+
33
+ it 'applies injection before returning instance' do
34
+ container.stub(:inject) do |value|
35
+ expect(value).to eq(container_item_value_reified)
36
+ end
37
+
38
+ container_item.instance()
39
+ end
40
+
41
+ it 'returns instance for item' do
42
+ expect(container_item.instance).to eq(container_item_value_reified)
43
+ end
44
+ end
45
+
46
+ describe ContainerItem do
47
+ let(:container) do
48
+ c = double()
49
+ c.stub(:inject).with(anything()) { true }
50
+ c
51
+ end
52
+
53
+ let(:container_item_key) { :lorem }
54
+ let(:container_item_value) { 'lorem' }
55
+ let(:container_item_value_reified) { 'lorem' }
56
+ let(:container_item) {
57
+ c = ContainerItem.new(container)
58
+ c.store(container_item_key, container_item_value)
59
+ c
60
+ }
61
+
62
+ context 'without proc' do
63
+ it_behaves_like "a container_item"
64
+ end
65
+
66
+ context 'with proc' do
67
+ context 'without argument' do
68
+ let(:container_item_value) {
69
+ Proc.new do
70
+ 'lorem'
71
+ end
72
+ }
73
+
74
+ it_behaves_like 'a container_item'
75
+ end
76
+
77
+ context 'with argument' do
78
+ let(:container_item_value) {
79
+ Proc.new do |c|
80
+ expect(c).to eq(container)
81
+ 'lorem'
82
+ end
83
+ }
84
+
85
+ it_behaves_like 'a container_item'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ require 'encase/container'
3
+ require 'encase'
4
+
5
+ module Encase
6
+ class Lorem
7
+ end
8
+
9
+ class MyBox
10
+ include Encase
11
+
12
+ needs :apples, :mangoes
13
+ needs :oranges
14
+ end
15
+
16
+ describe Container do
17
+ let(:container) {
18
+ Container.new
19
+ }
20
+
21
+ it 'can store objects' do
22
+ container.object(:lorem, 'lorem')
23
+ expect(container.lookup(:lorem)).to eq('lorem')
24
+ end
25
+
26
+ it 'can store factories' do
27
+ container.factory(:lorem, Lorem)
28
+ expect(container.lookup(:lorem)).to be_a(Lorem)
29
+ end
30
+
31
+ it 'can store singletons' do
32
+ container.singleton(:lorem, Lorem)
33
+ instance1 = container.lookup(:lorem)
34
+ instance2 = container.lookup(:lorem)
35
+
36
+ expect(instance1).to eq(instance2)
37
+ end
38
+
39
+ it 'can be configured with a DSL' do
40
+ container.configure do
41
+ object :lorem, 'lorem'
42
+ factory :lipsum, Lorem
43
+ singleton :dolor, Lorem
44
+ end
45
+
46
+ expect(container.lookup(:lorem)).to eq('lorem')
47
+ expect(container.lookup(:lipsum)).to be_a(Lorem)
48
+
49
+ instance1 = container.lookup(:dolor)
50
+ instance2 = container.lookup(:dolor)
51
+
52
+ expect(instance1).to eq(instance2)
53
+ end
54
+
55
+ it 'raises a KeyError for unknown keys' do
56
+ expect {
57
+ container.lookup(:unknown_key)
58
+ }.to raise_error(KeyError)
59
+ end
60
+
61
+ it 'can create nested containers' do
62
+ child = container.child()
63
+ grand_child = child.child()
64
+
65
+ expect(grand_child.parent).to eq(child)
66
+ expect(child.parent).to eq(container)
67
+ expect(container.parent).to be_nil
68
+ end
69
+
70
+ it 'can lookup values from nested containers' do
71
+ child = container.child()
72
+ grand_child = child.child()
73
+
74
+ container.object(:lorem, 'in_container')
75
+ child.object(:lorem, 'in_child')
76
+ grand_child.object(:lorem, 'in_grand_child')
77
+
78
+ expect(grand_child.lookup(:lorem)).to eq('in_grand_child')
79
+
80
+ grand_child.unregister(:lorem)
81
+ expect(grand_child.lookup(:lorem)).to eq('in_child')
82
+
83
+ child.unregister(:lorem)
84
+ expect(grand_child.lookup(:lorem)).to eq('in_container')
85
+ end
86
+
87
+ it 'can clear the container store' do
88
+ container.object(:lorem, 'lorem')
89
+ container.object(:ipsum, 'ipsum')
90
+ container.object(:dolor, 'dolor')
91
+
92
+ container.clear
93
+
94
+ expect(container.contains?(:lorem)).to be_false
95
+ expect(container.contains?(:ipsum)).to be_false
96
+ expect(container.contains?(:dolor)).to be_false
97
+ end
98
+
99
+ it 'can inject dependencies' do
100
+ container.configure do
101
+ object :apples, 10
102
+ object :mangoes, 20
103
+ object :oranges, 30
104
+ factory :box, MyBox
105
+ end
106
+
107
+ box = container.lookup(:box)
108
+ expect(box.apples).to eq(10)
109
+ expect(box.mangoes).to eq(20)
110
+ expect(box.oranges).to eq(30)
111
+ end
112
+
113
+ it 'can inject dependencies lazily' do
114
+ container.configure do
115
+ object :apples, -> { 10 }
116
+ object :mangoes, -> { 20 }
117
+ object :oranges, -> { 30 }
118
+ factory :box, -> { MyBox }
119
+ end
120
+
121
+ box = container.lookup(:box)
122
+ expect(box.apples).to eq(10)
123
+ expect(box.mangoes).to eq(20)
124
+ expect(box.oranges).to eq(30)
125
+ end
126
+
127
+ it 'assigns container on injection' do
128
+ class DummyObjectToBeInjected
129
+ include Encase
130
+ needs :lorem
131
+ end
132
+
133
+ container.object(:lorem, 'lorem')
134
+ container.factory(:dummy, DummyObjectToBeInjected)
135
+
136
+ dummy = container.lookup(:dummy)
137
+ expect(dummy.container).to eq(container)
138
+ end
139
+
140
+ it 'call on_inject hook after injection' do
141
+ class DummyWithOnInject
142
+ include Encase
143
+ needs :lorem
144
+ attr_accessor :injected
145
+
146
+ def on_inject
147
+ self.injected = true
148
+ end
149
+ end
150
+
151
+ container.object(:lorem, 'lorem')
152
+ container.factory(:dummy, DummyWithOnInject)
153
+
154
+ dummy = container.lookup(:dummy)
155
+ expect(dummy.injected).to be_true
156
+ end
157
+
158
+ context 'Performance', :benchmark => true do
159
+ require 'benchmark'
160
+
161
+ it 'can store 100s of dependencies quickly' do
162
+ runtime = Benchmark.realtime do
163
+ container.configure do
164
+ 10000.times do |i|
165
+ item = "item#{i}"
166
+ object item.to_sym, item
167
+ end
168
+ end
169
+ end
170
+
171
+ expect(runtime).to be < 0.1
172
+ end
173
+
174
+ it 'can lookup 100s of dependencies quickly' do
175
+ class ClassWithManyDeps
176
+ include Encase
177
+
178
+ 100.times do |i|
179
+ needs "item#{i}".to_sym
180
+ end
181
+ end
182
+
183
+ container.configure do
184
+ 100.times do |i|
185
+ item = "item#{i}"
186
+ object item.to_sym, item
187
+ end
188
+
189
+ factory :lorem, ClassWithManyDeps
190
+ end
191
+
192
+ runtime = Benchmark.realtime do
193
+ 100.times do |i|
194
+ container.lookup(:lorem)
195
+ end
196
+ end
197
+
198
+ expect(runtime).to be < 0.1
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'encase'
3
+
4
+ class EmptyBox
5
+ end
6
+
7
+ class Box
8
+ include Encase
9
+
10
+ needs :apples, :mangoes
11
+ needs :oranges
12
+ end
13
+
14
+ describe 'Encaseable' do
15
+ let(:box) { Box.new }
16
+ let(:empty_box) { EmptyBox.new }
17
+
18
+ it 'creates accessors if needs are specified' do
19
+ expect(Box).to respond_to(:needs_to_inject)
20
+ expect(box).to respond_to(:container)
21
+ expect(box).to respond_to(:apples)
22
+ expect(box).to respond_to(:mangoes)
23
+ expect(box).to respond_to(:oranges)
24
+ end
25
+
26
+ it 'does not create accessors if needs are not specified' do
27
+ expect(EmptyBox).to_not respond_to(:needs_to_inject)
28
+ expect(empty_box).to_not respond_to(:container)
29
+ expect(empty_box).to_not respond_to(:apples)
30
+ expect(empty_box).to_not respond_to(:mangoes)
31
+ expect(empty_box).to_not respond_to(:oranges)
32
+ end
33
+
34
+ it 'stores provided dependencies for later lookup' do
35
+ expect(box.class.needs_to_inject).to match_array([:apples, :mangoes, :oranges])
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'encase/factory_item'
3
+
4
+ module Encase
5
+ class MyClass
6
+ end
7
+
8
+ describe FactoryItem do
9
+ let(:container) {
10
+ c = double()
11
+ c.stub(:inject).with(anything()) { true }
12
+ c
13
+ }
14
+
15
+ let(:factory_item) {
16
+ f = FactoryItem.new(container)
17
+ f.store(:lorem, MyClass)
18
+ f
19
+ }
20
+
21
+ it 'creates a new instance on every fetch' do
22
+ factory_item.reify()
23
+
24
+ instance1 = factory_item.fetch()
25
+ instance2 = factory_item.fetch()
26
+
27
+ expect(instance1).to_not eq(instance2)
28
+ end
29
+
30
+ it 'applies injection on created instance' do
31
+ container.stub(:inject) do |to_inject|
32
+ expect(to_inject).to be_a(MyClass)
33
+ true
34
+ end
35
+
36
+ factory_item.instance
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'encase/object_item'
3
+
4
+ module Encase
5
+ describe ObjectItem do
6
+ let(:container) {
7
+ c = double()
8
+ c.stub(:inject).with(anything()) { true }
9
+ c
10
+ }
11
+
12
+ let(:object_item) {
13
+ o = ObjectItem.new(container)
14
+ o.store(:lorem, 'lorem')
15
+ o
16
+ }
17
+
18
+ it 'applies injection if not injected' do
19
+ expect(object_item.inject('lorem')).to be_true
20
+ expect(object_item.injected).to be_true
21
+ end
22
+
23
+ it 'does not apply injection if already injected' do
24
+ object_item.inject('lorem')
25
+
26
+ expect(object_item.inject('lorem')).to be_false
27
+ expect(object_item.injected).to be_true
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'encase/singleton_item'
3
+
4
+ module Encase
5
+ class MySingletonClass
6
+ end
7
+
8
+ describe SingletonItem do
9
+ let(:container) {
10
+ c = double()
11
+ c.stub(:inject).with(anything()) { true }
12
+ c
13
+ }
14
+
15
+ let(:singleton_item) {
16
+ s = SingletonItem.new(container)
17
+ s.store(:lorem, MySingletonClass)
18
+ s
19
+ }
20
+
21
+ it 'returns the same object on every fetch' do
22
+ singleton_item.reify()
23
+
24
+ instance1 = singleton_item.fetch()
25
+ instance2 = singleton_item.fetch()
26
+
27
+ expect(instance1).to eq(instance2)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH << '../lib'
2
+
3
+ RSpec.configure do |config|
4
+ config.treat_symbols_as_metadata_keys_with_true_values = true
5
+ config.run_all_when_everything_filtered = true
6
+ config.filter_run :focus
7
+ config.order = 'random'
8
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: encase
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Darshan Sawardekar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.1
55
+ description: Encase is a library for using dependency injection within ruby applications.
56
+ It provides a lightweight IOC Container that manages dependencies between your classes.
57
+ It was written to assist with wiring of domain objects outside Rails applications
58
+ to enable faster test suites.
59
+ email:
60
+ - darshan@sawardekar.org
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - .travis.yml
68
+ - CHANGELOG.md
69
+ - Gemfile
70
+ - LICENSE.txt
71
+ - README.md
72
+ - Rakefile
73
+ - encase.gemspec
74
+ - lib/encase.rb
75
+ - lib/encase/container.rb
76
+ - lib/encase/container_item.rb
77
+ - lib/encase/container_item_factory.rb
78
+ - lib/encase/encaseable.rb
79
+ - lib/encase/factory_item.rb
80
+ - lib/encase/object_item.rb
81
+ - lib/encase/singleton_item.rb
82
+ - lib/encase/version.rb
83
+ - portkey.json
84
+ - spec/lib/encase/container_item_factory_spec.rb
85
+ - spec/lib/encase/container_item_spec.rb
86
+ - spec/lib/encase/container_spec.rb
87
+ - spec/lib/encase/encaseable_spec.rb
88
+ - spec/lib/encase/factory_item_spec.rb
89
+ - spec/lib/encase/object_item_spec.rb
90
+ - spec/lib/encase/singleton_item_spec.rb
91
+ - spec/spec_helper.rb
92
+ homepage: https://github.com/dsawardekar/encase
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.2.0
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Lightweight IOC Container for ruby.
116
+ test_files:
117
+ - spec/lib/encase/container_item_factory_spec.rb
118
+ - spec/lib/encase/container_item_spec.rb
119
+ - spec/lib/encase/container_spec.rb
120
+ - spec/lib/encase/encaseable_spec.rb
121
+ - spec/lib/encase/factory_item_spec.rb
122
+ - spec/lib/encase/object_item_spec.rb
123
+ - spec/lib/encase/singleton_item_spec.rb
124
+ - spec/spec_helper.rb