John Breedlove
John Breedlove

Reputation: 425

How can I make some methods from an array defined outside the class def

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"]

This does not, but this is what I need:

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

Upvotes: 0

Views: 437

Answers (5)

storage_pooling
storage_pooling

Reputation: 19

It is not a good idea to make glabal variables, instead, moving variable_of_doom into Class scopes would be a god idea.

Upvotes: 0

FMc
FMc

Reputation: 42411

Your question boils down to this failing example:

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

Are the variables of doom constant? If so, the following works, because classes have access to constants declared outside their own class definition:

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

class MyAwesomeClass
  puts VARIABLE_OF_DOOM
end

If you need the doom-worthy variables to be dynamic, you could create a module or class with a method that knows how to deliver them.

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

I would not resort to making $variables_of_doom a global. It's not necessary and should be avoided on principle.

Upvotes: 2

Alex D
Alex D

Reputation: 30445

In Ruby, a class (or def) block demarcates a new, separate scope in which local variables from surrounding scopes are not accessible. If you need surrounding local variables to be in scope, you can get around this using blocks, like:

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

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

In Pragmatic Bookshelf's Ruby Metaprogramming, they describe the use of this technique as "scope gates": use class or def blocks when you want to create a new scope for local variables, use blocks when you don't.

Upvotes: 1

Arup Rakshit
Arup Rakshit

Reputation: 118261

How to resolve the issue has been told in other answer. I would like to suggest you to do such instropection as below to check the scope rule :

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

Upvotes: 0

Old Pro
Old Pro

Reputation: 25537

In Ruby, global variables need are indicated by $ just like instance variables are indicated with @. So this works:

$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]

Upvotes: 0

Related Questions