5

I'm trying to make a "generic model" so it can connect to any table of any database. First, I made this class which connects to another database specified (not using the schema)

Db

class Db < ActiveRecord::Base

    self.abstract_class = true

    attr_accessor :error

    def initialize(item = nil)
        @error = ""
        connect
        super
    end

    def connect
        could_connect = true
        @error = ""

        begin
            ActiveRecord::Base.establish_connection(
              :adapter  => "mysql2",
              :host     => "localhost",
              :username => "root",
              :password => "",
              :database => "another_database", 
              :port => 3306,
              :encoding => "utf8"
            )
        rescue ActiveRecord::ConnectionNotEstablished
            @error = "Could not connect to database. The connection was not established"
            could_connect = false
        rescue Mysql2::Error
            @error = "Could not connect to database using MySQL2"
            could_connect = false
        rescue => e
            @error = "Could not connect to database. #{e.message}."
            could_connect = false
        end

        return could_connect
    end

end

Then, I made this class which inherits from Db and specifies the table name

Gmodel

class Gmodel < Db

    def initialize(new_table_name)
        ActiveRecord::Base.set_table_name(new_table_name)
        super
    end

end

Finally, in the controller

MainController

class MainController < ApplicationController

  def index
    @users = Gmodel.new("users")
  end

end

But, it gaves me this error:

undefined method `stringify_keys' for "users":String

What could be wrong? Is there some better way to do this? Thanks in advance!

mu is too short
  • 426,620
  • 70
  • 833
  • 800
pablomarti
  • 2,087
  • 2
  • 22
  • 35

1 Answers1

13

Why not simply create an ActiveRecord::Base subclass at runtime and avoid all the hassle?

t = 'some_table'
c = Class.new(ActiveRecord::Base) { self.table_name = t }

then c refers to an AR class for some_table and you can do the usual things:

o = c.find(1)
# 'o' is now a wrapper for the row of some_table where 'id = 1'

cols = c.columns.map(&:name)
# 'cols' is now an array of some_table's column names

This is Ruby where classes are objects too.

If you need to connect to another database then you can to put the establish_connection call in the block along with the self.table_name:

t = 'some_table'
d = 'some_other_database'
c = Class.new(ActiveRecord::Base) do
    establish_connection(:adapter => 'mysql2', :database => d, ...)
    self.table_name = t
end
mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • This looks very much like something I could use to solve [this problem](http://stackoverflow.com/questions/10729289/tableless-model-with-activerecord-associations-in-rails-3-2) - would greatly appreciate if you could have a look and give me some input? – John Schulze May 24 '12 at 03:55
  • 1
    Anonymous ActiveRecord classes are not supported in Rails 4: https://github.com/rails/rails/issues/8934 – Jonathan Nesbitt Feb 23 '16 at 22:02
  • @JonathanNesbitt Do you have any work-arounds in mind? Maybe something unpleasant using one of the eval or exec methods would work. Or ditch ActiveRecord in favor of something more flexible. – mu is too short Feb 26 '16 at 05:29