rails 3 + декоратор Draper

Захламлять модели своими методами в Rails-приложении это говнокод, это наверное знает каждый, в отличие от меня, однако, наличие дополнительных методов может существенно облегчить представление. Решить эту дилемму помогают декораторы. Ниже описан мой опыт знакомства с декораторами на примере гема Draper.

Зачем нужен рефакторинг моделей и что это такое написано еще тут. А вот гем Draper.

установка

Тут все как обычно. Добавляем в Gemfile

gem 'draper'

и бандлим

bundle install

эксплуатация

генерация

Если модель генерируется после того как был установлен Draper, то декоратор создастся сам автоматически. Для ручной генерации декоратора есть команда:

rails g decorator YourModel

После выполнения генерации можно увидеть директорию app/decorators где будет лежать файл your_model_decorator.rb. В этом файле и нужно прописывать дополнительные методы модели.

ошибки

Тут я не буду следовать инструкции со страницы гема т.к. она не соответствует реальности, по крайней мере с Rails 3.1.
Итак, на данный момент существует файл декоратора модели YourModel app/decorators/your_model_decorator.rb:

1
2
3
4
class YourModelDecorator < ApplicationDecorator
  decorates :your_model
  #bla-bla-bla
end

И если сейчас запустить консоль, то вылетит ошибка:

$ rails c
/home/username/ror/myapp/app/decorators/your_model_decorator.rb:2:in `': uninitialized constant ApplicationDecorator (NameError)
...

Конечно, можно создать файл app/decorators/application_decorator.rb как советует страница на гитхабе:

1
2
3
class ApplicationDecorator < Draper::Decorator
  # your methods go here
end

но это вряд ли поможет, т.к. выскочит другая ошибка при запуске консоли:

$ rails c
/home/username/ror/myapp/app/decorators/your_model_decorator.rb:2:in `': uninitialized constant Draper::Decorator (NameError)
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/draper-0.12.0/lib/draper/system.rb:9:in `block in load_app_local_decorators'
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/draper-0.12.0/lib/draper/system.rb:9:in `each'
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/draper-0.12.0/lib/draper/system.rb:9:in `load_app_local_decorators'
...

Пошарясь немного по файлам в директории /usr/local/rvm/gems/ruby-1.9.2-p290/gems/draper-0.12.0/lib/draper/, а конкретнее в файле /usr/local/rvm/gems/ruby-1.9.2-p290/gems/draper-0.12.0/lib/draper/base.rb становится ясно, что app/decorators/application_decorator.rb надо привести к виду:

1
2
3
class ApplicationDecorator < Draper::Base
  # your methods go here
end

И тут все ошибки резко пропадают.

использование

Теперь в декоратор нужно перенести лишние методы из модели. Для того чтобы экземпляры модели пополнились методами из декоратора при их использовании в контроллере или еще где-либо нужно делать так:

> your_model_exemplar=YourModel.first
> your_model_exemplar.new_method_which_is_in_decorator
NoMethodError: undefined method `new_method_which_is_in_decorator' for #
> your_model_exemplar=YourModelDecorator.new YourModel.first
> your_model_exemplar.new_method_which_is_in_decorator
=> "it really works"
> your_model_exemplars=YourModelDecorator.decorate YourModel.first(10)
> your_model_exemplars.first.new_method_which_is_in_decorator
=> "it really works"

Для коллекции обязательно использовать метод decorate, для единичного экземпляра можно и new, и decorate. Сказка про decorate_collection для коллекции, которая написана на гитхабе это всего лишь сказка, ее рекомендации не работают в реальном мире.

хелперы в декораторе

Офстраница на гитхабе советует задействовать хелперы через helpers и сокращенную форму h или без дополнительных примочек просто подключив модуль include Draper::LazyHelpers (ниже дезинформация с https://github.com/drapergem/draper):

1
2
3
4
5
6
7
class ArticleDecorator < Draper::Decorator   
  def published_at     
    date = h.content_tag(:span, article.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
    time = h.content_tag(:span, article.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
    h.content_tag :span, date + time, :class => 'created_at'
  end
end

У меня ничего из предложенного не прокатило, зато неожиданно прокатил способ с helper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class YourModelDecorator < ApplicationDecorator
  include Draper::LazyHelpers #по идее должен избавить от явного объявления helper, но нет
  decorates :your_model
  def test_helpers
    content_tag :div, "it really works"
  end
  def test_helpers_via_h
    h.content_tag :div, "it really works"
  end    
  def test_helpers_via_helpers
    helpers.content_tag :div, "it really works"
  end   
  def may_be_that_is_working?
    helper.content_tag :div, "Hallelujah!"
  end  
end
> your_model_exemplar=YourModelDecorator.new YourModel.first
> your_model_exemplar.test_helpers
NoMethodError: undefined method `content_tag' for #
> your_model_exemplar.test_helpers_via_h
NoMethodError: undefined method `content_tag' for nil:NilClass
> your_model_exemplar.test_helpers_via_helpers
NoMethodError: undefined method `content_tag' for nil:NilClass
> your_model_exemplar.may_be_that_is_working?
=> "<div>Hallelujah!</div>"

Собственно все. Быть может все эти ошибки из-за того, что у меня ruby 1.9.2, а гем писался для 1.9.3 или рельсы у меня старые, а может просто доки у гема что-то типа квеста.

One Comments to “rails 3 + декоратор Draper”

  1. [...] 3 Posted by lukmus | Category: ruby on rails | No Comments Если использовать декоратор Draper, то для использования методов модели описанных в [...]

Leave a Reply

(обязательно)

(обязательно)