【问题标题】:No method error for class variable defined with attr_accessor用 attr_accessor 定义的类变量没有方法错误
【发布时间】:2019-09-15 05:05:52
【问题描述】:

我想使用字符串数组动态定义方法。

这里有一段简单的代码可以实现这一点。


class SomeClass
  attr_accessor :my_array

  def initialize(user, record)
    @my_array=[]
  end

  my_array.each do |element|
    alias_method "#{element}?".to_sym, :awesome_method
  end

  def awesome_method
    puts 'awesome'
  end

end

当我在控制台中实例化这个类时,我得到以下错误

NoMethodError(nil:NilClass 的未定义方法 `each')

这段代码有什么问题以及如何使它工作。任何帮助都非常感谢:)

编辑 1: 我最终想要实现的是从 SomeClass 继承并覆盖子类中的 my_array 以动态定义具有如下属性的方法

class OtherClass < SomeClass

  my_array = %w[method1 method2 method3] 
  # Some mechanism to over write my_array. 

end

然后使用self.inherited在子类中动态定义方法。

有没有什么好的方法可以做到这一点?

【问题讨论】:

  • 你从类作用域调用my_array,而它是在实例作用域上定义的。

标签: ruby-on-rails ruby ruby-on-rails-5


【解决方案1】:

在您的代码中,您使用实例变量 (@my_array) 和 attr_accessor,然后尝试从类级别(即从类定义的主体,在任何方法之外)访问 my_array。但是实例变量只存在于实例级别,所以在类范围内是不可用的。

一种解决方案(自然解决方案,以及您可能会在其他语言中使用的解决方案)是使用类变量:@@my_array。但是 ruby​​ 中的类变量有点问题,所以最好的解决方案是使用类实例变量,如下所示:

class SomeClass
  class << self
    attr_accessor :my_array
  end

  @my_array=[]

  def initialize(user, record)
  end

  @my_array.each do |element|
    alias_method "#{element}?".to_sym, :awesome_method
  end

  def awesome_method
    puts 'awesome'
  end

end

语法有点棘手,所以,如果你查了一下它仍然没有意义,请尝试阅读范围并使用带有 @@ 的常规类变量。

编辑:

好的,因此,在您进行编辑之后,您想要完成的工作变得更加清晰。一个完整的工作示例如下:

class SomeClass
  class << self
    attr_accessor :my_array
  end

  @my_array=[]

  def awesome_method
    puts 'awesome'
  end

  def self.build!
    @my_array.each do |element|
      self.define_method("#{element}?".to_sym){ awesome_method }
    end
  end

end

class ChildClass < SomeClass
  @my_array = %w[test little_test]

  self.build!
end

child_instance = ChildClass.new

child_instance.test?
>> awesome

child_instance.little_test?
>> awesome

所以,我对 SomeClass 做了一些调整:

  1. 不需要初始化方法
  2. 我尝试使用继承的钩子来解决这个问题。它永远不会起作用,因为一旦编写了“ChildClass
  3. 我认为您需要“define_method”,而不是“alias_method”。
  4. awesome_method 在块中传递,这是 ruby​​ 进行函数式编程的方式。

完成后,ChildClass 继承自 SomeClass,并且它的实例具有动态创建的方法“test?”还有'little_test?'。

【讨论】:

  • 请参阅描述的编辑 1。有什么建议么?非常感谢您的帮助:)
  • 效果很好!我对构建有两个疑问!方法。 1. 由于访问器是在单例类中定义的,我们应该使用 my_array 而不是 @my_array 吗? 2. 我们应该使用define_method 而不是self.define_method 吗?如果这些建议是正确的,请更新您的答案。无论如何,我将其标记为正确。非常感谢您的帮助!!
  • 我认为define_method的工作原理是调用当前上下文的方法定义,也就是self,所以define_method和self.define_method没有区别。不过,我不是 100% 确定的。
  • 对于 \@my_array 与 my_array,当您调用“my_array”时,您只是通过您定义的访问器访问 \@my_array,因此没有区别。我已经包含了访问器,因为我认为您想从类定义外部调用 my_array(您可以更改 my_array 并从外部调用 ChildClass.build!)。但是,如果您不需要此功能,则可以完全省略访问器代码。在这种情况下,您将需要调用 \@my_array 否则会收到 NameError 异常。
【解决方案2】:

您需要将my_array 更改为可访问的类级别,在我的例子中是类常量。

class SomeClass
  DYNAMIC_METHOD_NAMES = %w(method_a method_b method_C).freeze

  def initialize(user, record)
  end

  DYNAMIC_METHOD_NAMES.each do |element|
    alias_method "#{element}?".to_sym, :awesome_method
  end

  def awesome_method
    puts 'awesome'
  end
end

【讨论】:

  • 谢谢。你是对的,这会奏效。但是当我从这个类继承并使用 self.inherited 动态定义子类中的方法时,我需要覆盖 DYNAMIC_METHOD_NAMES 。关于如何最好地实现这一目标的任何建议。请参阅问题描述中的编辑 1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多