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

30 октября 2008 13:19 | Комментарии
Чёрная магия ActiveRecord

Натолкнулся только что на одну неприятную особенность 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, а не исключение как было раньше.

Тэги: ActiveResource
Будь всегда в курсе последних новостей блога подписавшись на новости в формате RSS. Присоединяйся!
* * *

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

*

(не будет опубликован) *

(если есть)

1. Проверьте комментарий перед отправкой
2. Все комментарии проходят модерацию перед публикацией в блоге