Last active
February 16, 2021 12:55
-
-
Save okuramasafumi/4ff586c5ee45d907f02984418d3c91ec to your computer and use it in GitHub Desktop.
MyDSL
This file contains hidden or 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
# ライブラリコード | |
module MyDSL | |
class ClassMacro < Module | |
class StoredItem | |
attr_reader :name | |
attr_accessor :context | |
def initialize(name, block_args: [], &block) | |
@name = name | |
@block_args = block_args | |
@block = block | |
@context = self | |
end | |
def value | |
if @block | |
instance_exec(@context, &@block) | |
else | |
@context.public_send(@name) | |
end | |
end | |
end | |
class Store | |
include Enumerable | |
attr_writer :context | |
def initialize(items = [], context:) | |
@items = items | |
@context = context | |
end | |
def <<(item) | |
@items << item | |
end | |
def each(&block) | |
@items.each do |item| | |
item.context = @context if item.context == item | |
block.call(item) | |
end | |
end | |
end | |
def initialize(&block) | |
@block = block | |
end | |
def included(base) | |
base.instance_eval do | |
def define_store_dsl(name, context: nil) | |
context = context.nil? ? self : context | |
instance_variable_set("@#{name}", Store.new(context: context)) unless instance_variable_defined?("@#{name}") | |
define_singleton_method(name) do |*args| | |
store_var = instance_variable_get("@#{name}") | |
args.each do |arg| | |
store_var << StoredItem.new(arg.to_sym) | |
end | |
end | |
define_method(name) do | |
store = self.class.instance_variable_get("@#{name}") | |
store.context = context.respond_to?(:call) ? context.call(self) : context | |
store | |
end | |
end | |
def define_store_dsl_with_block(hash) | |
method_name = hash.keys.first | |
store_target = hash.values.first | |
define_singleton_method(method_name) do |name, &block| | |
store_var = instance_variable_get("@#{store_target}") | |
store_var << StoredItem.new(name, &block) | |
end | |
end | |
def define_instance_method(name, &block) | |
define_method(name, &block) | |
end | |
end | |
# Evaluate DSL that users wrote | |
base.instance_exec(&@block) | |
end | |
end | |
end | |
# デベロッパコード | |
Resource = MyDSL::ClassMacro.new do | |
define_store_dsl :attributes, context: proc {|resource| resource.user } | |
define_store_dsl_with_block attribute: :attributes | |
define_instance_method :to_hash do | |
attributes.each_with_object({}) do |attribute, hash| | |
hash[attribute.name] = attribute.value | |
end | |
end | |
end | |
# エンドユーザーコード | |
class User | |
attr_accessor :id, :name, :email | |
def initialize(id, name, email) | |
@id = id | |
@name = name | |
@email = email | |
end | |
end | |
class UserResource | |
include Resource | |
attr_reader :user | |
def initialize(user) | |
@user = user | |
end | |
attributes :id, :name | |
attribute :name_with_email do |user| | |
"#{user.name}: #{user.email}" | |
end | |
end | |
user = User.new(1, 'John', '[email protected]') | |
p UserResource.new(user).to_hash # => {'id' => 1, 'name' => 'John', 'name_with_email' => 'John: [email protected]'} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment