I have a scenario where I need to dynamically create classes from
existing application (external to app) databases, to pull data for
reports.
# Table definitions
APP_TABLES = %w(#list of tables for a given app - I'm sure I could
query this on a simple connect. But let's stick to this for now.)
APP_TABLES.each do |table_name|
# camelize for class creation
class_name = table_name.camelize
klass = Class.new(App) # uses App class to establish_connection to
externalDB
klass = class_eval do
#unsure about this. I need to set table_name to same as class name
ActiveRecord::Base.set_table_name("#{class_name}")
ActiveRecord::Base.const_set(constant_name, klass)
end # class_eval
# After creating the class - I want to establish an instance - but it
says that the class does not exist - more specifically constant doesn't
exist.
eval("#{table_name} = #{class_name}.new")
I would love to hear thoughts on this, especially if I'm approaching
this incorrectly.
klass = Class.new(App) # uses App class to establish_connection to
externalDB
klass = class_eval do
#unsure about this. I need to set table_name to same as class name
ActiveRecord::Base.set_table_name("#{class_name}")
ActiveRecord::Base.const_set(constant_name, klass)
end # class_eval
This sounds fishy, you're calling set_table_name on AR::Base, not your
class. just klass.set_table_name 'blah' should be enough
# After creating the class - I want to establish an instance - but it
says that the class does not exist - more specifically constant doesn't
exist.
eval("#{table_name} = #{class_name}.new")
I would love to hear thoughts on this, especially if I'm approaching
this incorrectly.
You haven't created a constant called class_name -
ActiveRecord::Base.const_set(constant_name, klass) would have created
ActiveRecord::Base::Foo.
You probably wanted Object.const_set class_name, klass
klass = class_eval do
#unsure about this. I need to set table_name to same as class name
ActiveRecord::Base.set_table_name("#{class_name}")
ActiveRecord::Base.const_set(constant_name, klass)
end # class_eval
This sounds fishy, you're calling set_table_name on AR::Base, not your
class. just klass.set_table_name 'blah' should be enough
True. I receive an unknown method error though.
eval("#{table_name} = #{class_name}.new")
You haven't created a constant called class_name -
You probably wanted Object.const_set class_name, klass
Sorry - I seem to have missed posting the
class_name = table_name.camelize
line in there (have large sections of comments that I didn't want to
clutter the post).
I've changed the above code to:
APP_TABLES.each do |table_name|
class_name = table_name.camelize
klass = Class.new(App)
constant_name = "#{class_name}"
klass = class_eval do
set_table_name("#{class_name}") or puts "Class set table name
failed"
Object.const_set(constant_name, klass) or puts "Class name
constant set failed"
end # class_eval
but get an unknown method error on the set_table_name call.
The App class (below) inherits from ActiveRecord::Base which in turn
should have that method.
class App < ActiveRecord::Base
abstract_class = true
def initialize
establish_connection :app_development
end
# to avoid trying to connect to the Spydirdb table by default
def self.columns() @columns ||= ; end
end
klass = class\_eval do
set\_table\_name\("\#\{class\_name\}"\) or puts "Class set table name
failed"
Object.const_set(constant_name, klass) or puts "Class name
constant set failed"
end # class_eval
but get an unknown method error on the set_table_name call.
you don't need the class_eval . just klass.set_table_name should work
(I don't think the method is protected, if it is you might have to use
send)
The App class (below) inherits from ActiveRecord::Base which in turn
should have that method.
class App < ActiveRecord::Base
abstract_class = true
this should be self.abstract_class = true (right now you're just
setting a local variable)
def initialize
establish_connection :app_development
end
This is dodgy. First of all you're overriding initialize, but without
calling through to the super class, so your activerecord objects won't
get initialized properly. You've also change the signature of
initialize. Also, do you really want to be reconnecting to the
database everytime a new instance is created? lastly this
establish_connection is a class method
class App < ActiveRecord::Base
abstract_class = true
this should be self.abstract_class = true (right now you're just
setting a local variable)
Point taken.
def initialize
establish_connection :app_development
end
This is dodgy. First of all you're overriding initialize, but without
calling through to the super class, so your activerecord objects won't
get initialized properly.
Taken from online suggestions for handling multiple DBs.
You've also change the signature of
initialize. Also, do you really want to be reconnecting to the
database everytime a new instance is created? lastly this
establish_connection is a class method
# APP_TABLES contain a list of table names - would be better to
dynamically read them after connecting
APP_TABLES.each do |table_name|
class_name = table_name.camelize
klass = Class.new(App)
constant_name = "#{class_name}"
# even though klass is created with App as superclass (inheriting from
ActiveRecord::Base, the set_table_name seems to be unavailable - what am
I missing here?
klass.set_table_name ("#{class_name}") or puts "Class set table name
failed"
klass.const_set(constant_name, klass) or puts "Class name constant set
failed"
eval("#{table_name} = #{class_name}.new") or puts "Class instantiation
failed"
end
How can I determine if the klass has actually been created?
How long will the dynamic classes exist in memory?
For the duration of this rake task execution?
Is there a way to list classes created at runtime?
# APP_TABLES contain a list of table names - would be better to
dynamically read them after connecting
APP_TABLES.each do |table_name|
class_name = table_name.camelize
klass = Class.new(App)
constant_name = "#{class_name}"
# even though klass is created with App as superclass (inheriting from
ActiveRecord::Base, the set_table_name seems to be unavailable - what am
I missing here?
what happens when you try?
klass.set_table_name ("#{class_name}") or puts "Class set table name
failed"
klass.const_set(constant_name, klass) or puts "Class name constant set
failed"
eval("#{table_name} = #{class_name}.new") or puts "Class instantiation
failed"
end
How can I determine if the klass has actually been created?
I don't think Class.new can actually fail
How long will the dynamic classes exist in memory?
For the duration of this rake task execution?
Is there a way to list classes created at runtime?
I think that klass exists much like any variable - it may be garbage
collected if nothing references it (classes might be special though) .
You can step through objects of any class (including Class) via
objectspace, although you probably won't be able to distinguish
classes created like this from other classes.
That's just what you're printing out - the return value of
set_table_name isn't documented, so i wouldn't assume nil or false to
mean failure
OK, so I'm down to:
class ExternalApp < ActiveRecord::Base
self.abstract_class = true
establish_connection :external_app
def self.columns() @columns ||= ; end
end
#App tables has a list of tables in external app, until I get this bit
working
APP_TABLES.each do |table_name|
constant_name = class_name = table_name.camelize
klass = Class.new(ExternalApp)
klass.set_table_name ("#{class_name}")
klass.const_set(constant_name, klass)
eval("#{table_name} = #{class_name}.new") or puts "Class instantiation
failed"
end
On running this rake task I get the following error:
#App tables has a list of tables in external app, until I get this bit
working
APP_TABLES.each do |table_name|
constant_name = class_name = table_name.camelize
klass = Class.new(ExternalApp)
klass.set_table_name ("#{class_name}")
klass.const_set(constant_name, klass)
eval("#{table_name} = #{class_name}.new") or puts "Class instantiation
failed"
end
On running this rake task I get the following error: