xoux
xoux

Reputation: 3494

How do I dynamically create class method by using a variable from outside a singleton class?

In MyClass I have a class method which creates certain instance methods dynamically (using define_method). However, I also want to dynamically create class methods.

As can be seen below, I am using class << self and calling define_method from within this scope (actually I am not sure if scope is the right word to use, though block also doesn't seem correct. What is the correct term to reference everything executed inside of class << self?) to define a class method.

However, I want to name the class method using a variable that comes from outside of class << self, namely, column_name.

Therefore I want to use the variable column_name inside of class << self to name my class methods in define_method. I get an error when I try to run create_methods:

NameError: undefined local variable or method 'column_name' for # from (pry):7:in 'singleton class'

class MyClass
  @columns = ['col1', 'col2']  
  def self.create_methods  
    @columns.each do |column_name|
      define_method(column_name) { |column_name| puts column_name }    
      class << self    
        define_method(column_name) { |column_name| puts "class_method defined" }      
      end  
    end  
  end
end

How can I access a variable from inside the singleton class? What is the best way of accomplishing what I want to accomplish here (creating class methods)?

Note: I have tried the method define_singleton_method and it seems to work for what I want to do. Does it accomplish exactly what I am trying to do? Even if it does, my question about using class << self remains.

Upvotes: 0

Views: 260

Answers (1)

David Lilue
David Lilue

Reputation: 631

To accomplish what you want, you can do:

class MyClass
    @@columns = ['col1', 'col2']  

    def self.create_methods  
        @@columns.each do |column_name|
            define_method(column_name) { |column_name| puts column_name }    
        end

        class << self
            @@columns.each do |column_name|
                define_method(column_name) { |col_name| puts "class_method defined" }      
            end
        end
    end
end

Note: Using def self."method_name" to define a class method let you to use instances variables (ie. @columns), that's why i redefined as class variable.

When you open the singleton class (class << self), you lose the previous scope, so you cant get column_name (Name Error). But have access to class variables.

In your case, use define_singleton_method. As follow:

class MyClass
    @columns = ['col1', 'col2']  

    def self.create_methods  
        @columns.each do |column_name|
            define_method(column_name) { |column_name| puts column_name }    
            define_singleton_method(column_name) { |col_name| puts "class_method defined" }
        end
    end
end

Upvotes: 1

Related Questions