Created
December 28, 2017 18:40
-
-
Save flash-gordon/9a01e1e67c9edab77338ab2e4c68523c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'bundler/inline' | |
gemfile do | |
source 'https://rubygems.org' | |
gem 'dry-auto_inject' | |
gem 'dry-transaction' | |
gem 'dry-container', git: 'https://github.com/dry-rb/dry-container' | |
end | |
class Container | |
extend Dry::Container::Mixin | |
class DynamicItem < ::Dry::Container::Item::Base | |
MissingDependency = Class.new(StandardError) | |
def call(*) | |
raise MissingDependency, "You didn't provide #{ options[:key] }" | |
end | |
end | |
class CallableItem < Dry::Container::Item::Callable | |
def call(container) | |
callable? ? item.call(container) : item | |
end | |
end | |
class Resolver < Dry::Container::Resolver | |
attr_reader :current_container | |
def initialize(current_container) | |
@current_container = current_container | |
end | |
def call(container, key) | |
item = container.fetch(key.to_s) do | |
raise KeyError, "Nothing registered with the key #{ key.inspect }" | |
end | |
item.(current_container) | |
end | |
end | |
class Registry < Dry::Container::Registry | |
def initialize | |
super | |
@factory = proc do |key, item, options| | |
if options[:dynamic] | |
DynamicItem.new(item, **options, key: key) | |
elsif item.is_a?(Proc) | |
CallableItem.new(item, **options, call: true) | |
else | |
CallableItem.new(item, options) | |
end | |
end | |
end | |
def call(container, key, item, options) | |
key = key.to_s.dup.freeze | |
@_mutex.synchronize do | |
existing = container[key] | |
if !existing.nil? && !existing.options[:dynamic] | |
raise Error, "There is already an item registered with the key #{key.inspect}" | |
end | |
container[key] = factory.(key, item, options) | |
end | |
end | |
end | |
configure do |config| | |
config.resolver = Resolver.new(Container) | |
config.registry = Registry.new | |
end | |
class << self | |
def register_dynamic(key, **options) | |
register(key, nil, **options, dynamic: true) | |
end | |
def register_constant(key, &block) | |
register(key) { |container| block.call.new(__container__: container) } | |
end | |
def inherited(klass) | |
super | |
klass.configure do |config| | |
config.resolver = Resolver.new(klass) | |
end | |
end | |
def provide(registrations) | |
parent = self | |
Class.new(self) do | |
registrations.each do |key, value| | |
register(key, value) | |
end | |
if parent.respond_to?(:_stubs, true) | |
enable_stubs! | |
@_stubs = parent.send(:_stubs) | |
end | |
end | |
end | |
end | |
end | |
class InjectionStrategy < Dry::AutoInject::Strategies::Kwargs | |
def initialize(*args, **kwargs) | |
super(*args, :__container__, **kwargs) | |
end | |
def define_new | |
class_mod.class_exec(container, dependency_map) do |container, dependency_map| | |
define_method :new do |*args, **kwargs| | |
current_container = kwargs[:__container__] ||= container | |
deps = dependency_map.to_h.each_with_object({}) { |(name, identifier), obj| | |
obj[name] = kwargs[name] || current_container[identifier] | |
}.merge(kwargs) | |
super(*args, **deps) | |
end | |
end | |
end | |
end | |
Dry::AutoInject::Strategies.register :kwargs_with_container, InjectionStrategy | |
class Container | |
register(:db) { { users: %w(Jack John Jade Richard) } } | |
register_constant(:user_repo) { Repositories::UserRepo } | |
register_constant(:list_users) { Operations::ListUsers } | |
register_dynamic(:current_user) | |
end | |
Import = Dry::AutoInject(Container).kwargs_with_container | |
module Repositories | |
class UserRepo | |
include Import[:db] | |
def list(start_with) | |
db.fetch(:users).select { |u| u.start_with?(start_with) } | |
end | |
end | |
end | |
module Operations | |
class ListUsers | |
include Dry::Transaction | |
include Import[:user_repo, :current_user] | |
step :list_users | |
def list_users(start_with) | |
filtered = user_repo.list(start_with).map do |u| | |
{ name: u, current: current_user == u } | |
end | |
Success(filtered) | |
end | |
end | |
end | |
container_a = Container.provide(current_user: 'Jack') | |
puts container_a[:list_users].('J') | |
# Success([{:name=>"Jack", :current=>true}, {:name=>"John", :current=>false}, {:name=>"Jade", :current=>false}]) | |
container_b = Container.provide(current_user: 'Richard') | |
puts container_b[:list_users].('J') | |
# Success([{:name=>"Jack", :current=>false}, {:name=>"John", :current=>false}, {:name=>"Jade", :current=>false}]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment