在Ruby on Rails中处理遗留数据库会导致奇怪的类加载问题

时间:2010-12-22 01:59:27

标签: ruby-on-rails ruby legacy

简短版本:在试图处理Ruby-on-Rails中的遗留数据库时,我遇到了一些我不理解的类加载行为。

长版本:在我的工作中,我正在开发一个针对MySQL数据库运行的PHP代码库,它有时有用,但通常令人沮丧的命名约定。因此,它不适合典型的Ruby on Rails命名模式。一个例子:

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. */
);

值得注意的属性:

  • objectOfSomeType - 表名是单数,有时是camelCased,有时是under_scored
  • OOST(某些类型的对象) - 列都具有共享前缀
  • OOSTid - 几乎所有表都有自动增量主键=前缀+'id'
  • OOSTatooID - 表示与anotherTypeOfObject表(具有前缀='ATOO')的外键关系
  • OOST {已输入,已修改}日期 - 对应于RoR的{已创建,已更新} _at

我试图建立一个ActiveRecord::Base的子类,它有一些处理表前缀的函数。 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

这适用于单个模型(ObjectOfSomeType对象具有nameotherProperty属性)。但是,这些协会出了问题。举例来说,在object_of_some_type / show.html.erb模板中:

This works fine, no need for .OOSTname:
<%= h @oost.name %>

This works fine, accessing the prefixed name directly:
<%= h @oost.atoo.ATOOname %>

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 %>

标记的行是什么“修复”了这个问题?我可能会采用不同的方法(在这个数据库中设置一个充满RoR命名视图的数据库),但我仍然希望用我的Ruby知识填补这个空白。

1 个答案:

答案 0 :(得分:1)

ActiveRecord在命名约定时非常自以为是。使用DataMapper可能会有更多的运气,它允许您设置一个模型,该模型使用其自己的Rails类命名约定作为其属性,但在保存或更新时,它们将被转换为数据库中的基础列名。

http://datamapper.org/docs/legacy