开发者

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜