Rails not recognizing true or false

此生再无相见时 提交于 2019-12-11 03:49:33

问题


I have something that should be really simple, but it's killing me.

l = LineItem.first
#<LineItem id: 5, product_id: 1, quantity: 1, price: #<BigDecimal:7f7fdb51a3f8,'0.999E3',9(18)>, cart_id: 5, discount_percentage: 10, discount_amount: nil, discount_active: true, created_at: "2012-01-12 16:17:41", updated_at: "2012-01-12 16:17:41">

I have

l.discount_percentage.blank?
=> false

So, I have the following method:

  def total_price
    discount_amount = 0 if discount_amount.blank?
    discount_percentage = 0 if discount_percentage.blank?

    discounted_amount_from_percent = price*(discount_percentage.to_f/100)

    applicable_discount = [discount_amount,discounted_amount_from_percent].max

    return (price-applicable_discount)
  end

But when I do this:

l.total_price

Instead of returning 899, it returns 999 (meaning that the if discount_percentage.blank? didn't work at all!)

Or the syntax WHATEVER_HERE if true/false only work in the View on Rails??


回答1:


Here lays the problem:

discount_amount = 0 if discount_amount.blank?
discount_percentage = 0 if discount_percentage.blank?

Ruby "sees" variables from top to bottom and from left to right, so in that line he first sees a local variable (discount_amount =) so he decides this discount_amount thing in discount_mount.blank? is that same local variable (and not the instance method. You think the variable is not defined yet, but Ruby has already spotted it). Not having any value yet, discount_amountit set to default value nil, so nil.blank? succeeds and the assignment discount_percentage = 0 is made. Ditto for discount_percentage. Here's a demo snippet:

class ExampleClass 
  def run
    x = "it works as expected" if x == "x"
    x
  end

  def run2
    if x == "x"
      x = "it works as expected" 
    end
    x
  end

  def run3
    xy = "it works as expected" if x == "x"
    xy
  end

  def x; "x"; end
end

p ExampleClass.new.run #=> nil
p ExampleClass.new.run2 #=> "it works as expected"
p ExampleClass.new.run3 #=> "it works as expected"

Step 1: don't use the same names for local variables and instance methods. That's usually a bad idea anyway because you lose track of which one you are using, but in this case it has really bitten you.

Step 2: Do not write imperative code when you're doing math calculations! Really, maths (9X % of the things you do in a typical application, (10-X)% being unavoidable side-effects) play well with expressions, not with statements. I'd write:

def total_price
  final_discount_amount = discount_amount || 0
  final_discount_percentage = discount_percentage || 0
  discounted_amount_from_percent = price * (final_discount_percentage.to_f/100)
  applicable_discount = [final_discount_amount, discounted_amount_from_percent].max
  price - applicable_discount
end



回答2:


When using attribute writers (e.g foo = ...), you should use self explicitly. This is nicely explained here.

So your code should be like this:

 def total_price
    self.discount_amount = 0 if discount_amount.blank?
    self.discount_percentage = 0 if discount_percentage.blank?

    # local var, self not necessary
    discounted_amount_from_percent = price*(discount_percentage.to_f/100)

    # local var, self not necessary
    applicable_discount = [discount_amount,discounted_amount_from_percent].max

    return (price-applicable_discount)
  end

This is also explained in the Programming Ruby book:

Why did we write self.leftChannel in the example on page 74? Well, there's a hidden gotcha with writable attributes. Normally, methods within a class can invoke other methods in the same class and its superclasses in functional form (that is, with an implicit receiver of self). However, this doesn't work with attribute writers. Ruby sees the assignment and decides that the name on the left must be a local variable, not a method call to an attribute writer.



来源:https://stackoverflow.com/questions/8838700/rails-not-recognizing-true-or-false

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!