Ruby гарантирует, что локальная переменная не конфликтует с именем метода

Я пытаюсь похудеть в своих толстых моделях, применяя принцип единственной ответственности. Я выбрасываю много общего кода в миксины, которые затем включаются в несколько классов. Однако я столкнулся с этой странной проблемой, когда локальные переменные сталкиваются с именами методов (или attr_accessors) класса, который включает миксин. Например:

module MyAwesomeMixin
  def update_total
    my_total = self.pricing_items.reduce(0) {|sum, x| sum + x} # this borks if the "host class" has a method called my_total=
    total = my_total
  end
end

Приведенный выше фрагмент кода имеет совершенно непредвиденные последствия, если «хост-класс» имеет метод с именем my_total=. Как я могу убедиться, что переменная my_total полностью находится в области действия метода update_total и не имеет абсолютно никакого отношения к окружающей среде?


person Saurabh Nanda    schedule 27.12.2012    source источник
comment
Какие именно непредвиденные последствия случаются и как они проявляются?   -  person Sergio Tulentsev    schedule 27.12.2012
comment
Я не могу воспроизвести столкновение. Я определил класс MyClass, который включает MyAwesomeMixin и def my_total=(val), тогда MyClass.new.update_total не вызывает my_total. Можете ли вы дать фрагмент кода и выполнение, показывающее проблему?   -  person user1852994    schedule 27.12.2012
comment
Также total = my_total строка бесполезна. Он создает локальную переменную total, которая никогда не используется.   -  person Sergio Tulentsev    schedule 27.12.2012
comment
Совет от профессионала: предоставление небольшого работоспособного фрагмента кода, демонстрирующего проблему, ОЧЕНЬ поможет. Некоторые опытные пользователи даже не утруждают себя ответом, если не могут скопировать/вставить/запустить ваш код и сразу увидеть проблему. Более того, я обнаружил, что простая попытка составить такой фрагмент часто заставляет меня понять, в чем проблема (и, таким образом, устраняет необходимость спрашивать). Я не могу сосчитать, сколько раз это случалось со мной.   -  person Sergio Tulentsev    schedule 27.12.2012


Ответы (2)


Есть кое-что, что ты нам не показываешь. В этом случае единственным "непреднамеренным последствием" является то, что метод my_total= не вызывается, потому что создается локальная переменная my_total.

module MyAwesomeMixin
  def update_total
    my_total = 10 # !> assigned but unused variable - my_total
  end
end

class Foo
  include MyAwesomeMixin

  attr_accessor :my_total
end

f = Foo.new
f.my_total # => nil
f.update_total
f.my_total # => nil

Если вы собираетесь вызвать сеттер, вызовите его на self.

module MyAwesomeMixin
  def update_total
    self.my_total = 10
  end
end

f = Foo.new
f.my_total # => nil
f.update_total
f.my_total # => 10
person Sergio Tulentsev    schedule 27.12.2012
comment
Я в полном замешательстве, в таком случае. Как получается, что puts my_total ссылается на метод чтения, а my_total=something создает локальную переменную? - person Saurabh Nanda; 27.12.2012
comment
Если существует my_total локальная переменная, она будет использоваться. Если оно не существует, ruby ​​будет искать это имя во внешних областях видимости. - person Sergio Tulentsev; 27.12.2012

Всякий раз, когда возникает неоднозначность между интерпретациями локальной переменной и метода, она всегда будет интерпретироваться как локальная переменная.

Однако локальная переменная не будет доступна за пределами определения метода, поэтому вам не нужно беспокоиться о сбое имени в «хост-классе». Вам нужно только беспокоиться об этом в определении метода вашего update_total. И поскольку вы используете фиксированное количество локальных переменных в своем определении, вы должны знать, о каких из них следует позаботиться.

<сильный>1. Неоднозначность между локальной переменной и методом установки

Если у вас есть это:

variable_name =

то это всегда будет интерпретироваться как назначение локальной переменной. Если вы хотите сослаться на метод variable_name=, то вам нужно устранить неоднозначность его из локальной переменной, используя явный приемник и/или пару круглых скобок, например любую из следующих:

self.variable_name = foo
variable_name=(foo)
self.variable_name=(foo)

<сильный>2. Неоднозначность между локальной переменной и методом получения

Если у вас есть:

variable_name

и такой локальной переменной в области видимости нет, то она будет интерпретирована как вызов метода. Если в области видимости есть такая локальная переменная, то она будет интерпретироваться как локальная переменная. Если вы хотите вызвать метод при таких условиях, опять же, вам нужно устранить неоднозначность его из локальной переменной, используя явный приемник и/или пару круглых скобок, например любую из следующих:

self.variable_name
variable_name()
self.variable_name()
person sawa    schedule 27.12.2012