Захламлять модели своими методами в 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 или рельсы у меня старые, а может просто доки у гема что-то типа квеста.
[...] 3 Posted by lukmus | Category: ruby on rails | No Comments Если использовать декоратор Draper, то для использования методов модели описанных в [...]