Работа с устаревшей базой данных в Ruby on Rails вызывает странную проблему с загрузкой классов - PullRequest
1 голос
/ 22 декабря 2010

Короткая версия: пытаясь разобраться с устаревшей базой данных в Ruby-on-Rails, я столкнулся с некоторым поведением загрузки классов, которое мне не понятно.

Длинная версия: На своей работе я работаю над базой кода PHP, работающей с базой данных MySQL, с иногда полезными, но обычно расстраивающими соглашениями по именованию. Таким образом, он не вписывается в типичные шаблоны именования 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 - имя таблицы в единственном числе, иногда в верблюжьих, иногда в нижней части
  • 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 имеет атрибуты name и otherProperty). Но что-то не так с ассоциациями. Например, в шаблоне 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 Ответ

1 голос
/ 29 декабря 2010

ActiveRecord довольно самоуверен, когда дело доходит до соглашений об именах.Возможно, вам повезет гораздо больше, если использовать DataMapper, который позволяет вам настроить модель, которая использует свои собственные соглашения о присвоении имен для своих атрибутов в стиле Rails, но при сохранении или обновлении они переводятся в имена соответствующих столбцов в базе данных.

http://datamapper.org/docs/legacy

...