3

我有一个类似于 DSL 的 Ruby 代码,如下所示:

Activity.new('20-06-2012') do
  Eat.log do |l|
    l.duration = 0.30
    l.priority = 5
  end
  Work.log do |l|
    l.duration = 2
    l.priority = 3
  end
end

每次调用 log() 方法时,都会实例化一个 Log 对象(在幕后),并将块传递给该方法(该块传递给 Log 对象的构造函数)。我的问题是,有没有办法从 log() 方法收集所有返回?在上面的示例中,最外层块的返回值是对 log() 的最后一次调用。但我想在一个数组中获取所有调用的结果,而不仅仅是最后一个。

谢谢!

4

6 回答 6

3

您的内部 DSL 仍然太像 Ruby,您可以将其重构为如下所示:

activity '20-06-2012' do
  log :eat do
    duration 0.30
    priority 5
  end
  log :work do
    duration 2
    priority 3
  end
end

现在捕获你的调用instance_eval并将值累积在一个内部数组中,通常是 DSL 的东西。

于 2012-06-20T17:26:18.877 回答
1

块的最后一条语句始终是返回值。如果你不想强迫你的用户写成l块中的最后一条语句,你应该使用对l实例所做的修改(不管它是什么),而不是块的返回值。另一种选择是更改l对象的 setter-behavior 以在分配时返回所有值的数组 - 但我会选择第一个选项,具体取决于您的代码,它可能如下所示:

class Log
  attr_accessor :duration, :priority
  def initialize(block)
    block.call(self)
  end
end

Log.new proc{ |l| l.duration = 0.3; l.priority = 5 }
# => #<Log:0x000000029d8030 @duration=0.3, @priority=5>
于 2012-06-20T17:16:45.210 回答
1

创建一个新对象或其他任何对象,并将其从初始化程序ActivityLog传递给块。Activity

Activity.new('20-06-2012') do |log|
  log.eat do |l|
    l.duration = 0.30
    l.priority = 5
  end
  log.work do |l|
    l.duration = 2
    l.priority = 3
  end
end

如果您只有少数几种日志类型,只需将它们添加到ActivityLog( ActivityLog#eat,ActivityLog#work等)。初始化相应的对象(Eat, Work)并使用接收到的块调用其 log 方法。

作为下一步,您可以将它们添加到数组中并在其中迭代ActivityLog#initialize并动态创建每个方法。

懒惰的方法是ActivityLog让 amethod_missing接受任何方法,将其转换为类常量,然后执行上述步骤。这很糟糕,因为它通常会使调试变得更加困难,如果您在对象上调用与记录器不对应的方法,您可能会得到误导性异常,并且您不能methods针对该对象调用并查看列出的方法。

希望有帮助。

于 2012-06-20T17:31:31.963 回答
0

使用return,用逗号分隔每个块。

Activity.new('20-06-2012') do
  return_value = Eat.log do |l|
    l.duration = 0.30
    l.priority = 5
  end, Work.log do |l|
    l.duration = 2
    l.priority = 3
  end
return return_value #redundant
end

当您返回用逗号分隔的多个值时,Ruby 会将它们连接到一个数组中。

于 2012-06-20T17:13:29.737 回答
0

André 的方法会起作用,但它的可读性不是很强。如果你在一个月左右的时间里回到它,很难发现隐藏在那里的逗号。

就我个人而言,我会让数组集合更加明确:

Activity.new('20-06-2012') do
  logs = []
  logs << Eat.log do |l|
    l.duration = 0.30
    l.priority = 5
  end
  logs << Work.log do |l|
    l.duration = 2
    l.priority = 3
  end
  logs
end

这样,很难查看代码并误读它的作用

于 2012-06-20T17:16:15.093 回答
0

如果(且仅当)您的日志结构简单且一致,则可以使用以下方法:

Activity.new('20-06-2012') do
  logs = {
    Eat => {:duration => 0.30, :priority => 5},
    Work => {:duration => 2, :priority => 3}
  }

  logs.map do |log_type, log_data|
    log_type.log do |l|
      l.duration = log_data[:duration]
      l.priority = log_data[:priority]
    end
  end
end

您需要自行决定这是否适合您的结构以及是否足够可读

于 2012-06-20T17:22:30.567 回答