Skip to content

Instantly share code, notes, and snippets.

@sheldonh
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sheldonh/175895d559a4d0b8ab04 to your computer and use it in GitHub Desktop.
Save sheldonh/175895d559a4d0b8ab04 to your computer and use it in GitHub Desktop.
Ruby factory prototype
module Starjuice
module Factory
module Constructor
class Adaptor
def initialize(adaptor = nil)
@adaptor = adaptor || ->(properties) { [properties] }
end
def call(provider_class, properties = nil)
if properties.nil?
provider_class.new
else
provider_class.new(*adapt(properties))
end
end
def adapt(properties)
@adaptor.call(properties)
end
end
class PropertyBased < Adaptor
end
class Positional < Adaptor
def initialize(*keywords)
@keywords = keywords
end
def adapt(properties)
@keywords.inject([]) { |m, k| m << properties[k] }
end
end
end
class ProviderRegistration
attr_reader :interface, :provider_id, :provider_class, :provider_constructor
def initialize(interface, provider_id, provider_class, provider_constructor)
@interface, @provider_id, @provider_class, @provider_constructor = interface, provider_id, provider_class, provider_constructor
end
end
class ProviderRegistry
def initialize
@registrations = []
end
def register(provider_registration)
if existing = find(provider_registration.interface, provider_registration.provider_id)
$stderr.puts "WARNING: #{provider_registration.provider_class} replacing #{existing.provider_class} as #{provider_registration.provider_id} #{provider_registration.interface} provider"
end
@registrations.unshift provider_registration
end
def find(interface, provider_id)
@registrations.detect { |registration| registration.interface.eql?(interface) and registration.provider_id.eql?(provider_id) }
end
end
module InterfaceFactory
class Base
attr_reader :interface
def initialize(interface, options)
@interface = interface
@interface_constructor = options[:constructor] || ->(interface, provider) { interface.new(provider) }
@registry = ProviderRegistry.new
end
def register_provider(provider_id, provider_class, options = {})
provider_constructor = options[:constructor] || Constructor::PropertyBased.new
registration = ProviderRegistration.new(@interface, provider_id, provider_class, provider_constructor)
@registry.register(registration)
end
def create(provider_id, properties = nil)
registration = @registry.find(@interface, provider_id)
provider = registration.provider_constructor.call(registration.provider_class, properties)
@interface_constructor.call(@interface, provider)
end
end
def creates_interface(interface, options = {})
@interface_factory = Base.new(interface, options)
end
def create(provider_id, properties = nil)
@interface_factory.create(provider_id, properties)
end
def register_provider(provider_id, provider_class, options = {})
@interface_factory.register_provider(provider_id, provider_class, options)
end
end
end
end
class Cow
def initialize(provider)
@provider = provider
end
def describe
@provider.describe
end
end
class CowFactory
extend Starjuice::Factory::InterfaceFactory
creates_interface Cow
end
class Friesland
def initialize(colors)
@background_color = colors[:background]
@foreground_color = colors[:foreground]
end
def describe
"A large, #{@foreground_color}-and-#{@background_color}, milk-producing cow"
end
end
CowFactory.register_provider :friesland, Friesland
class Jersey
def initialize(color = "brown")
@color = color
end
def describe
"A small, #{@color}, butter-producing cow"
end
end
CowFactory.register_provider :jersey, Jersey, constructor: Starjuice::Factory::Constructor::Positional.new(:color)
class Angus
def initialize(color)
@color = color
end
def describe
"A large, #{@color}, beef-producing cow with no horns"
end
end
CowFactory.register_provider :angus, Angus, constructor: ->(c, props) { c.new(props[:color]) }
CowFactory.register_provider :martian, Angus, constructor: Starjuice::Factory::Constructor::Positional.new(:color)
class Invisible
def describe
"An invisible cow (presumably)"
end
end
CowFactory.register_provider :invisible, Invisible
cows = {
"Abbie" => CowFactory.create(:friesland, foreground: "black", background: "white"),
"Bessy" => CowFactory.create(:jersey, color: "pink"),
"Curly" => CowFactory.create(:jersey),
"Daisy" => CowFactory.create(:angus, color: "brown"),
"Sneak" => CowFactory.create(:invisible),
"Xqtuk" => CowFactory.create(:martian, color: "green"),
}
cows.each { |name, cow| puts "#{name}: #{cow.describe}" }
puts "And now, a little warning..."
CowFactory.register_provider :martian, Friesland
cow = CowFactory.create(:martian, foreground: "spectral", background: "weird")
puts "Xqtuk has become: #{cow.describe}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment