Rails模型方法中的无限循环

时间:2014-08-29 15:01:55

标签: ruby-on-rails recursion

用例

如果用户未将产品装满信用额度(默认为6),则会在方框模型上调用一个方法来填充它们。

代码指南

框中的信用数由box_credits给出,它循环显示框中的所有产品并返回它们的总值。这似乎有效。

布尔方法box_filled?检查box_credits方法是否等于或大于可用信用数(订阅信用额)。

fill_once方法应该在框中添加产品,直到填充框(box_filled?返回true)。当box_credits等于可用的学分数时,就会发生这种情况。

守则

def fill_once
  unless self.box_filled?
    # Get a random product from the user's recommendations
    product = self.subscription.user.recommended_product_records[rand(self.subscription.user.recommended_product_records.length - 1)]
    # Make sure the product hasn't already been included in the box
    unless self.added_product_ids.include? product.id
      # If fresh, add the product to the box, size-dependently
      unless product.sample_price_credits.nil?
        product.add_to_box_credits(self.subscription, "sample")
      else
        unless product.full_price_credits.nil?
          product.add_to_box_credits(self.subscription, "full")
        end
      end
      self.save!
    end
    self.fill_once # Here's the recursion
  end
end

box_filled?方法如下所示:

def box_filled?
  subscription = self.subscription
  if self.box_credits >= subscription.credits
    return true
  else
    return false
  end
end

box_credits由此方法确定:

def box_credits
  count = 0
  unless self.added_product_hashes.nil?
    # Takes product hashes in the form {id, size, method}
    self.added_product_hashes.each do |product_hash|
      # Add credits to the count accordingly
      if product_hash["method"] == "credits"

        # Depending on the product size, add the corresponding amount of credits
        if product_hash["size"] == "sample"
          # Get the credit cost for a product sample
          cost = Product.find(product_hash["id"].to_i).sample_price_credits
          count += cost
        elsif product_hash["size"] == "full"
          # Get the credit cost for a full product
          cost = Product.find(product_hash["id"].to_i).full_price_credits
          count += cost
        else
          next
        end

      else
        next
      end
    end
  end

  return count
end

问题

fill_once永远运行:它似乎忽略了unless self.box_filled?条件。

尝试解决方案

我尝试从fill_once方法移除对fill_once的递归调用,并将其拆分为until循环(until box_filled? ... fill_once ...),但没有快乐。

更新

也正在添加多个相同的产品。我认为问题是更新的记录没有被操作 - 只有原始实例。例如。 unless self.added_product_ids.include? product.id检查原始的盒子实例,而不是更新的记录,在added_product_ids中看不到任何产品,并查找它找到的每个产品。

解决方案

好的,这已经解决了。怀疑,更新的记录没有传递给迭代器。这就是我解决它的方法:

# Add one random user recommended product to the box
def fill_once(box=self)
  unless box.box_filled?
    # Get a random product from the user's recommendations
    product = box.subscription.user.recommended_product_records[rand(box.subscription.user.recommended_product_records.length - 1)]
    # Make sure the product hasn't already been included in the box
    unless box.added_product_ids.include? product.id
      # If fresh, add the product to the box, size-dependently
      unless product.sample_price_credits.nil?
        box = product.add_to_box_credits(box.subscription, "sample")
      else
        unless product.full_price_credits.nil?
        box = product.add_to_box_credits(box.subscription, "full")
        end
      end
    end
    fill_once(box)
  end
end

使用默认值为self的Ruby默认参数,但是使用更新记录的选项允许我根据需要多次传递记录。

2 个答案:

答案 0 :(得分:0)

unless self.added_product_ids.include? product.id表示不会将重复的产品添加到框中。因此,如果所有产品建议添加到框但总信用额小于box_credits,则可能导致无限循环。我不确定,但可能是原因。

你可以添加

puts "Box credits #{self.box_credits} vs. credits: #{self.subscription.credits} "

self.fill_once # Here's the recursion

看看是否会发生这种情况。

答案 1 :(得分:0)

<强>解决方案

好的,这已经解决了。怀疑,更新后的记录没有传递给迭代器。以下是我如何解决它:

# Add one random user recommended product to the box
def fill_once(box=self)
  unless box.box_filled?
    # Get a random product from the user's recommendations
    product = box.subscription.user.recommended_product_records[rand(box.subscription.user.recommended_product_records.length - 1)]
    # Make sure the product hasn't already been included in the box
    unless box.added_product_ids.include? product.id
      # If fresh, add the product to the box, size-dependently
      unless product.sample_price_credits.nil?
        box = product.add_to_box_credits(box.subscription, "sample")
      else
        unless product.full_price_credits.nil?
        box = product.add_to_box_credits(box.subscription, "full")
        end
      end
    end
    fill_once(box)
  end
end

使用默认值为self的Ruby默认参数,但使用更新记录的选项允许我根据需要多次传递记录。