简体   繁体   中英

Dynamically create Ruby class with ActiveRecord connection

I'm attempting to dynamically create a Class and assign one of several database connections to each Class.

I'm working with anywhere between two or three databases which change over time, therefore, I'm hesitant to store each connection string in a separate Class and inherit from it instead of ActiveRecord::Base.

The following throws an error "RuntimeError: Anonymous class is not allowed.", but I'm not sure how to work around it or if there are better alternatives.

  class ClassFactory
    def self.create_class(new_class, table, db_connection)

      c = Class.new(ActiveRecord::Base) do
        db = db_connection
        self.table_name = table
        establish_connection(:adapter => db.database_type, :host => db.host, :database => db.database, :username => db.username, :password => db.password).connection
      end

      Module.const_set new_class, c
    end
  end

Per ActiveRecord::ConnectionHandling#establish_connection 's source , you can't establish a connection from a class for which name doesn't return a truthy value.

Now, you are assigning the class to a constant using const_set , and that will give it a name. But you need to do that before you call establish_connection :

class ClassFactory
  def self.create_class(new_class, table, db_connection)
    c = Class.new(ActiveRecord::Base) do
      db = db_connection
      self.table_name = table
    end

    Module.const_set new_class, c
    c.establish_connection(:adapter => db.database_type, :host => db.host, :database => db.database, :username => db.username, :password => db.password).connection
  end
end

Also, do you really want Module.const_set(...) ? That will result in a class named Module::Foo . Possibly you just want Object.const_set(...) (which just gives Foo )? Or even const_set(...) , so you get ClassFactory::Foo ?

You may set establish_connection for model dynamically:

database.yml

development:
  adapter: mysql
  username: root
  password: 
  database: example_development

oracle_development:
  adapter: oracle
  username: root
  password: 
  database: example_oracle_development

And in any place of code you may change db connection of your model:

User.establish_connection "oracle_#{RAILS_ENV}".to_sym

Also you can create model classes dynamically:

class ClassFactory
  def self.create_class(new_class, table, connection_name)

    Object.const_set(new_class, Class.new(ActiveRecord::Base) {})
    new_class.constantize.table_name = table
    new_class.constantize.establish_connection(connection_name)

  end
end

ClassFactory.create_class('NewUser', 'users', :development)

After that NewUser class will be available to use.

This version works in both Rails 5.0 and 3.2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM