how to write domain-specific language in ruby
example:
Smokestack.define do
factory User do
name "Gabe BW"
pet_name "Toto"
end
end
user = Smokestack.build(User)
puts user.name == 'Gabe BW' # true
puts user.pet_name == 'Toto' # true
other_user = Smokestack.build(User, name: "Bob")
puts other_user.name == 'Bob' # true
puts other_user.pet_name == 'Toto' # true
how it works?
module Smokestack
@registry = {}
def self.registry
@registry
end
def self.define(&block)
definition_proxy = DefinitionProxy.new
definition_proxy.instance_eval(&block)
end
def self.build(factory_class, overrides = {})
instance = factory_class.new
factory = registry[factory_class]
attributes = factory.attributes.merge(overrides)
attributes.each do |attribute_name, value|
instance.send("#{attribute_name}=", value)
end
instance
end
end
class DefinitionProxy
def factory(factory_class, &block)
factory = Factory.new
factory.instance_eval(&block)
Smokestack.registry[factory_class] = factory
end
end
class Factory < BasicObject
def initialize
@attributes = {}
end
attr_reader :attributes
def method_missing(name, *args, &block)
@attributes[name] = args[0]
end
end
三個主要的物件
module Smokestack => 記錄所有的, 用@registry 來記錄目前所有的定義(definition_proxy), build就是實際產生一個需要的instance
class DefinitionProxy => 定義 factory方法, 用instance_eval打開每一個factory, 再傳入需要做的block, 之後透過smokestack來registry factory
class Factory < BasicObject => 實際的每一個factory要做什麼, 用method_missing來實現
沒有留言:
張貼留言