Skip to content

Instantly share code, notes, and snippets.

@flash-gordon
Created December 28, 2017 18:40
Show Gist options
  • Save flash-gordon/9a01e1e67c9edab77338ab2e4c68523c to your computer and use it in GitHub Desktop.
Save flash-gordon/9a01e1e67c9edab77338ab2e4c68523c to your computer and use it in GitHub Desktop.
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