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:


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?

Thanks,
Earle

EDIT: last line of ‘dynamically defined classes’ code should read

puts a.valid? --> true

Earle C. 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 –

On Mon, 17 Mar 2008, Earle C. wrote:

require ‘rubygems’
DbBase.establish_connection(:adapter => ‘sqlite3’, :database =>
require ‘active_record’
‘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


Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!

Hi –

On Mon, 17 Mar 2008, Earle C. wrote:

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

Try changing that to:

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

David


Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!

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 C. [email protected]

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 C. [email protected]

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

Hi –

On Mon, 17 Mar 2008, Earle C. wrote:

require ‘rubygems’
DbBase.establish_connection(:adapter => ‘sqlite3’, :database =>
require ‘active_record’
‘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


Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs