dry-container 0.5.0 → 0.8.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.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/container/item"
4
+
5
+ module Dry
6
+ class Container
7
+ class Item
8
+ # Callable class to returns a item call
9
+ #
10
+ # @api public
11
+ #
12
+ class Callable < Item
13
+ # Returns the result of item call or item
14
+ #
15
+ # @return [Mixed]
16
+ def call
17
+ callable? ? item.call : item
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/container/item/memoizable"
4
+ require "dry/container/item/callable"
5
+
6
+ module Dry
7
+ class Container
8
+ class Item
9
+ # Factory for create an Item to register inside of container
10
+ #
11
+ # @api public
12
+ class Factory
13
+ # Creates an Item Memoizable or Callable
14
+ # @param [Mixed] item
15
+ # @param [Hash] options
16
+ #
17
+ # @raise [Dry::Container::Error]
18
+ #
19
+ # @return [Dry::Container::Item::Base]
20
+ def call(item, options = {})
21
+ options[:memoize] ? Memoizable.new(item, options) : Callable.new(item, options)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/container/item"
4
+
5
+ module Dry
6
+ class Container
7
+ class Item
8
+ # Memoizable class to store and execute item calls
9
+ #
10
+ # @api public
11
+ #
12
+ class Memoizable < Item
13
+ # @return [Mutex] the stored mutex
14
+ attr_reader :memoize_mutex
15
+
16
+ # Returns a new Memoizable instance
17
+ #
18
+ # @param [Mixed] item
19
+ # @param [Hash] options
20
+ #
21
+ # @raise [Dry::Container::Error]
22
+ #
23
+ # @return [Dry::Container::Item::Base]
24
+ def initialize(item, options = {})
25
+ super
26
+ raise_not_supported_error unless callable?
27
+
28
+ @memoize_mutex = ::Mutex.new
29
+ end
30
+
31
+ # Returns the result of item call using a syncronized mutex
32
+ #
33
+ # @return [Dry::Container::Item::Base]
34
+ def call
35
+ memoize_mutex.synchronize do
36
+ @memoized_item ||= item.call
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # @private
43
+ def raise_not_supported_error
44
+ raise ::Dry::Container::Error, "Memoize only supported for a block or a proc".freeze
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,8 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/hash"
4
+
1
5
  module Dry
2
6
  class Container
3
- PREFIX_NAMESPACE = ->(namespace, key, config) do
7
+ PREFIX_NAMESPACE = lambda do |namespace, key, config|
4
8
  [namespace, key].join(config.namespace_separator)
5
9
  end
10
+
11
+ EMPTY_HASH = {}.freeze
12
+
6
13
  # Mixin to expose Inversion of Control (IoC) container behaviour
7
14
  #
8
15
  # @example
@@ -42,9 +49,17 @@ module Dry
42
49
 
43
50
  setting :registry, ::Dry::Container::Registry.new
44
51
  setting :resolver, ::Dry::Container::Resolver.new
45
- setting :namespace_separator, '.'
52
+ setting :namespace_separator, "."
53
+
54
+ @_container = ::Concurrent::Hash.new
55
+ end
56
+ end
46
57
 
58
+ # @private
59
+ module Initializer
60
+ def initialize(*args, &block)
47
61
  @_container = ::Concurrent::Hash.new
62
+ super
48
63
  end
49
64
  end
50
65
 
@@ -52,15 +67,11 @@ module Dry
52
67
  def self.included(base)
53
68
  base.class_eval do
54
69
  extend ::Dry::Configurable
70
+ prepend Initializer
55
71
 
56
72
  setting :registry, ::Dry::Container::Registry.new
57
73
  setting :resolver, ::Dry::Container::Resolver.new
58
- setting :namespace_separator, '.'
59
-
60
- def initialize(*args, &block)
61
- @_container = ::Concurrent::Hash.new
62
- super(*args, &block)
63
- end
74
+ setting :namespace_separator, "."
64
75
 
65
76
  def config
66
77
  self.class.config
@@ -83,7 +94,7 @@ module Dry
83
94
  # @return [Dry::Container::Mixin] self
84
95
  #
85
96
  # @api public
86
- def register(key, contents = nil, options = {}, &block)
97
+ def register(key, contents = nil, options = EMPTY_HASH, &block)
87
98
  if block_given?
88
99
  item = block
89
100
  options = contents if contents.is_a?(::Hash)
@@ -100,12 +111,15 @@ module Dry
100
111
  #
101
112
  # @param [Mixed] key
102
113
  # The key for the item you wish to resolve
114
+ # @yield
115
+ # Fallback block to call when a key is missing. Its result will be returned
116
+ # @yieldparam [Mixed] key Missing key
103
117
  #
104
118
  # @return [Mixed]
105
119
  #
106
120
  # @api public
107
- def resolve(key)
108
- config.resolver.call(_container, key)
121
+ def resolve(key, &block)
122
+ config.resolver.call(_container, key, &block)
109
123
  end
110
124
 
111
125
  # Resolve an item from the container
@@ -125,8 +139,7 @@ module Dry
125
139
  #
126
140
  # @param [Dry::Container] other
127
141
  # The other container to merge in
128
- # @param [Hash] options
129
- # @option options [Symbol] :namespace
142
+ # @param [Symbol, nil] namespace
130
143
  # Namespace to prefix other container items with, defaults to nil
131
144
  #
132
145
  # @return [Dry::Container::Mixin] self
@@ -146,7 +159,7 @@ module Dry
146
159
  self
147
160
  end
148
161
 
149
- # Check whether an items is registered under the given key
162
+ # Check whether an item is registered under the given key
150
163
  #
151
164
  # @param [Mixed] key
152
165
  # The key you wish to check for registration with
@@ -179,6 +192,44 @@ module Dry
179
192
  self
180
193
  end
181
194
 
195
+ # Calls block once for each key/value pair in the container, passing the key and the registered item parameters.
196
+ #
197
+ # If no block is given, an enumerator is returned instead.
198
+ #
199
+ # @return [Enumerator]
200
+ #
201
+ # @api public
202
+ #
203
+ # @note In discussions with other developers, it was felt that being able to iterate over not just
204
+ # the registered keys, but to see what was registered would be very helpful. This is a step
205
+ # toward doing that.
206
+ def each(&block)
207
+ config.resolver.each(_container, &block)
208
+ end
209
+
210
+ # Decorates an item from the container with specified decorator
211
+ #
212
+ # @return [Dry::Container::Mixin] self
213
+ #
214
+ # @api public
215
+ def decorate(key, with: nil, &block)
216
+ key = key.to_s
217
+ original = _container.delete(key) do
218
+ raise Error, "Nothing registered with the key #{key.inspect}"
219
+ end
220
+
221
+ if with.is_a?(Class)
222
+ decorator = with.method(:new)
223
+ elsif block.nil? && !with.respond_to?(:call)
224
+ raise Error, "Decorator needs to be a Class, block, or respond to the `call` method"
225
+ else
226
+ decorator = with || block
227
+ end
228
+
229
+ _container[key] = original.map(decorator)
230
+ self
231
+ end
232
+
182
233
  # Evaluate block and register items in namespace
183
234
  #
184
235
  # @param [Mixed] namespace
@@ -212,10 +263,35 @@ module Dry
212
263
  self
213
264
  end
214
265
 
266
+ # Freeze the container. Nothing can be registered after freezing
267
+ #
268
+ # @api public
269
+ def freeze
270
+ super
271
+ _container.freeze
272
+ self
273
+ end
274
+
215
275
  # @private no, really
216
276
  def _container
217
277
  @_container
218
278
  end
279
+
280
+ # @api public
281
+ def dup
282
+ copy = super
283
+ copy.instance_variable_set(:@_container, _container.dup)
284
+ copy
285
+ end
286
+
287
+ # @api public
288
+ def clone
289
+ copy = super
290
+ unless copy.frozen?
291
+ copy.instance_variable_set(:@_container, _container.dup)
292
+ end
293
+ copy
294
+ end
219
295
  end
220
296
  end
221
297
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Container
3
5
  # Create a namespace to be imported
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
1
5
  module Dry
2
6
  class Container
3
7
  # @api private
@@ -43,6 +47,10 @@ module Dry
43
47
  self
44
48
  end
45
49
 
50
+ def resolve(key)
51
+ super(namespaced(key))
52
+ end
53
+
46
54
  private
47
55
 
48
56
  def namespaced(key)
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/container/item/factory"
4
+
1
5
  module Dry
2
6
  class Container
3
7
  # Default registry for registering items with the container
@@ -34,9 +38,13 @@ module Dry
34
38
  raise Error, "There is already an item registered with the key #{key.inspect}"
35
39
  end
36
40
 
37
- container[key] = ::Dry::Container::Item.new(item, options)
41
+ container[key] = factory.call(item, options)
38
42
  end
39
43
  end
44
+
45
+ def factory
46
+ @factory ||= ::Dry::Container::Item::Factory.new
47
+ end
40
48
  end
41
49
  end
42
50
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Container
3
5
  # Default resolver for resolving items from container
@@ -10,16 +12,24 @@ module Dry
10
12
  # The container
11
13
  # @param [Mixed] key
12
14
  # The key for the item you wish to resolve
15
+ # @yield
16
+ # Fallback block to call when a key is missing. Its result will be returned
17
+ # @yieldparam [Mixed] key Missing key
18
+ #
19
+ # @raise [Dry::Container::Error]
20
+ # If the given key is not registered with the container (and no block provided)
13
21
  #
14
- # @raise [Dry::Conainer::Error]
15
- # If the given key is not registered with the container
16
22
  #
17
23
  # @return [Mixed]
18
24
  #
19
25
  # @api public
20
26
  def call(container, key)
21
27
  item = container.fetch(key.to_s) do
22
- raise Error, "Nothing registered with the key #{key.inspect}"
28
+ if block_given?
29
+ return yield(key)
30
+ else
31
+ raise Error, "Nothing registered with the key #{key.inspect}"
32
+ end
23
33
  end
24
34
 
25
35
  item.call
@@ -48,7 +58,6 @@ module Dry
48
58
  container.keys
49
59
  end
50
60
 
51
-
52
61
  # Calls block once for each key in container, passing the key as a parameter.
53
62
  #
54
63
  # If no block is given, an enumerator is returned instead.
@@ -59,6 +68,20 @@ module Dry
59
68
  def each_key(container, &block)
60
69
  container.each_key(&block)
61
70
  end
71
+
72
+ # Calls block once for each key in container, passing the key and the registered item parameters.
73
+ #
74
+ # If no block is given, an enumerator is returned instead.
75
+ #
76
+ # @return Key, Value
77
+ #
78
+ # @api public
79
+ # @note In discussions with other developers, it was felt that being able to iterate over not just
80
+ # the registered keys, but to see what was registered would be very helpful. This is a step
81
+ # toward doing that.
82
+ def each(container, &block)
83
+ container.map { |key, value| [key, value.call] }.each(&block)
84
+ end
62
85
  end
63
86
  end
64
87
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Container
3
5
  module Stub
@@ -5,12 +7,16 @@ module Dry
5
7
  #
6
8
  # @api public
7
9
  def resolve(key)
8
- _stubs.fetch(key) { super }
10
+ _stubs.fetch(key.to_s) { super }
9
11
  end
10
12
 
11
13
  # Add a stub to the container
12
14
  def stub(key, value, &block)
13
- _stubs[key] = value
15
+ unless key?(key)
16
+ raise ArgumentError, "cannot stub #{key.to_s.inspect} - no such key in container"
17
+ end
18
+
19
+ _stubs[key.to_s] = value
14
20
 
15
21
  if block
16
22
  yield
@@ -23,11 +29,12 @@ module Dry
23
29
  # Remove stubbed keys from the container
24
30
  def unstub(*keys)
25
31
  keys = _stubs.keys if keys.empty?
26
- keys.each { |key| _stubs.delete(key) }
32
+ keys.each { |key| _stubs.delete(key.to_s) }
27
33
  end
28
34
 
29
35
  # Stubs have already been enabled turning this into a noop
30
36
  def enable_stubs!
37
+ # DO NOTHING
31
38
  end
32
39
 
33
40
  private
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Container
3
5
  # @api public
4
- VERSION = '0.5.0'.freeze
6
+ VERSION = "0.8.0".freeze
5
7
  end
6
8
  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.5.0
4
+ version: 0.8.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: 2016-08-31 00:00:00.000000000 Z
11
+ date: 2021-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -86,29 +86,24 @@ dependencies:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
- description:
89
+ description: A simple, configurable object container implemented in Ruby
90
90
  email:
91
91
  - andyholland1991@aol.com
92
92
  executables: []
93
93
  extensions: []
94
94
  extra_rdoc_files: []
95
95
  files:
96
- - ".codeclimate.yml"
97
- - ".gitignore"
98
- - ".rspec"
99
- - ".rubocop.yml"
100
- - ".rubocop_todo.yml"
101
- - ".travis.yml"
102
96
  - CHANGELOG.md
103
- - Gemfile
104
97
  - LICENSE
105
98
  - README.md
106
- - Rakefile
107
99
  - dry-container.gemspec
108
100
  - lib/dry-container.rb
109
101
  - lib/dry/container.rb
110
102
  - lib/dry/container/error.rb
111
103
  - lib/dry/container/item.rb
104
+ - lib/dry/container/item/callable.rb
105
+ - lib/dry/container/item/factory.rb
106
+ - lib/dry/container/item/memoizable.rb
112
107
  - lib/dry/container/mixin.rb
113
108
  - lib/dry/container/namespace.rb
114
109
  - lib/dry/container/namespace_dsl.rb
@@ -116,15 +111,14 @@ files:
116
111
  - lib/dry/container/resolver.rb
117
112
  - lib/dry/container/stub.rb
118
113
  - lib/dry/container/version.rb
119
- - rakelib/rubocop.rake
120
- - spec/integration/container_spec.rb
121
- - spec/integration/mixin_spec.rb
122
- - spec/spec_helper.rb
123
- - spec/support/shared_examples/container.rb
124
- homepage: https://github.com/dryrb/dry-container
114
+ homepage: https://dry-rb.org/gems/dry-container
125
115
  licenses:
126
116
  - MIT
127
- metadata: {}
117
+ metadata:
118
+ allowed_push_host: https://rubygems.org
119
+ changelog_uri: https://github.com/dry-rb/dry-container/blob/master/CHANGELOG.md
120
+ source_code_uri: https://github.com/dry-rb/dry-container
121
+ bug_tracker_uri: https://github.com/dry-rb/dry-container/issues
128
122
  post_install_message:
129
123
  rdoc_options: []
130
124
  require_paths:
@@ -133,20 +127,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
127
  requirements:
134
128
  - - ">="
135
129
  - !ruby/object:Gem::Version
136
- version: 2.0.0
130
+ version: 2.6.0
137
131
  required_rubygems_version: !ruby/object:Gem::Requirement
138
132
  requirements:
139
133
  - - ">="
140
134
  - !ruby/object:Gem::Version
141
135
  version: '0'
142
136
  requirements: []
143
- rubyforge_project:
144
- rubygems_version: 2.5.1
137
+ rubygems_version: 3.1.6
145
138
  signing_key:
146
139
  specification_version: 4
147
- summary: A simple container intended for use as an IoC container
148
- test_files:
149
- - spec/integration/container_spec.rb
150
- - spec/integration/mixin_spec.rb
151
- - spec/spec_helper.rb
152
- - spec/support/shared_examples/container.rb
140
+ summary: A simple, configurable object container implemented in Ruby
141
+ test_files: []