dry-container 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3dc623285b4e64816e0bea0c4668ab012291e234
4
- data.tar.gz: 8fa2abd26fa7064d7765cc7cc27a75e01190888b
3
+ metadata.gz: 746334a72c7c6dbc33c55a831bc0b6be199d5307
4
+ data.tar.gz: 2c86a308642567d9dbc2d91330fd86f5f5dcd7e2
5
5
  SHA512:
6
- metadata.gz: c2dfecb1010dc1a4325ca8dd6842d157a127427895802fe133b41c6bb7ff2605a059be69a78a7d5bd7a0c11e96202d2ad7f42c39a0017cedb29b5c6d68fe8d31
7
- data.tar.gz: 006d27b613370ba76c9d0301286cf7038c8141afe7d3f4c5f835245c1e68ae76aff2b6a70e82c0b5452ad79e6623b166e5b0e2880c147afdf8b495ce9c6a81c2
6
+ metadata.gz: bfd4c3abb602f546e0abdf5e4ff39cdd8abb20ef16b8eea97896fdf6b8fed34324556b9150883c351cd8736493e9d2defb5159a2a5c47223403689e94a0e9d95
7
+ data.tar.gz: fd568448c7ccbb98bb0049b66878b6eccddca1d3db95c47cf3ffea365f5bd6f514eaed861b142a9e77151c9dab04f15566b9e401a5ca58a69a60f9edbb1af3f5
data/.rubocop.yml CHANGED
@@ -10,3 +10,11 @@ Style/Documentation:
10
10
  Lint/HandleExceptions:
11
11
  Exclude:
12
12
  - rakelib/*.rake
13
+
14
+ Lint/NestedMethodDefinition:
15
+ Exclude:
16
+ - lib/dry/container/mixin.rb
17
+
18
+ Metrics/MethodLength:
19
+ Exclude:
20
+ - lib/dry/container/mixin.rb
data/.rubocop_todo.yml CHANGED
@@ -1,11 +1,6 @@
1
1
  # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-06-10 01:03:27 +0100 using RuboCop version 0.26.0.
2
+ # on 2015-06-16 19:51:19 +0100 using RuboCop version 0.32.0.
3
3
  # The point is for the user to remove these configuration records
4
4
  # one by one as the offenses are removed from the code base.
5
5
  # Note that changes in the inspected code, or installation of new
6
6
  # versions of RuboCop, may require this file to be generated again.
7
-
8
- # Offense count: 1
9
- # Configuration parameters: CountComments.
10
- Metrics/MethodLength:
11
- Max: 14
data/.travis.yml CHANGED
@@ -20,3 +20,5 @@ matrix:
20
20
  allow_failures:
21
21
  - rvm: ruby-head
22
22
  - rvm: jruby-head
23
+ notifications:
24
+ email: false
data/LICENSE CHANGED
@@ -18,4 +18,3 @@ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
18
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
19
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
20
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
- s
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <a href="https://travis-ci.org/dryrb/dry-container" target="_blank">![Build Status](https://travis-ci.org/dryrb/dry-container.svg?branch=master)</a>
5
5
  <a href="http://inch-ci.org/github/dryrb/dry-container" target="_blank">![Inline docs](http://inch-ci.org/github/dryrb/dry-container.svg?branch=master&style=flat)</a>
6
6
 
7
- A simple IoC container implemented in Ruby
7
+ A simple, configurable container implemented in Ruby
8
8
 
9
9
  ## Synopsis
10
10
 
@@ -69,6 +69,36 @@ container.register(:item, :my_item)
69
69
  container.resolve(:item)
70
70
  # => :my_item
71
71
  ```
72
+ ### Using a custom registry/resolver
73
+
74
+ You can configure how items are registered and resolved from the container:
75
+
76
+ ```ruby
77
+ Dry::Container.configure do |config|
78
+ config.registry = ->(container, key, item, options) { container[key] = item }
79
+ config.resolver = ->(key) { container[key] }
80
+ end
81
+
82
+ class Container
83
+ extend Dry::Container::Mixin
84
+
85
+ configure do |config|
86
+ config.registry = ->(container, key, item, options) { container[key] = item }
87
+ config.resolver = ->(key) { container[key] }
88
+ end
89
+ end
90
+
91
+ class ContainerObject
92
+ include Dry::Container::Mixin
93
+
94
+ configure do |config|
95
+ config.registry = ->(container, key, item, options) { container[key] = item }
96
+ config.resolver = ->(key) { container[key] }
97
+ end
98
+ end
99
+ ```
100
+
101
+ This allows you to customise the behaviour of Dry::Container, for example, the default registry (Dry::Container::Registry) will raise a Dry::Container::Error exception if you try to register under a key that is already used, you may want to just overwrite the existing value in that scenario, configuration allows you to do so.
72
102
 
73
103
  ## License
74
104
 
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.require_paths = ['lib']
17
17
 
18
18
  spec.add_runtime_dependency 'thread_safe'
19
+ spec.add_runtime_dependency 'dry-configurable', '0.1.0'
19
20
 
20
21
  spec.add_development_dependency 'bundler'
21
22
  spec.add_development_dependency 'rake'
data/lib/dry/container.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'thread_safe'
2
+ require 'dry/configurable'
2
3
  require 'dry/container/error'
3
4
  require 'dry/container/item'
5
+ require 'dry/container/registry'
6
+ require 'dry/container/resolver'
4
7
  require 'dry/container/mixin'
5
8
  require 'dry/container/version'
6
9
 
@@ -26,13 +26,37 @@ module Dry
26
26
  module Mixin
27
27
  # @private
28
28
  def self.extended(base)
29
- base.instance_variable_set(:@_mutex, Mutex.new)
29
+ base.class_eval do
30
+ extend ::Dry::Configurable
31
+
32
+ setting :registry, Registry.new
33
+ setting :resolver, Resolver.new
34
+
35
+ @_container = ThreadSafe::Cache.new
36
+
37
+ class << self
38
+ attr_reader :_container
39
+ end
40
+ end
30
41
  end
31
42
  # @private
32
43
  def self.included(base)
33
- base.send(:define_method, :initialize) do |*args, &block|
34
- @_mutex = Mutex.new
35
- super(*args, &block)
44
+ base.class_eval do
45
+ extend ::Dry::Configurable
46
+
47
+ setting :registry, Registry.new
48
+ setting :resolver, Resolver.new
49
+
50
+ attr_reader :_container
51
+
52
+ def initialize(*args, &block)
53
+ @_container = ThreadSafe::Cache.new
54
+ super(*args, &block)
55
+ end
56
+
57
+ def config
58
+ self.class.config
59
+ end
36
60
  end
37
61
  end
38
62
  # Register an item with the container to be resolved later
@@ -42,15 +66,11 @@ module Dry
42
66
  # @param [Mixed] contents
43
67
  # The item to register with the container (if no block given)
44
68
  # @param [Hash] options
45
- # @option options [Symbol] :call
46
- # Whether the item should be called when resolved
69
+ # Options to pass to the registry when registering the item
47
70
  # @yield
48
71
  # If a block is given, contents will be ignored and the block
49
72
  # will be registered instead
50
73
  #
51
- # @raise [Dry::Conainer::Error]
52
- # If an item is already registered with the given key
53
- #
54
74
  # @return [Dry::Container] self
55
75
  #
56
76
  # @api public
@@ -62,11 +82,7 @@ module Dry
62
82
  item = contents
63
83
  end
64
84
 
65
- if _container.key?(key)
66
- fail Error, "There is already an item registered with the key #{key.inspect}"
67
- else
68
- _container[key] = Item.new(item, options)
69
- end
85
+ config.registry.call(_container, key, item, options)
70
86
 
71
87
  self
72
88
  end
@@ -75,18 +91,11 @@ module Dry
75
91
  # @param [Mixed] key
76
92
  # The key for the item you wish to resolve
77
93
  #
78
- # @raise [Dry::Conainer::Error]
79
- # If the given key is not registered with the container
80
- #
81
94
  # @return [Mixed]
82
95
  #
83
96
  # @api public
84
97
  def resolve(key)
85
- item = _container.fetch(key) do
86
- fail Error, "Nothing registered with the key #{key.inspect}"
87
- end
88
-
89
- item.call
98
+ config.resolver.call(_container, key)
90
99
  end
91
100
  # Resolve an item from the container
92
101
  #
@@ -99,13 +108,6 @@ module Dry
99
108
  def [](key)
100
109
  resolve(key)
101
110
  end
102
-
103
- private
104
-
105
- # @private
106
- def _container
107
- @_mutex.synchronize { @_container ||= ThreadSafe::Cache.new }
108
- end
109
111
  end
110
112
  end
111
113
  end
@@ -0,0 +1,40 @@
1
+ module Dry
2
+ class Container
3
+ # Default registry for registering items with the container
4
+ #
5
+ # @api public
6
+ class Registry
7
+ # @private
8
+ def initialize
9
+ @_mutex = Mutex.new
10
+ end
11
+ # Register an item with the container to be resolved later
12
+ #
13
+ # @param [ThreadSafe::Hash] container
14
+ # The container
15
+ # @param [Mixed] key
16
+ # The key to register the container item with (used to resolve)
17
+ # @param [Mixed] item
18
+ # The item to register with the container
19
+ # @param [Hash] options
20
+ # @option options [Symbol] :call
21
+ # Whether the item should be called when resolved
22
+ #
23
+ # @raise [Dry::Conainer::Error]
24
+ # If an item is already registered with the given key
25
+ #
26
+ # @return [Mixed]
27
+ #
28
+ # @api public
29
+ def call(container, key, item, options)
30
+ @_mutex.synchronize do
31
+ if container.key?(key)
32
+ fail Error, "There is already an item registered with the key #{key.inspect}"
33
+ else
34
+ container[key] = Item.new(item, options)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ module Dry
2
+ class Container
3
+ # Default resolver for resolving items from container
4
+ #
5
+ # @api public
6
+ class Resolver
7
+ # Resolve an item from the container
8
+ #
9
+ # @param [ThreadSafe::Hash] container
10
+ # The container
11
+ # @param [Mixed] key
12
+ # The key for the item you wish to resolve
13
+ #
14
+ # @raise [Dry::Conainer::Error]
15
+ # If the given key is not registered with the container
16
+ #
17
+ # @return [Mixed]
18
+ #
19
+ # @api public
20
+ def call(container, key)
21
+ item = container.fetch(key) do
22
+ fail Error, "Nothing registered with the key #{key.inspect}"
23
+ end
24
+
25
+ item.call
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,6 +1,6 @@
1
1
  module Dry
2
2
  class Container
3
3
  # @api public
4
- VERSION = '0.1.0'.freeze
4
+ VERSION = '0.2.0'.freeze
5
5
  end
6
6
  end
@@ -1,5 +1,6 @@
1
1
  RSpec.describe Dry::Container do
2
- let(:container) { Dry::Container.new }
2
+ let(:klass) { Dry::Container }
3
+ let(:container) { klass.new }
3
4
 
4
5
  it_behaves_like 'a container'
5
6
  end
@@ -1,16 +1,18 @@
1
1
  RSpec.describe Dry::Container::Mixin do
2
2
  describe 'extended' do
3
- let(:container) do
3
+ let(:klass) do
4
4
  Class.new { extend Dry::Container::Mixin }
5
5
  end
6
+ let(:container) { klass }
6
7
 
7
8
  it_behaves_like 'a container'
8
9
  end
9
10
 
10
11
  describe 'included' do
11
- let(:container) do
12
- Class.new { include Dry::Container::Mixin }.new
12
+ let(:klass) do
13
+ Class.new { include Dry::Container::Mixin }
13
14
  end
15
+ let(:container) { klass.new }
14
16
 
15
17
  it_behaves_like 'a container'
16
18
  end
@@ -1,76 +1,153 @@
1
1
  shared_examples 'a container' do
2
- describe 'registering a block' do
3
- context 'without options' do
4
- it 'registers and resolves an object' do
5
- container.register(:item) { 'item' }
2
+ describe 'configuration' do
3
+ describe 'registry' do
4
+ describe 'default' do
5
+ it { expect(klass.config.registry).to be_a(Dry::Container::Registry) }
6
+ end
7
+
8
+ describe 'custom' do
9
+ let(:custom_registry) { double('Registry') }
10
+ let(:key) { :key }
11
+ let(:item) { :item }
12
+ let(:options) { {} }
13
+
14
+ before do
15
+ klass.configure do |config|
16
+ config.registry = custom_registry
17
+ end
18
+
19
+ allow(custom_registry).to receive(:call)
20
+ end
6
21
 
7
- expect(container.resolve(:item)).to eq('item')
22
+ after do
23
+ # HACK: Have to reset the configuration so that it doesn't
24
+ # interfere with other specs
25
+ klass.configure do |config|
26
+ config.registry = Dry::Container::Registry.new
27
+ end
28
+ end
29
+
30
+ subject! { container.register(key, item, options) }
31
+
32
+ it do
33
+ expect(custom_registry).to have_received(:call).with(
34
+ container._container,
35
+ key,
36
+ item,
37
+ options
38
+ )
39
+ end
8
40
  end
9
41
  end
10
42
 
11
- context 'with option call: false' do
12
- it 'registers and resolves a proc' do
13
- container.register(:item, call: false) { 'item' }
43
+ describe 'resolver' do
44
+ describe 'default' do
45
+ it { expect(klass.config.resolver).to be_a(Dry::Container::Resolver) }
46
+ end
14
47
 
15
- expect(container.resolve(:item).call).to eq('item')
16
- expect(container[:item].call).to eq('item')
48
+ describe 'custom' do
49
+ let(:custom_resolver) { double('Resolver') }
50
+ let(:item) { double('Item') }
51
+ let(:key) { :key }
52
+
53
+ before do
54
+ klass.configure do |config|
55
+ config.resolver = custom_resolver
56
+ end
57
+
58
+ allow(custom_resolver).to receive(:call).and_return(item)
59
+ end
60
+
61
+ after do
62
+ # HACK: Have to reset the configuration so that it doesn't
63
+ # interfere with other specs
64
+ klass.configure do |config|
65
+ config.resolver = Dry::Container::Resolver.new
66
+ end
67
+ end
68
+
69
+ subject! { container.resolve(key) }
70
+
71
+ it { expect(custom_resolver).to have_received(:call).with(container._container, key) }
72
+ it { is_expected.to eq(item) }
17
73
  end
18
74
  end
19
75
  end
20
76
 
21
- describe 'registering a proc' do
22
- context 'without options' do
23
- it 'registers and resolves an object' do
24
- container.register(:item, proc { 'item' })
77
+ context 'with default configuration' do
78
+ describe 'registering a block' do
79
+ context 'without options' do
80
+ it 'registers and resolves an object' do
81
+ container.register(:item) { 'item' }
25
82
 
26
- expect(container.resolve(:item)).to eq('item')
27
- expect(container[:item]).to eq('item')
83
+ expect(container.resolve(:item)).to eq('item')
84
+ end
28
85
  end
29
- end
30
86
 
31
- context 'with option call: false' do
32
- it 'registers and resolves a proc' do
33
- container.register(:item, proc { 'item' }, call: false)
87
+ context 'with option call: false' do
88
+ it 'registers and resolves a proc' do
89
+ container.register(:item, call: false) { 'item' }
34
90
 
35
- expect(container.resolve(:item).call).to eq('item')
36
- expect(container[:item].call).to eq('item')
91
+ expect(container.resolve(:item).call).to eq('item')
92
+ expect(container[:item].call).to eq('item')
93
+ end
37
94
  end
38
95
  end
39
- end
40
96
 
41
- describe 'registering an object' do
42
- context 'without options' do
43
- it 'registers and resolves the object' do
44
- item = 'item'
45
- container.register(:item, item)
97
+ describe 'registering a proc' do
98
+ context 'without options' do
99
+ it 'registers and resolves an object' do
100
+ container.register(:item, proc { 'item' })
46
101
 
47
- expect(container.resolve(:item)).to be(item)
48
- expect(container[:item]).to be(item)
102
+ expect(container.resolve(:item)).to eq('item')
103
+ expect(container[:item]).to eq('item')
104
+ end
105
+ end
106
+
107
+ context 'with option call: false' do
108
+ it 'registers and resolves a proc' do
109
+ container.register(:item, proc { 'item' }, call: false)
110
+
111
+ expect(container.resolve(:item).call).to eq('item')
112
+ expect(container[:item].call).to eq('item')
113
+ end
49
114
  end
50
115
  end
51
116
 
52
- context 'with option call: false' do
53
- it 'registers and resolves an object' do
54
- item = -> { 'test' }
55
- container.register(:item, item, call: false)
117
+ describe 'registering an object' do
118
+ context 'without options' do
119
+ it 'registers and resolves the object' do
120
+ item = 'item'
121
+ container.register(:item, item)
56
122
 
57
- expect(container.resolve(:item)).to eq(item)
58
- expect(container[:item]).to eq(item)
123
+ expect(container.resolve(:item)).to be(item)
124
+ expect(container[:item]).to be(item)
125
+ end
126
+ end
127
+
128
+ context 'with option call: false' do
129
+ it 'registers and resolves an object' do
130
+ item = -> { 'test' }
131
+ container.register(:item, item, call: false)
132
+
133
+ expect(container.resolve(:item)).to eq(item)
134
+ expect(container[:item]).to eq(item)
135
+ end
59
136
  end
60
137
  end
61
- end
62
138
 
63
- describe 'registering with the same key multiple times' do
64
- it do
65
- container.register(:item, proc { 'item' })
139
+ describe 'registering with the same key multiple times' do
140
+ it do
141
+ container.register(:item, proc { 'item' })
66
142
 
67
- expect { container.register(:item, proc { 'item' }) }.to raise_error(Dry::Container::Error)
143
+ expect { container.register(:item, proc { 'item' }) }.to raise_error(Dry::Container::Error)
144
+ end
68
145
  end
69
- end
70
146
 
71
- describe 'resolving with a key that has not been registered' do
72
- it do
73
- expect { container.resolve(:item) }.to raise_error(Dry::Container::Error)
147
+ describe 'resolving with a key that has not been registered' do
148
+ it do
149
+ expect { container.resolve(:item) }.to raise_error(Dry::Container::Error)
150
+ end
74
151
  end
75
152
  end
76
153
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-container
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Holland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-10 00:00:00.000000000 Z
11
+ date: 2015-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thread_safe
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-configurable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +101,8 @@ files:
87
101
  - lib/dry/container/error.rb
88
102
  - lib/dry/container/item.rb
89
103
  - lib/dry/container/mixin.rb
104
+ - lib/dry/container/registry.rb
105
+ - lib/dry/container/resolver.rb
90
106
  - lib/dry/container/version.rb
91
107
  - rakelib/rubocop.rake
92
108
  - spec/integration/container_spec.rb