AR: static vs. dynamic model generation

I have a project where there are an arbitrary number of databases, all of which have the same schema, but different data. In light of this, I'm trying to generate sets of models on the fly (one set per database). I need to be able to access several databases simultaneously without the connections overwriting each other. The traditional method for accessing multiple databases is to subclass ActiveRecord::Base as an abstract class for making the connection, then subclassing the abstract class to make the models. This isn't scalable, however, as I would have to manually create a new set of classes for each database.

I can generate classes on the fly and achieve the same effect as the traditional method, but for some reason ActiveRecord won't pick up my validations.

Below is my test code for both methods:

EDIT: last line of 'dynamically defined classes' code should read

puts a.valid? --> true

Earle Clubb wrote:

EDIT: last line of 'dynamically defined classes' code should read

puts a.valid? --> true

Your problem domain is not anything like the "usual" way that you describe. In the usual way, (with the subclassing of AR::Base, the different tables represent different models but in your case, you have the same models across separate dbs.

I would approach this like this..

class AudioFile < ActiveRecord::Base   abstract_class = true

  # all AudoFile code validation code goes here.. end

[:db1, :db2, :db3].each do |db|   Class.new("AudioFile#{db}") do     establish_connection db   end end

hth

ilan

Hi --

I have a project where there are an arbitrary number of databases, all of which have the same schema, but different data. In light of this, I'm trying to generate sets of models on the fly (one set per database). I need to be able to access several databases simultaneously without the connections overwriting each other. The traditional method for accessing multiple databases is to subclass ActiveRecord::Base as an abstract class for making the connection, then subclassing the abstract class to make the models. This isn't scalable, however, as I would have to manually create a new set of classes for each database.

I can generate classes on the fly and achieve the same effect as the traditional method, but for some reason ActiveRecord won't pick up my validations.

Below is my test code for both methods:

------------------------------ statically defined classes ------------------------------

require 'rubygems' require 'active_record'

class DbBase < ActiveRecord::Base self.abstract_class = true end

class AudioFile < DbBase validates_presence_of :name end

DbBase.establish_connection(:adapter => 'sqlite3', :database => 'test1.db') a = AudioFile.new puts a.valid? --> false

------------------------------ dynamically defined classes ------------------------------

require 'rubygems' require 'active_record'

DbBase = Class.new(ActiveRecord::Base) do self.abstract_class = true end

AudioFile = Class.new(DbBase) do validates_presence_of :name end

DbBase.establish_connection(:adapter => 'sqlite3', :database => 'test1.db') a = AudioFile.new puts a.valid?

------------------------------

Any ideas on how I can do this dynamic definition without ActiveRecord ignoring my validations?

I don't know exactly but in messing around with it I've noticed that the validations don't run even if you take out the second level of inheritance:

AudioFile = Class.new(ActiveRecord::Base) do    validates_presence_of :name end

AudioFile.establish_connection(:adapter => 'sqlite3', :database => 'test1.db') a = AudioFile.new puts a.valid? # true

Not an answer to your question, but maybe reducing it will pinpoint the issue.

David

Hi --

I have a project where there are an arbitrary number of databases, all of which have the same schema, but different data. In light of this, I'm trying to generate sets of models on the fly (one set per database). I need to be able to access several databases simultaneously without the connections overwriting each other. The traditional method for accessing multiple databases is to subclass ActiveRecord::Base as an abstract class for making the connection, then subclassing the abstract class to make the models. This isn't scalable, however, as I would have to manually create a new set of classes for each database.

I can generate classes on the fly and achieve the same effect as the traditional method, but for some reason ActiveRecord won't pick up my validations.

Below is my test code for both methods:

------------------------------ statically defined classes ------------------------------

require 'rubygems' require 'active_record'

class DbBase < ActiveRecord::Base self.abstract_class = true end

class AudioFile < DbBase validates_presence_of :name end

DbBase.establish_connection(:adapter => 'sqlite3', :database => 'test1.db') a = AudioFile.new puts a.valid? --> false

------------------------------ dynamically defined classes ------------------------------

require 'rubygems' require 'active_record'

DbBase = Class.new(ActiveRecord::Base) do self.abstract_class = true end

AudioFile = Class.new(DbBase) do validates_presence_of :name end

DbBase.establish_connection(:adapter => 'sqlite3', :database => 'test1.db') a = AudioFile.new puts a.valid?

------------------------------

Any ideas on how I can do this dynamic definition without ActiveRecord ignoring my validations?

I think that what's happening is that when you do the AF = Class.new(DbBase) version, the rhs gets evaluated before the assignment (of course). That means that a bunch of inheritance callback stuff gets executed with respect to an anonymous class. The part I haven't found is why that matters, since the class then gets bound to a constant... but it does appear to matter.

So -- and I present this more as a way to spot the problem, than a way to solve it elegantly -- you can re-execute some inheritance callback code, using the new class.

AudioFile = Class.new(ActiveRecord::Base) ActiveRecord::Base.send(:inherited_with_inheritable_attributes, AudioFile)

I'm still not sure exactly what makes the inheritance hooks care whether the new subclass is anonymous or not. Nor do I know whether re-executing that method is harmful... but it's ugly enough not to be a long-term solution anyway :slight_smile:

David

Hi --

Curtis wrote:

Hello Earle,

I have the same problem to solve. I was wondering if you ever came up with a solution?

Thanks,

Curtis

On Mar 17, 5:31�pm, Earle Clubb <rails-mailing-l...@andreas-s.net>

I did find a solution. When I was using a single database, I had all of my models wrapped up in a module so I could have cleaner module-level methods for connecting to the db, getting/setting flags in the db, etc. I turns out that if you make a copy of the module and change the module and db name, you get a whole new isolated connection. So I wrote a method to copy the module and change the name. Seems to work well. If you need more info, let me know. Thanks to all who helped out with this.

Earle