【问题标题】:Toplevel error accessing Ruby class variable in class_eval访问 class_eval 中的 Ruby 类变量的顶级错误
【发布时间】:2018-10-02 16:03:06
【问题描述】:

我正在编写旧代码 Rails 3、Ruby 1.9.3 代码库。我引用了一个由于各种原因无法升级的 gem。 gem 已经使用 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 访问类变量,但出现未定义的方法错误。

关于如何解决此警告的任何建议?如果无法修复关于在 class_eval 中缓存 JSON 转储的任何建议?谢谢。

更新: 感谢 @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

上述工作,但对上述解决方案的任何反馈将不胜感激。谢谢。

【问题讨论】:

    标签: ruby ruby-on-rails-3.2


    【解决方案1】:

    看起来问题在于,虽然 class_eval do 块是在 SomeNamespace::Bar 的上下文中执行的,但该上下文不适用于对类变量的引用。

    如果你explicitly access the class variable,那么事情应该会按预期进行:

    # 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,那么您使用自己的模块变量/类变量也没有问题,而不是重用现有的@987654329 @。我可能更喜欢这种方法;感觉它更好地封装了您的更改。

    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
    

    【讨论】:

      猜你喜欢
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-22
      • 1970-01-01
      • 2011-10-13
      相关资源
      最近更新 更多