RubyonRails——您能将“下一步”传递回调用当前函数的函数吗?

mnemlml8  于 2021-09-29  发布在  Java
关注(0)|答案(2)|浏览(303)

我有一系列嵌套的 each 循环遍历卡片列表。这些循环调用其他子函数,这些子函数测试是否满足某些条件才能继续。

def card_handler
    cards.each do |card|

      #some non-relevant code is here on my end

      already_sent?
    end
  end

  def already_sent?
    # allows for checking if different emails have been sent on the same card

    if list_action == 147
      a_s_helper(p1_label)
    elsif list_action == 146
      a_s_helper(p2_label)
    elsif list_action == 145
      a_s_helper(p3_label)
    end
  end

  def a_s_helper(label)

    if card::card_labels.include? label
    # if the card already has the label, I want to log the error and return all the way to the next card in the iteration

    puts '\n Order info: \n id: #{id} \n Email already sent'

    next 
    # doesn't work

    else
      real_id?
    end
  end

就像我在a_s_helper中的评论中所说的,如果卡已经有了标签,我希望记录错误并返回迭代中的下一张卡。我从当前设置中得到一个“无效的下一个”错误。
有没有退票的办法 next 返回到父函数或循环?

vbkedwbf

vbkedwbf1#

next 仅在循环的直接上下文中有效。一旦您调用了一个方法,您就不再直接处于该循环上下文中。您不能使用next来像这样短路外回路。
您有两个选择:
从 predicate 函数返回状态(这是您应该从 predicate 执行的操作!)并在此基础上短路回路,或
使用ruby的 catch...throw 构造(它不是它的raise/rescue异常处理程序,而是类似于块范围的goto语句)
选项1:返回状态。依我看,这是最合适的方法。 predicate 方法(以?结尾的方法)通常应该返回布尔值并且是幂等的(也就是说,应该没有副作用,比如记录语句)。他们习惯于问是/否的问题。理想情况下,根据这个问题决定做什么应该超出他们的范围。

def card_handler
  cards.each do |card|
    #some non-relevant code is here on my end
    if already_sent?
      puts '\n Order info: \n id: #{id} \n Email already sent'
      next
    end
  end
end

def already_sent?
  case list_action
  when 145
    a_s_helper(p3_label)
  when 145
    a_s_helper(p2_label)
  when 147
    a_s_helper(p1_label)
  end
end

def a_s_helper(label)
  card::card_labels.include? label
end

这会导致助手向循环返回true或false值,循环可以决定记录消息并转到下一次迭代。
备选案文2: catch...throw ```
def card_handler
cards.each do |card|
# Put all your code that should nomally run inside the catch block. If
# the message :email_sent is thrown, then Ruby will zip up the stack and
# resume execution at the end of the block. This will skip any unexecuted
# code in the block, essentially terminating the execution.
catch :email_sent do
already_sent?
end
end
end

def already_sent?

...

end

def a_s_helper(label)

...

throw :email_sent if card::card_labels.include? label

...

end

您可能会倾向于使用选项2,因为它对方法构造的控制不太仔细,但它与异常非常接近,因为流控制被广泛认为是一种反模式(它本质上是一种稍微花哨的goto,因为它使代码难以读取和调试而臭名昭著)。如果您可以简单地从助手返回一个状态,并根据该状态决定是否继续循环,那么您应该这样做。
vbopmzt1

vbopmzt12#

我想向未来看到这个问题的人展示一下,我是如何实现从@chris heald那里得到的解决方案的。我把它做得更紧凑了一点。这就是我最终使用的代码:

def card_handler
    cards.each do |card|
      real_id?
      puts "real_id? : #{real_id?}"
      next if !(real_id?)

      needs_email?
      puts "needs_email? : #{needs_email?}"
      next if !(needs_email?)

      get_email_info
    end
  end

  def needs_email?
    case list_action
    when 147
      !(card::card_labels.include? p1_label::id)
    when 146
      !(card::card_labels.include? p2_label::id)
    when 145
      !(card::card_labels.include? p3_label::id)
    else
      false
    end
  end

  def real_id?
    id != 0 ? true : false
  end

  def get_email_info
    #more stuff
  end

相关问题