В части №1 мы увидели, что нулевые значения, на наш взгляд, могут накапливаться по многим причинам.
Сегодня мы собираемся использовать рубиновый драгоценный камень Draper, чтобы реализовать шаблон проектирования Decorator в нашем приложении Ruby 2.4.0 и Rails 5.1.4.
Использование этого шаблона упростит разработку интерфейса пользователя; 5 строк в 3 файлах рефакторинга.
Декоратор наследует методы от объекта, который он обертывает, добавляя дополнительные методы, которые мы реализуем, не влияя на базовый объект. То, что мы добавим сегодня, касается логики представления.
Какие?
Другими словами, декораторы обертывают или, как мне нравится думать, владеют рубиновым объектом:
# Two ways to call 2.4.0 :010 > user = User.first.decorate => #<UserDecorator:0x007fd10659bb98 @object=#<User id: 1, name: "Loi V Tran", email: "[email protected]", ...) 2.4.0 :012 > user = UserDecorator.new(User.first) => #<UserDecorator:0x007fd103366128 @object=#<User id: 1, name: "Loi V Tran", email: "[email protected]", ...)
и заботятся, среди прочего, о представлении нашего приложения конечным пользователям. Посмотрим как.
Сначала добавьте гем Draper в наш Gemfile.
# Gemfile gem 'draper'
Потом:
bundle install
Когда это будет завершено, запустите:
rails g decorator User
Это создаст для нас новый файл по адресу
# app/decorators/user.rb class UserDecorator < Draper::Decorator delegate_all end
Теперь мы можем провести рефакторинг и исправить ошибку внешнего интерфейса с помощью 5 строк кода в 3 файлах.
Первые два - это наши изменения в контроллере и представлении соответственно:
- Контроллер:
# app/controllers/users_controller.rb def index @users = User.all end
становится:
def index @users = User.all.decorate end
2. Просмотр:
# app/views/index.html.erb <% @users.each do |other_user| %> ... # Displaying location requires 2 method calls <ul> <li>Position: <%= other_user.position %></li> <li>School: <%= other_user.school %></li> <li>Location: <%= other_user.city + ', ' + other_user.state %></li> </ul> ... <% end %>
становится:
# app/views/index.html.erb <% @users.each do |other_user| %> ... # Displaying location requires 1 method call <ul><li>Position: <%= other_user.position %></li> <li>School: <%= other_user.school %></li> <li>Location: <%= other_user.location %></li> </ul>
... <% end %>
Наконец, нам нужно реализовать метод location
, который мы только что вызвали в представлении.
# app/decorators/user.rbclass UserDecorator < Draper::Decorator
delegate_all
def location return object.city + ', ' + object.state if object.city && object.state return object.city if object.city 'Unlisted' end end
Теперь, когда мы находимся в нашем представлении, мы больше не вызываем методы объекта User, а вызываем объект декоратора. Давайте докажем это, остановив сервер с помощью pry.
оригинал:
# Remember @users = [User, User, User, ...]
other_user.class == Пользователь
новый:
@users = [UserDecorator, UserDecorator, UserDecorator, ...]
other_user.class == UserDecorator.
после этого, когда мы запустим other_user.location
<ul>
<li>Position: <%= other_user.position %></li>
<li>School: <%= other_user.school %></li>
<li>Location: <%= other_user.location %></li>
</ul>
Метод location
, который мы определяем в UserDecorator
, называется:
# app/decorators/user_decorator.rb
class UserDecorator < Draper::Decorator delegate_all def location return object.city + ', ' + object.state if object.city && object.state return object.city if object.city 'Unlisted' end end
В результате мы исправили ошибку, обнаруженную в интерфейсе.
Использование шаблона проектирования декоратора в Ruby может облегчить нам жизнь несколькими способами. Вот несколько.
1. Мы не реализуем сложную в обслуживании условную логику во внешнем интерфейсе, чтобы обойти нулевые значения.
2. Мы не добавляем логику в нашу модель ActiveRecord User, потому что это поведение не касается нашей модели, а скорее представления нашего объекта конечному пользователю.
3. Мы избегаем вспомогательных методов, вместо этого передаем логику декоратору, используя шаблон проектирования, который можно снова использовать позже. Это также помогает нам не загрязнять глобальное пространство имен, как это сделал бы вспомогательный метод.
Спасибо за прочтение!
Следите за новостями.
Поставьте отметку Нравится или прокомментируйте, чтобы получать уведомления о части 3, где мы пишем тесты с использованием гема RSpec 3.7, чтобы иметь уверенность, что наш метод ведет себя так, как мы ожидаем.