Dynamic class creation at runtime

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.

On Jan 17, 10:02am, Sem P. [email protected] wrote:

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

Fred

On Jan 17, 12:44pm, Sem P. [email protected] wrote:

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

Thanks for the reply.

Frederick C. wrote in post #975422:

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 external app table by default

def self.columns() @columns ||= []; end
end

Frederick C. wrote in post #975445:

On Jan 17, 12:44pm, Sem P. [email protected] wrote:

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

Thanks. I’ll fix all that and get back to you.

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?

On Jan 18, 6:31am, Sem P. [email protected] wrote:

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.

Fred

Thanks for your repeated answers.

Frederick C. wrote in post #975619:

what happens when you try?

Class set table name failed

On Jan 18, 7:55am, Sem P. [email protected] wrote:

Thanks for your repeated answers.

Frederick C. wrote in post #975619:

what happens when you try?

Class set table name failed

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

Fred

Frederick C. wrote in post #975672:

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:

lib/rspec/core/backward_compatibility.rb:20:in `const_missing’:
uninitialized constant Services

Is const_set where the klass name is ‘set in stone’?

On 19 Jan 2011, at 08:05, Sem P. [email protected] wrote:

On running this rake task I get the following error:

lib/rspec/core/backward_compatibility.rb:20:in `const_missing’:
uninitialized constant Services

Is const_set where the klass name is ‘set in stone’?

It is, but you’re using it back to front: klass.const_set sets a
constant on klass, whereas you want to set a top level constant ie
Object.const_set

Fred.

Frederick C. wrote in post #975964:

It is, but you’re using it back to front: klass.const_set sets a
constant on klass, whereas you want to set a top level constant ie
Object.const_set

That makes sense, thought it might be a scope problem.

Thank you.

Frederick C. wrote in post #975964:

It is, but you’re using it back to front: klass.const_set sets a
constant on klass, whereas you want to set a top level constant ie
Object.const_set

You are my personal hero, today. Thank you so much for helping out on
this thread. It works.

Ok, so this seems to work

  1. Create dynamic classes from a list (some rework on getting that from
    tables automatically needed)
  2. Instantiate those objects

A minor error pops up now.

The tables exist, as do the fields.

However, with:

service = Services.new(:portID => 333, :method => “PUT”)

But I get the error:

unknown attribute: portID

On Jan 19, 8:35am, Sem P. [email protected] wrote:

The tables exist, as do the fields.

However, with:

service = Services.new(:portID => 333, :method => “PUT”)

But I get the error:

unknown attribute: portID

Probably the result of you suppressing the columns method. If AR
things there are no columns, it will think there are no attributes

Fred

Frederick C. wrote in post #975987:

Probably the result of you suppressing the columns method. If AR
things there are no columns, it will think there are no attributes

From the superclass. Right. Thanks.

Everything’s perfect now. Thanks again for the repeated replies.