简体   繁体   English

使用attr_accessor定义的类变量没有方法错误

[英]No method error for class variable defined with attr_accessor

I want to define methods dynamically using an array of strings. 我想使用字符串数组动态定义方法。

Here is a simple piece of code that should achieve that. 这是应该实现的简单代码。


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

When I instantiate this class in the console, I get the following error 当我在控制台中实例化此类时,出现以下错误

NoMethodError (undefined method `each' for nil:NilClass) NoMethodError(nil:NilClass的未定义方法“ each”)

What is wrong with this code and how to make it work. 此代码有什么问题,以及如何使其工作。 any help highly appreciated :) 任何帮助,高度赞赏:)

Edit 1: What I ultimately want to achieve is to inherit from SomeClass and override my_array in the child class to dynamically define methods with its attributes like so 编辑1:我最终想要实现的是从SomeClass继承并覆盖子类中的my_array以动态定义具有其属性的方法,如下所示

class OtherClass < SomeClass

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

end

And then use self.inherited to dynamically define methods in child class. 然后使用self.inherited动态定义子类中的方法。

Is there a good way to achieve this? 是否有实现此目标的好方法?

In your code, you use an instance variable (@my_array) and an attr_accessor over it, and then try to access my_array from class level (that is, from the body of the class definition, outside of any methods). 在代码中,您使用实例变量(@my_array)和其上的attr_accessor,然后尝试从类级别(即从任何方法之外的类定义的主体)访问my_array。 But instance variables only exist at instance level, so it is not available in the class scope. 但是实例变量仅存在于实例级别,因此在类范围中不可用。

One solution (the natural one, and the one which you would probably use in other languages) is to use a class variable: @@my_array. 一种解决方案(自然的解决方案,以及您可能会用其他语言使用的解决方案)是使用类变量:@@ my_array。 But class variables in ruby are a little problematic, so the best solution would be to make use of class instance variables, like that: 但是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

The syntax is a little tricky, so, if you look that up and it still doesn't makes sense, try just reading about scopes and using a regular class variable with @@. 语法有点棘手,因此,如果您查找起来仍然没有意义,请尝试仅阅读范围并使用带有@@的常规类变量。

Edit: 编辑:

Ok, so, after your edit, it became more clear what you are trying to accomplish. 好的,因此,在您进行编辑之后,您将要完成的任务变得更加清晰。 A full working example is like follows: 完整的工作示例如下:

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

So, I've made some tweaks on SomeClass: 因此,我对SomeClass进行了一些调整:

  1. It does not need an initialize method 它不需要初始化方法
  2. I tried to use the inherited hook for this problem. 我试图使用继承的钩子来解决此问题。 It won't ever work, because this hook is called as soon as "ChildClass < SomeClass" is written, and this must be before you can define something like @my_array = %w[test little_test]. 它将永远无法工作,因为在编写“ ChildClass <SomeClass”后立即调用此钩子,并且必须在定义@my_array =%w [test little_test]之类的东西之前就调用此钩子。 So, I have added a self.build! 因此,我添加了一个self.build! method that must be called in the child instances so that they build their methods from my_array. 必须在子实例中调用的方法,以便它们从my_array构建其方法。 This is inevitable, but I think it is also good, because it makes more explicit in the subclasses that you are doing something interesting there. 这是不可避免的,但我认为它也很好,因为它在子类中更加明确地表明您正在此处做一些有趣的事情。
  3. I think you want "define_method", not "alias_method". 我认为您要使用“ define_method”,而不是“ alias_method”。
  4. awesome_method in passed in a block, which is ruby's way of doing functional programming. awesome_method传入一个块,这是ruby进行函数式编程的方式。

With that done, ChildClass inherits from SomeClass, and it's instances have the dynamically created methods 'test?' 完成后,ChildClass继承自SomeClass,并且其实例具有动态创建的方法“ test?”。 and 'little_test?'. 和“ little_test?”。

You need to change my_array to class level accessible, in my case class constant. 您需要将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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM