Skip to content

Commit

Permalink
Merge pull request #66 from dry-rb/memoize-static-values
Browse files Browse the repository at this point in the history
Support for decorating static values
  • Loading branch information
flash-gordon authored Jul 9, 2019
2 parents 268e483 + 755fe81 commit 72029ea
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 26 deletions.
21 changes: 21 additions & 0 deletions lib/dry/container/item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ def initialize(item, options = {})
def call
raise NotImplementedError
end

# @private
def value?
!callable?
end

# @private
def callable?
options[:call]
end

# Build a new item with transformation applied
#
# @private
def map(func)
if callable?
self.class.new(-> { func.(item.call) }, options)
else
self.class.new(func.(item), options)
end
end
end
end
end
7 changes: 0 additions & 7 deletions lib/dry/container/item/callable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@ class Callable < Item
def call
callable? ? item.call : item
end

private

# @private
def callable?
options[:call]
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/dry/container/item/memoizable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Memoizable < Item
# @return [Dry::Container::Item::Base]
def initialize(item, options = {})
super
raise_not_supported_error unless item.is_a?(::Proc)
raise_not_supported_error unless callable?

@memoize_mutex = ::Mutex.new
end
Expand Down
26 changes: 14 additions & 12 deletions lib/dry/container/mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ class Container
PREFIX_NAMESPACE = lambda do |namespace, key, config|
[namespace, key].join(config.namespace_separator)
end

EMPTY_HASH = {}.freeze

# Mixin to expose Inversion of Control (IoC) container behaviour
#
# @example
Expand Down Expand Up @@ -90,7 +93,7 @@ def config
# @return [Dry::Container::Mixin] self
#
# @api public
def register(key, contents = nil, options = {}, &block)
def register(key, contents = nil, options = EMPTY_HASH, &block)
if block_given?
item = block
options = contents if contents.is_a?(::Hash)
Expand Down Expand Up @@ -208,23 +211,22 @@ def each(&block)
# @return [Dry::Container::Mixin] self
#
# @api public
def decorate(key, with:)
original = _container.delete(key.to_s) do
def decorate(key, with: nil, &block)
key = key.to_s
original = _container.delete(key) do
raise Error, "Nothing registered with the key #{key.inspect}"
end

decorator = with
memoize = original.is_a?(Item::Memoizable)

if decorator.is_a?(Class)
decorated = -> { decorator.new(original.call) }
elsif decorator.respond_to?(:call)
decorated = -> { decorator.call(original.call) }
if with.is_a?(Class)
decorator = with.method(:new)
elsif block.nil? && !with.respond_to?(:call)
raise Error, "Decorator needs to be a Class, block, or respond to the `call` method"
else
raise Error, "Decorator needs to be a Class or responds to the `call` method"
decorator = with || block
end

register(key, memoize: memoize, &decorated)
_container[key] = original.map(decorator)
self
end

# Evaluate block and register items in namespace
Expand Down
32 changes: 26 additions & 6 deletions spec/support/shared_examples/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -426,14 +426,34 @@
end

context 'for not callable item' do
before do
container.register(key, call: false) { "value" }
container.decorate(key, with: SimpleDelegator)
describe 'wrapping' do
before do
container.register(key, call: false) { "value" }
container.decorate(key, with: SimpleDelegator)
end

it 'expected to be an instance of SimpleDelegator' do
expect(container.resolve(key)).to be_instance_of(SimpleDelegator)
expect(container.resolve(key).__getobj__.call).to eql("value")
end
end

it 'expected to be an instance of SimpleDelegator' do
expect(container.resolve(key)).to be_instance_of(SimpleDelegator)
expect(container.resolve(key).__getobj__.call).to eql("value")
describe 'memoization' do
before do
@called = 0
container.register(key, 'value')

container.decorate(key) do |value|
@called += 1
"<#{value}>"
end
end

it 'decorates static value only once' do
expect(container.resolve(key)).to eql('<value>')
expect(container.resolve(key)).to eql('<value>')
expect(@called).to be(1)
end
end
end

Expand Down

0 comments on commit 72029ea

Please sign in to comment.