Dynamically defined ar classes

I’m using at least 20 primary models, and join models for each
combination plus some hierarchical ones. I am wondering if it would be
possible to define the model classes iteratively, and then reopen the
definitions that I wanted to add code to.

primaries = [:Argument, :Author, :Book, :Category, :Hyperlink, :List,
:Message, :Ufile, :User, :Language, :Word, :Part_of_speech, :Definition,
:School, :Course, :Meeting, :Task, :Question, :Survey, :Datapoint,
:Code]
primaries.each do |primary|
class primary
primaries.each { |p| has_many join_tables(primary, p); has_many p,
:through => join_tables(primary, p) }
end
end

where join_tables is a function that looks into a nested hash to find
the symbol that I’ve chosen to name each table in the database. I do
not want to use single table inheritance. I thought that there might be
some trouble if a controller tried to load a file like argument.rb and
couldn’t find it, but couldn’t that be addressed by putting this do-end
loop at a point that always gets executed, like within the application
controller? Also, I’d need to pluralize the symbols before passing them
to has_many; must I convert to_s and then back or can I call pluralize
on the symbol? Do I need to call downcase?

Lastly: can variables be interpolated into backticks (system calls)?

I made a typo: each class should inherit from ActiveRecord::Base

primaries.each do |primary|
class primary < ActiveRecord::Base
primaries.each { |p| has_many join_tables(primary, p); has_many p,
:through => join_tables(primary, p) }
end
end

I’m not sure I completely understand why you want to do this. If it’s
to keep things DRY I would question whether this is a bit of an OTT
optimisation. The models you specified seem generally unrelated and
as your application grows, you will most likely find that this method
becomes a hindrance rather than beneficial.

I may be wrong of course, but generally separating your models out and
defining them individually is a good plan long term. If you want
something to get started with, I would suggest looking at Dr Nic’s
Magic Models (http://magicmodels.rubyforge.org/). This is a plugin
that automatically creates models from the database schema. Even if
it’s not what you’re looking for, you may pick up a few tips for what
you’re trying to do by having a look through the source code.

Steve

So: can this work?
Yeah, but not in the way you have. You can use the ‘eval’ command to
execute ruby code to create your classes:

[‘MyNewClass’].each do |klass|
model_string = “class #{klass} < ActiveRecord::Base; def say_hello;
‘Hello!’; end; end;”
eval(model_string)
end

my_new_class = MyNewClass.new
my_new_class.say_hello -> “Hello!”

You can build up the string using the values defined in your hashes
and arrays then execute. I’m not sure what effect all this will have
on the execution time of you app, but try it out and see how you get
on.

Steve

I’ve found as my application has grown that adding primary models has
been so intimidating that it has stifled my progress. I started with
the first nine and then added a language module, a schoolwork module,
and plans for a stats module. But I couldn’t settle on whether to
create join tables for each combination because it would mean I’d have
three hundred more secondary models to keep straight. Most of them
don’t have much in the way of unique behavior.

If this scheme worked out, I could add a primary model just by adding a
symbol to the array primaries, and adding an element to the join_tables
hash with twenty symbols containing names for join tables. Then I would
reopen the models to my heart’s content, and if I wanted to add similar
methods to several I could use the same iteration idiom, even if all I
was doing was mixing in a module.

Using magic models might help; thanks for the suggestion. But I still
think the question should be fair game. There’s one more wrinkle. I
might subclass the models like so:
class Primary < ActiveRecord::Base
end

then each actual primary model would descend from Primary
primaries.each do |primary|
class primary < Primary
end
end

So: can this work?