0

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.

2
  • It seems to me that you're dynamically trying to create a class method in another class method. I'm not quite clear why. Commented May 22, 2016 at 21:23
  • I am implementing some of the basic functionality of Active Record. In particular I have a class SQLObject which makes queries to a SQLite database. My models inherit from SQLObject. The latter has a class method which generates some methods based on the name of the model class. One of these methods is a class method "find" which finds records by id. But I also want to generate methods "find_by_column_name" which queries the database for entries with a given value for a specific column. And this should all be done automatically given just the name of the inheriting class. Commented May 23, 2016 at 7:17

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

3 Comments

Why does the first option not work if I use a class instance variable @columns instead of a class variable @@columns?
From this link stackoverflow.com/questions/15773552/… it seems that class instance variables are available only to class methods and not to instance methods whereas class variables are available to both instance methods and class methods.
@evianpring yes, @columns is a class instance variable (without a getter method). But the variable column_name is local and cannot be access from the singleton class class << self.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.