Короткая версия: пытаясь разобраться с устаревшей базой данных в 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.