Ruby on Rails
Организация классов
Поиск…
замечания
Это похоже на простое дело, но когда вы начинаете занятия в классе, вы будете благодарны, что потратили время на их организацию.
Модельный класс
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates :user, presence: true
validates :title, presence: true, length: { in: 6..40 }
scope :topic, -> (topic) { joins(:topics).where(topic: topic) }
before_save :update_slug
after_create :send_welcome_email
def publish!
update(published_at: Time.now, published: true)
end
def self.find_by_slug(slug)
find_by(slug: slug)
end
private
def update_slug
self.slug = title.join('-')
end
def send_welcome_email
WelcomeMailer.welcome(self).deliver_now
end
end
Модели обычно отвечают за:
- установление отношений
- проверка достоверности данных
- обеспечение доступа к данным по областям и методам
- Выполнение действий по сохранению данных.
На самом высоком уровне модели описывают концепции домена и управляют их персистентностью.
Класс обслуживания
Контроллер является точкой входа в наше приложение. Однако это не единственная возможная точка входа. Я хотел бы получить доступ к моей логике:
- Рейк-задачи
- фоновые задания
- приставка
- тесты
Если я введу свою логику в контроллер, она не будет доступна из всех этих мест. Итак, давайте попробуем «тощий контроллер, толстая модель» и переместим логику к модели. Но какой? Если данный фрагмент логики включает модели User
, Cart
и Product
- где он должен жить?
Класс, который наследует от ActiveRecord::Base
уже имеет много обязанностей. Он обрабатывает интерфейс запроса, ассоциации и проверки. Если вы добавите еще больше кода в свою модель, он быстро станет недостижимым беспорядком с сотнями общественных методов.
Служба - это обычный объект Ruby. Его класс не должен наследовать ни от какого конкретного класса. Его имя - это глагольная фраза, например CreateUserAccount
а не UserCreation
или UserCreationService
. Он живет в каталоге приложений / сервисов. Вы должны создать этот каталог самостоятельно, но Rails будет автоматически загружать классы внутри вас.
Объект службы делает одну вещь
Объект службы (объект метода) выполняет одно действие. Он выполняет бизнес-логику для выполнения этого действия. Вот пример:
# app/services/accept_invite.rb
class AcceptInvite
def self.call(invite, user)
invite.accept!(user)
UserMailer.invite_accepted(invite).deliver
end
end
Ниже приведены три конвенции:
Услуги идут под app/services directory
. Я рекомендую вам использовать подкаталоги для бизнес-логики. Например:
- Файл
app/services/invite/accept.rb
будет определятьInvite::Accept
покаapp/services/invite/create.rb
определитInvite::Create
- Услуги начинаются с глагола (и не заканчиваются Сервисом):
ApproveTransaction
,SendTestNewsletter
,ImportUsersFromCsv
- Услуги отвечают на метод
call
. Я обнаружил, что использование другого глагола делает его немного избыточным:ApproveTransaction.approve()
не читается хорошо. Кромеcall
методcall
является де-факто-методом для объектовlambda
,procs
и методов.
Выгоды
Объекты службы показывают, что делает мое приложение
Я могу просто взглянуть на каталог служб, чтобы узнать, что делает мое приложение: ApproveTransaction
, CancelTransaction
, BlockAccount
, SendTransactionApprovalReminder
...
Быстрый взгляд на объект службы, и я знаю, что такое бизнес-логика. Мне не нужно проходить через контроллеры, обратные вызовы модели ActiveRecord
и наблюдатели, чтобы понять, что подразумевает «одобрение транзакции».
Модифицированные модели и контроллеры
Контроллеры превращают запрос (params, session, cookies) в аргументы, передают их службе и перенаправляют или обрабатывают в соответствии с ответом службы.
class InviteController < ApplicationController
def accept
invite = Invite.find_by_token!(params[:token])
if AcceptInvite.call(invite, current_user)
redirect_to invite.item, notice: "Welcome!"
else
redirect_to '/', alert: "Oopsy!"
end
end
end
Модели имеют дело только с ассоциациями, областями, валидациями и настойчивостью.
class Invite < ActiveRecord::Base
def accept!(user, time=Time.now)
update_attributes!(
accepted_by_user_id: user.id,
accepted_at: time
)
end
end
Это упрощает тестирование и обслуживание моделей и контроллеров!
Когда использовать класс обслуживания
Достижение для объектов службы, когда действие соответствует одному или нескольким из следующих критериев:
- Действие является сложным (например, закрытие книг в конце отчетного периода)
- Действие распространяется на несколько моделей (например, покупка электронной коммерции с использованием объектов Order, CreditCard и Customer)
- Действие взаимодействует с внешней службой (например, публикация в социальных сетях)
- Действие не является основной проблемой базовой модели (например, сбрасывание устаревших данных через определенный период времени).
- Существует несколько способов выполнения действия (например, аутентификация с помощью токена доступа или пароля).
источники