Dealing with legacy database in Ruby on Rails causes weird class-loading issue
Short version: In trying to deal with a legacy database in Ruby-on-Rails, I've come across some class-loading behavior that I don't understand.
Long version: At my job, I'm working on a PHP codebase running against a MySQL database with sometimes-helpful, but usually-frustrating naming conventions. So, it doesn't fit into the typical Ruby on Rails naming patterns. An example:
create table objectOfSomeType (
OOSTid int primary key auto_increment,
OOSTatooID int,
OOSTname varchar(255) not null,
OOSTotherThing datetime,
OOSTenteredDate timestamp not null,
OOSTenteredBy varchar(32) not null,
OOSTmodifiedDate timestamp,
OOSTmodifiedBy varchar(32)
/* etc. */
);
create table another_type_of_object (
ATOOid int primary key auto_increment,
ATOOname varchar(255) not null
/* etc. */
);
Notable properties:
- objectOfSomeType - table name is singular, sometimes camelCased, sometimes under_scored
- OOST (Object Of Some Type) - columns all have a shared prefix
- OOSTid - virtually all tables have an auto-increment primary key = prefix + 'id'
- OOSTatooID - indicates a foreign key relationship against the anotherTypeOfObject table (which has prefix = 'ATOO')
- OOST{entered,modified}Date - correspond to RoR's {created,updated}_at
I tried to set up a subclass of ActiveRecord::Base
that had some functions for dealing with the table prefixes. E.g.:
class ActiveRecordJob < ActiveRecord::Base
class_inheritable_accessor :table_prefix
class << self
# this class needs no table
abstract_class = true
# function to setup the prefix
def set_table_prefix(prefix)
self.table_prefix = prefix
set_primary_key "#{prefix}id"
end
# override the standard column_names to return prefix-less names
def column_names
names = super
if pfx = self.table_prefix
# obviously not a Rubist...
names = names.map { |n| n.sub(%r|^#{pfx}|, "") }
end
names
end
end
# override method_missing to set up non-prefixed versions
# if the prefixed version is found, otherwise fall back to super
def method_missing(method, *args)
if pfx = self.table_prefix
prefixed = pfx + method
if respond_to?(prefixed)
self.class.send(:define_method, method.to_sym) do
return send(prefixed, *args)
end
return send(method, *args)
end
end
super
end
end
class ObjectOfSomeType < ActiveRecordJob
set_table_name :objectOfSomeType
set_table_prefix :OOST
belongs_to :atoo, :class_name => 'AnotherTypeOfObject', :foreign_key => 'OOSTatooID'
end
class AnotherTypeOfObject < ActiveRecordJob
set_table_name :anotherTypeOfObject
set_table_prefix :ATOO
end
This works well enough for single models (an ObjectOfSomeType
object has name
and otherProperty
attributes). But, something is going wrong with the associations. By way of example, in the object_of_some_type/show.html.erb template:
This works fine, no need for .OOSTname:
<%= h @oost.name %>
This works fine, accessing the prefixed name directly:
<%= h @oost.atoo.ATOOname %&g开发者_如何转开发t;
But this throws an exception, because the custom method_missing is not hit:
<%= h @oost.atoo.name %>
Surprisingly (to this Ruby noob), this does work.
<%= h @oost.atoo.class.find(@oost.atoo.id).name %> <!-- this line marked -->
After the above, working call, this also works:
<%= h @oost.atoo.name %>
What does the marked line do that "fixes" the problem? I'm likely going to go a different route (setting up a database full of RoR-named views against this database), but I'd still like to fill in this gap in my Ruby knowledge.
ActiveRecord is quite opinionated when it comes to naming conventions. You might have considerably more luck using DataMapper, which allows you to set up a model that uses its own Rails-like naming conventions for its attributes, but on save or update they are translated into their underlying column names in the database.
http://datamapper.org/docs/legacy
精彩评论