如何从类def之外定义的数组中创建一些方法

时间:2013-06-22 14:10:16

标签: ruby metaprogramming

This works:

class MyAwesomeClass

variable_of_doom = ["things", "stuff", "chunky_bacon"]  

  variable_of_doom.each do |name|
    define_method("#{name}") do
      puts name
    end
  end
end

puts (MyAwesomeClass.instance_methods - Object.instance_methods).inspect

# => ["things", "stuff", "chunky_bacon"]

这不,但这就是我需要的:

variable_of_doom = ["things", "stuff", "chunky_bacon"]  

class MyAwesomeClass

  variable_of_doom.each do |name|
    define_method("#{name}") do
      puts name
    end
  end
end

puts (MyAwesomeClass.instance_methods - Object.instance_methods).inspect
# => NameError: undefined local variable or method ‘variable_of_doom’ for MyAwesomeClass:Class

# => method <class:MyAwesomeClass>  in untitled 4 at line 9
# => method <main>  in untitled 4 at line 7

5 个答案:

答案 0 :(得分:2)

你的问题归结为这个失败的例子:

variable_of_doom = %w(famine plague flood war pestilence drought television)

class MyAwesomeClass
  # undefined local variable or method `variable_of_doom' for MyAwesomeClass:Class
  puts variable_of_doom
end

厄运变量是否恒定?如果是这样,以下工作,因为类可以访问在它们自己的类定义之外声明的常量:

VARIABLE_OF_DOOM = %w(famine plague flood war pestilence drought dos)

class MyAwesomeClass
  puts VARIABLE_OF_DOOM
end

如果你需要值得注定的变量是动态的,你可以用一个知道如何传递它们的方法来创建一个模块或类。

module VarsOfDoom
  VOD = %w(famine plague flood war pestilence drought computer-mouse)

  def self.dynamic
    VOD.shuffle
  end
end

class MyAwesomeClass
  # Works because the module VarsOfDoom is itself a constant.
  puts VarsOfDoom::VOD
  puts
  puts VarsOfDoom.dynamic
end

我不会诉诸于$variables_of_doom全球化。这没有必要,原则上应该避免。

答案 1 :(得分:1)

在Ruby中,class(或def)块划分了一个新的独立范围,在该范围内无法访问来自周围范围的局部变量。如果您需要将周围的局部变量放在范围内,您可以使用块来解决这个问题,例如:

 variable_of_doom = ["things", "stuff", "chunky_bacon"]

 MyAwesomeClass.class_eval do
   variable_of_doom.each do |var|
     # do whatever you need here
   end
 end

在Pragmatic Bookshelf的 Ruby Metaprogramming 中,他们描述了将此技术用作“范围门”:当您想要创建新范围时,使用classdef块对于局部变量,请在不使用时使用块。

答案 2 :(得分:0)

在Ruby中,全局变量需要由$表示,就像实例变量用@表示一样。所以这有效:

$variable_of_doom = ["things", "stuff", "chunky_bacon"] 
class MyAwesomeClass

  $variable_of_doom.each do |name|
    define_method("#{name}") do
      puts name
    end
  end
end

puts (MyAwesomeClass.instance_methods - Object.instance_methods).inspect
# => [:things, :stuff, :chunky_bacon]

答案 3 :(得分:0)

如何解决问题已在其他答案中说明。我建议你按照下面这样的instropection检查范围规则:

variable_of_doom = ["things", "stuff", "chunky_bacon"] 

defined? variable_of_doom # => "local-variable"
local_variables.include? :variable_of_doom # => true

class MyAwesomeClass
  p defined?(variable_of_doom)
  p local_variables.include?(:variable_of_doom )
end
# >> nil
# >> false

答案 4 :(得分:0)

创建全局变量并不是一个好主意,相反,将variable_of_doom移动到Class范围将是一个神明的想法。