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.

evianpring
  • 3,316
  • 1
  • 25
  • 54
  • It seems to me that you're dynamically trying to create a class method in another class method. I'm not quite clear why. – ydaniju May 22 '16 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. – evianpring May 23 '16 at 07:17

1 Answers1

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
David Lilue
  • 597
  • 2
  • 14
  • Why does the first option not work if I use a class instance variable @columns instead of a class variable @@columns? – evianpring May 23 '16 at 07:23
  • From this link http://stackoverflow.com/questions/15773552/ruby-class-instance-variable-vs-class-variable 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 May 23 '16 at 07:26
  • @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`. – David Lilue May 24 '16 at 03:31