Изменение контекста/привязки внутри блока в рубине
У меня есть DSL в Ruby, который работает так:
desc 'list all todos'
command :list do |c|
c.desc 'show todos in long form'
c.switch :l
c.action do |global,option,args|
# some code that not relevant to this question
end
end
desc 'make a new todo'
command :new do |c|
# etc.
end
Один разработчик предложил мне увеличить мой DSL, чтобы не требовать передачи c
в блок command
, и, следовательно, для этого не требуется c.
для всех
методы внутри; предположительно, он подразумевал, что я мог бы сделать следующий код одинаковым:
desc 'list all todos'
command :list do
desc 'show todos in long form'
switch :l
action do |global,option,args|
# some code that not relevant to this question
end
end
desc 'make a new todo'
command :new do
# etc.
end
Код command
выглядит примерно так:
def command(*names)
command = make_command_object(..)
yield command
end
Я попробовал несколько вещей и не смог заставить его работать; Я не мог понять, как изменить контекст/привязку кода внутри блока command
, чтобы он отличался от значения по умолчанию.
Любые идеи о том, возможно ли это и как я могу это сделать?
Ответы
Ответ 1
Вставьте этот код:
def evaluate(&block)
@self_before_instance_eval = eval "self", block.binding
instance_eval &block
end
def method_missing(method, *args, &block)
@self_before_instance_eval.send method, *args, &block
end
Для получения дополнительной информации обратитесь к этой действительно хорошей статье здесь
Ответ 2
Может
def command(*names, &blk)
command = make_command_object(..)
command.instance_eval(&blk)
end
может оценивать блок в контексте объекта команды.
Ответ 3
class CommandDSL
def self.call(&blk)
# Create a new CommandDSL instance, and instance_eval the block to it
instance = new
instance.instance_eval(&blk)
# Now return all of the set instance variables as a Hash
instance.instance_variables.inject({}) { |result_hash, instance_variable|
result_hash[instance_variable] = instance.instance_variable_get(instance_variable)
result_hash # Gotta have the block return the result_hash
}
end
def desc(str); @desc = str; end
def switch(sym); @switch = sym; end
def action(&blk); @action = blk; end
end
def command(name, &blk)
values_set_within_dsl = CommandDSL.call(&blk)
# INSERT CODE HERE
p name
p values_set_within_dsl
end
command :list do
desc 'show todos in long form'
switch :l
action do |global,option,args|
# some code that not relevant to this question
end
end
Будет напечатан:
:list
{:@desc=>"show todos in long form", :@switch=>:l, :@action=>#<Proc:[email protected]:/Users/Ryguy/Desktop/tesdt.rb:38>}
Ответ 4
Я написал класс, который обрабатывает эту точную проблему, и занимается такими вещами, как доступ в @instance_variable, вложенность и т.д. Здесь запись из другого вопроса:
Блокировать вызов в Ruby on Rails