【问题标题】:Call all methods in block with additional parameter使用附加参数调用块中的所有方法
【发布时间】:2023-03-06 17:55:01
【问题描述】:

我有以下 Ruby 类:

class Sandwich
  class << self
    def prepare_with(special_ingredient, &block)
      # Some very very special magic is done here to
      # call instead just .fry! as .fry!({:ingredient=>special_ingredient})
      # and for any other method the same
    end

    def fry!(opts= {})
    end

    def add_mayo(opts = {})
    end
  end
end

class Hamburger < Sandwich
end

=> Hamburger.prepare_with(bacon) do
=>   Hamburger.fry!
=>   Hamburger.add_mayo
=> end

我想修改调用Hamburger类的所有方法,并在最后一个参数Hash中添加额外的key=&gt;value

应该在 Sandwich.prepare_with 中执行一些特殊的魔法来调用 Sandwich(及其所有后代)的所有方法,例如 call 而不是将 .fry! 调用为 .fry!({:ingredient=&gt;special_ingredient})

已编辑:理想情况下我们需要过滤内部块代码的调用的附加点,例如以下代码会引发任何 prepare_with 代码的异常,它不会过滤它使用附加参数调用的方法:

=> Hamburger.prepare_with(bacon) do
=>   Hamburger.fry!
=>   h = Hash.new("Go fish") 
=>   Hamburger.add_mayo
=> end

【问题讨论】:

  • 编辑了我的答案,如果这是您要找的,请告诉我
  • 你的意思是 Hash.new 应该抛出错误吗?
  • 我不知道我是否真的理解你的编辑,但我认为这是第二个问题,这个问题解决起来并不容易。如果您尝试在这里构建 DSL,您可能应该坚持使用外部 DSL。例如,您可以通过构建自己的解析器或使用 treetop 或 racc 来实现这一点。然后,如果出现未知语句,您可以引发异常。

标签: ruby lambda metaprogramming


【解决方案1】:

为什么fry!add_mayo 不是实例方法?

编辑:按照问题海报的要求,没有实例方法:

class Sandwich
  class << self
    def prepare_with(special_ingredient, &block)
      @@default_opts = {:special_ingredient => special_ingredient}
      block.call
    end

    def fry!(opts={})
      opts = opts.merge(@@default_opts)
      puts "fried with #{opts[:special_ingredient]}"
    end

    def add_mayo(opts = {})
      puts "added mayo"
    end
  end
end

class Hamburger < Sandwich
end

Hamburger.prepare_with(:bacon) do 
  Hamburger.fry!
  Hamburger.add_mayo
end

Hamburger.prepare_with(:tofu) do 
  Hamburger.fry!
end

输出:

fried with bacon
added mayo
fried with tofu

【讨论】:

  • 根据问题定义它们不能是实例方法)
  • 好吧,你必须记住某处的特殊成分。在类 var 中记住它们似乎很奇怪,但请稍等,我会更新我的答案以反映这一点。
  • 请看我编辑的笔记。谢谢。
【解决方案2】:

简答

block.call :ingredient => special_ingredient

长答案

我认为你应该处理对象。 add_mayo 和fry 应该是实例方法而不是类方法。

我会看到类似的东西

class Sandwich
  class << self
    def prepare &block
      sandwich = self.new
      block.call sandwich
      sandwich
    end
  end

  def fry(opts = {})
    #stuff
  end

  def add_mayo(opts = {})
  end
end

class Hamburger < Sandwich; end

hamburger = Hamburger.prepare do |h|
  h.fry :ingredient => :bacon
  h.add_mayo
end

【讨论】:

  • @padde 我必须开始等待更长的时间,我的答案总是在底部。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-25
  • 1970-01-01
相关资源
最近更新 更多