Чёрная магия ActiveResource

Натолкнулся только что на одну неприятную особенность ActiveResource. Как известно ActiveResource позволяет прозрачно подменять ActiveRecord на клиенте и транслировать запросы отправленные ему на сервер при помощи REST где уже и происходит обращение к БД через ActiveRecord. Такое поведение накладывает ряд ограничений на ActiveResource и заставляет его, в некоторых случаях, вести себя иначе чем ActiveRecord.
Возникшая же проблема связана с тем что ActiveRecord в момент инициализации класса обращается к связанной с ним таблице БД и запрашивает список колонок которые находятся в ней. А ActiveResource как вы поняли такого не делает и тому есть несколько причин, одна из которых состоит в том, что нет такого стандарта который бы заставлял разработчиков REST сервисов предоставлять информацию о структуре таблиц в надежде на то что работа с сервисом будет идти через ActiveResource.
В общем всё то что я упомянул выше выливается в следующую ошибку:
class Article < ActiveResource::Base
self.site = "http://localhost:3001"
end
...
obj = Article.new
put obj.title #=> undefined method `title' for #<Article:0x274bfb0>
т.е. на момент вызова obj.title класс ActiveResource не знает есть ли такое поле, каков его тип и чему разно значение по умолчанию для него.
Несколько идей того как это можно исправить предложены в этой статье. Я расскажу о самом простом и не совсем элегантном способе. Способ заключается в том что вы вручную инициализируете свойство класса @@attributes заполняя его доступными полями и значениями по умолчанию. Для этой цели я написал вспомогательный класс ActiveResourceDefaults:
module ActiveResourceDefaults
@@defaults = {}
def initialize(attributes = {})
@@defaults.each do |key, value|
attributes[key] = value if !attributes.key?(key.to_s)
end
super attributes
end
end
который вы можете положить в каталог <app-root>/lib и использовать следующим образом:
require 'ActiveResourceDefaults'
class Article < ActiveResource::Base
self.site = "http://localhost:3001"
include ActiveResourceDefaults
@@defaults = {
:title => nil,
:body => nil,
:published_at => nil
}
end
после этого при обращении к obj.title вы будете получать значение nil, а не исключение как было раньше.


Добавить новый комментарий