2

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

2 Answers 2

4

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?

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, that was helpful. You're right, I didn't want that.
2

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.

3 Comments

By my reading of the source, this shouldn't work; the very first line of ActiveRecord::ConnectionHandling#establish_connection is raise "Anonymous class is not allowed." unless name, before it takes a single look at the parameters you passed in. Have I misunderstood something?
Looks like code will work only in Rails 3.2 or older. establish connection source. Give me a second, i will try to make it work in new Rails.
Interesting, I was looking at ActiveRecord::ConnectionHandling#establish_connection, not ActiveRecord::ConnectionPool::ConnectionHandler#establish_connection. Gotta love confusingly similar names for things.

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.