Почему вы не можете объявить константы в методах с Ruby? - PullRequest
2 голосов
/ 05 августа 2010

Обратите внимание на следующее: StubFoo - заглушка Foo, которую я хотел бы использовать для некоторых тестов.

class Runner

  def run
      Foo = StubFoo
      foo = Foo.new
      # using Foo...
  end

end

Это генерирует следующее сообщение об ошибке: Dynamic constant assignment

Тем не менее, в RSpec я могу сделать следующее, что работает и совершенно законно:

it "should be an example" do
  Foo = StubFoo
  foo = Foo.new
  foo.to_s.should == "I am stubbed!"
end

Несколько вопросов по этому поводу.

  • Почему это работает с контрольным примером RSpec, но не с описанным выше методом?
  • Насколько мне известно, "это" - просто метод в RSpec, но я могу переопределить константу в "методе".

Я делаю это до того, как использую фальшивый фреймворк, просто для того, чтобы понять, как в Ruby отличаются насмешки, заглушки и т. Д. Я слышал, что динамические языки легче смоделировать / заглушки, и в Интернете есть руководства, в которых простое переназначение классов выполняется, как описано выше. Из моего исследования в Ruby невозможно объявить константы внутри метода, но я запутался, как упоминалось выше.

Редактировать

Правильно, это начинает иметь больше смысла. Я обновил run, чтобы теперь использовать const_set.

  def run
      old = Foo
      self.class.const_set(:Foo, StubFoo)
      foo = Foo.new
      puts foo.to_s
      self.class.const_set(:Foo, old)
      foo = Foo.new
      puts foo.to_s
  end

Это выдает предупреждение, однако, это то, что / как фреймворки работают тогда в Ruby? Очевидно, гораздо более элегантный и полный набор функций, но они просто подавляют это предупреждение?

Ответы [ 3 ]

9 голосов
/ 05 августа 2010

Вы не можете переназначить константы в определениях методов, используя Constant = value.Однако вы можете переназначить их, используя const_set.По сути, это должно препятствовать, но не запрещать переназначение динамических констант.

Относительно того, почему он работает с it: Да, it - это метод, но вы не определяете, вы вызываетеЭто.Переназначение происходит внутри блока, который вы передаете в качестве аргумента it.

Блоки наследуют тип контекста, в котором они созданы.Это означает, что внутри блока self.class.name будет именем вашего тестового класса.Поэтому, когда вы определяете константу внутри блока, переданного методу it, вы фактически определяете константу в своем тестовом классе.

2 голосов
/ 05 августа 2010

Потому что в своем тесте вы определяете не метод, а , вызывающий it.

Ruby обнаруживает, что вы определяете константу в методе, который может быть запущен несколько раз, отсюда и предупреждение. Если вы вызываете метод более одного раза, вы получите предупреждение warning: already initialized constant Foo. Это потому, что они должны быть постоянными. (Что произойдет, например, если вы определите Foo = Time.now?)

0 голосов
/ 05 августа 2010

Вы получаете сообщение об ошибке, потому что, если вызван Runner#run, Foo будет неожиданно изменен на вызывающей стороне Например:

class C; end

def add_two_to(x) # this method is defined externally, and you cannot change it.
  C = Class.new {} # error
  x + 2
end

x = C.new
puts add_two_to(5)
y = C.new

x.is_a? y.class # would be false if the code get here

Вызывающий add_two_to не ожидает переопределения C. Просто имеет больше смысла, что последняя строка приведенного выше кода всегда будет верной. Во втором примере ваш «метод» на самом деле не метод, а блок. Если вы запустите блок дважды (чего не сделает it), вы получите эту ошибку:

warning: already initialized constant Q
...