Рельсы не признают истину или ложь - PullRequest
2 голосов
/ 12 января 2012

У меня есть кое-что, что должно быть действительно простым, но это убивает меня.

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">

У меня есть

l.discount_percentage.blank?
=> false

Итак, у меня есть следующий метод:

  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

Но когда я сделаю это:

l.total_price

Вместо возврата 899 он возвращает 999 (это означает, что if discount_percentage.blank? Вообще не работал!)

Или синтаксис WHATEVER_HERE, если true / false работает только в View on Rails ??

Ответы [ 2 ]

6 голосов
/ 12 января 2012

Здесь кроется проблема:

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

Ruby «видит» переменные сверху вниз и слева направо, поэтому в этой строке он сначала видит локальную переменную (discount_amount =), поэтому он решает, что discount_amount вещь в discount_mount.blank? - это та же самая локальная переменная (а не метод экземпляра. Вы думаете, что переменная еще не определена, но Ruby уже заметил ее). Пока не имеет никакого значения, discount_amount установлено значение по умолчанию nil, поэтому nil.blank? успешно выполняется и присваивается discount_percentage = 0. То же самое для discount_percentage. Вот демонстрационный фрагмент:

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"

Шаг 1: не используйте одинаковые имена для локальных переменных и методов экземпляра. В любом случае это обычно плохая идея, потому что вы теряете, какой из них вы используете, но в этом случае это вас действительно укусило.

Шаг 2: Не пишите императивный код, когда выполняете математические вычисления! Действительно, математика (9X% от того, что вы делаете в типичном приложении, (10-X)% - это неизбежные побочные эффекты) хорошо работает с выражениями , а не с утверждениями. Я бы написал:

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
1 голос
/ 12 января 2012

При использовании средств записи атрибутов (например, foo = ...), вы должны использовать self явно. Это хорошо объяснено здесь .

Итак, ваш код должен быть таким:

 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

Это также объясняется в книге Programming Ruby :

Почему мы написали self.leftChannel в примере на странице 74? Что ж, есть скрытая ошибка с доступными для записи атрибутами. Как правило, методы внутри класса может вызывать другие методы в том же классе и его суперклассы в функциональной форме (то есть с неявным приемником self). Однако это не работает с авторами атрибутов. Руби видит назначение и решает, что имя слева должно быть местным переменная, а не вызов метода для записи атрибута.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...