Home > OS >  Dynamic table name in Ruby sqlite query based on class name
Dynamic table name in Ruby sqlite query based on class name

Time:05-08

I have a parent class that looks like this:

class Record
  attr_accessor :id, :url, :votes, :title, :first_name, :last_name, :selfdb

  def initialize(args = {})
    args.each { |name, value| instance_variable_set("@#{name}", value) }
    @selfdb = "#{self.class.name.downcase}s"
  end

  def self.find(id)
    DB.results_as_hash = true
    hasharray = DB.execute("SELECT * FROM ? WHERE id = ?", @selfdb, id)
    hasharray.empty? ? nil : new(hasharray[0].transform_keys(&:to_sym))
  end
end

Each child class of Record has a matching database table whose name is "#{name of the class}s", so the class "Post" is connected to a table named "posts".

My goal is to have self.find(id) to work on any children of this class. The solution I tried was to save the class' name into a string variable with an "s" at the end (so that class Post -> "posts", for example), to match the name of the database, as I tried in the instance variable @selfdb, but this does not work.

Calling @selfdb on the children classes confirms that it does correctly create the string for different classes, but running the sqlite with it inserted as the table name just returns nil.

This might be a very roundabout way of doing it, any suggestions are welcome. I am still learning and this is just a bootcamp assignment.

Edit: i realized one mistake I made: since self.find(id) is a class method, it can't use an instance variable. However, when I change the class method to work like this:

def self.find(id)
    selfdb = "#{self.name.downcase}s"
    DB.results_as_hash = true
    hasharray = DB.execute("SELECT * FROM ? WHERE id = ?", selfdb, id)
    hasharray.empty? ? nil : new(hasharray[0].transform_keys(&:to_sym))
end

... it still does not properly insert into the sqlite string.

CodePudding user response:

You define @selfdb in the initialize method which means it is only available in on the instance level. But your self.find method is a class method and therefore @selfdb is undefined on the class level.

I would suggest adding a class method that returns the table name like this

def self.table_name
  "#{name.downcase}s"
end

which you can then be used in the find class method like this

def self.find(id)
  # ...
  hasharray = DB.execute("SELECT * FROM ? WHERE id = ?", table_name, id)
  # ...
end

and in instance methods (for example to save a record) you need to use self.class.table_name or you could add a delegator to forward a table_name instance method call to the class method:

extend Forwardable
def_delegators :"self.class", :table_name
  • Related