Ошибка верхнего уровня при доступе к переменной класса Ruby в class_eval - PullRequest
0 голосов
/ 02 октября 2018

Я работаю над старым кодом Rails 3, кодовая база Ruby 1.9.3.Я ссылаюсь на драгоценный камень, который не могу обновить по разным причинам.Драгоценный камень уже был исправлен обезьяной (правильный термин?), Используя class_eval.Мне нужно внести изменения в другой метод, который выглядит примерно так:

SomeNamespace::Bar do
  def some_method
    @@part_of_header ||= JSON.dump({... Stuff ...})

    # ... other code ...

    headers = {
      "Header Part" => @@part_of_header
      #... other headers ...
    }
  end
end

Идея, стоящая за переменной класса @@ part_of_header, заключается в кэшировании дампа JSON, чтобы его можно было использовать повторно.@@ part_of_header НЕ определен где-либо еще в базовом классе Bar.

Мой пропатченный метод обезьяны выглядит следующим образом:

SomeNamespace::Bar.class_eval do
  def some_method
    @@part_of_header ||= JSON.dump({... Stuff ...})

    # ... other code that I changed ...

    headers = {
      "Header Part" => @@part_of_header
      #... other headers that I changed ...
    }
  end
end

Код работает нормально, но я получаю следующее предупреждение в строках спеременная класса @@ part_of_header:

Class variable access from toplevel

Я попытался переместить переменную класса в ее собственный метод:

SomeNamespace::Bar.class_eval do
  def header_part
    @@part_of_header ||= JSON.dump({... Stuff ...})
  end

  def some_method
    # ... other code that I changed ...

    headers = {
      "Header Part" => header_part
      #... other headers that I changed ...
    }
  end
end

Однако ошибка «верхнего уровня» только что перемещена в метод header_part.

Я также пытался получить доступ к переменной класса с помощью class_variable_set и class_variable_get, но получил неопределенные ошибки метода.

Есть ли какие-либо советы о том, как исправить это предупреждение?Если это не может быть исправлено, какой-либо совет по кэшированию дампа JSON в class_eval?Спасибо.

Обновление: Спасибо @Josh за использование полного имени класса с class_variable_get / set.Мое окончательное решение выглядит следующим образом:

SomeNamespace::Bar.class_eval do
  def header_part
    # Create the class variable if it does not exist, remember
    # the base class does not define @@part_of_header.
    if !SomeNamesapce::Bar.class_variable_defined?(:@@part_of_header)
      SomeNamespace::Bar.class_variable_set(:@@part_of_header, nil)
    end

     if (SomeNamespace::Bar.class_variable_get(:@@part_of_header).nil?
       header_part = JSON.dump({... Stuff ...})
       SomeNamespace::Bar.class_variable_set(:@@part_of_header, header_part)
     end

     SomeNamespace::Bar.class_variable_get(:@@part_of_header)
  end

  def some_method
    # ... other code that I changed ...

    headers = {
      "Header Part" => header_part
      #... other headers that I changed ...
    }
  end
end

Вышеуказанное работает, но любые отзывы о вышеупомянутом решении приветствуются.Спасибо.

1 Ответ

0 голосов
/ 02 октября 2018

Похоже, проблема в том, что, хотя блок class_eval do выполняется в контексте SomeNamespace::Bar, этот контекст не применяется к ссылкам на переменные класса.

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

# NOTE: Omitting conditional set (||=) for simplicity
SomeNamespace::Bar::class_variable_set(:@@part_of_header, JSON.dump({... Stuff ...}))

headers = {
  "Header Part" => SomeNamespace::Bar::class_variable_get(:@@part_of_header)
  #... other headers that I changed ...
}

Если @@part_of_header действительно используется только в some_method, и если вы полностью заменяете some_method, тогда нет ничего плохого в том, что вы используете собственную переменную модуля / переменную класса вместо повторного использования существующего SomeNamespace::Bar::@@part_of_header.Я мог бы предпочесть такой подход;Такое ощущение, что он лучше инкапсулирует ваши изменения.

module MonkeyPatch
  SomeNamespace::Bar.class_eval do
    def some_method
      # This is within the MonkeyPatch module, so it
      # makes a new class variable for MonkeyPatch
      @@part_of_header ||= "JSON.dump({'a': 12})"

      # ... other code that I changed ...

      headers = {
        "Header Part" => @@part_of_header
        #... other headers that I changed ...
      }
      puts headers
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...