Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for decorating static values #66

Merged
merged 1 commit into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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