dry-container 0.5.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []