Рассмотрим tasks#create
экшен. Из названия понятно, что он сохраняет новые таски. Если посмотреть на код, возникает подсознательное недоверие к коду из-за его количества.
Во первых, видно, что мешается логика экшена (authenticated?
) и логика сохранения, валидирования, вызова тасков в сайдкик. А так же, видно лишний метод #task_params
который приводит данные с экшена в данные, которые будут скормлены нашей модели.
Для начала, давайте сделаем прсотой интерактор CreateTask
:
require 'hanami/interactor'
module Interactors
class CreateTask
include Hanami::Interactor
def call
end
end
end
А так же вынесем логику создания, приведение данных к нужному виду и вызов воркера:
require 'hanami/interactor'
module Interactors
class CreateTask
include Hanami::Interactor
def initialize(params)
@params = params
end
def call
task = TaskRepository.new.create(task_params)
NewTaskNotificationWorker.perform_async(task.id)
end
private
def task_params
hash = @params[:task]
hash[:body] = Markdown.parse(hash[:md_body])
hash[:status] = Task::VALID_STATUSES[:in_progress]
hash[:approved] = nil
hash
end
end
end
И обновим контроллер:
module Web::Controllers::Tasks
class Create
include Web::Action
expose :task
params do
# ...
end
def call(params)
return unless authenticated?
if params.valid?
Interactors::CreateTask.new(params).call
flash[:info] = INFO_MESSAGE
redirect_to routes.tasks_path
else
@task = Task.new(params[:task])
self.body = Web::Views::Tasks::New.render(format: format, task: @task,
current_user: current_user, params: params, updated_csrf_token: set_csrf_token)
end
end
private
INFO_MESSAGE = 'Task had been added to moderation. You can check your task status on profile page'.freeze
end
end
Экшен стал чище, но валидация данных и создание таска в разных местах. Давайте исправим это:
require 'hanami/interactor'
module Interactors
class CreateTask
include Hanami::Interactor
expose :task # геттер, которым мы будем пользоваться в экшене
def initialize(params)
@params = params
end
def call
if @params.valid?
task = TaskRepository.new.create(task_params)
NewTaskNotificationWorker.perform_async(task.id)
else
@task = Task.new(@params[:task])
error('invalid task attributes') # кидаем ошибку, что бы изменить статус интерактора
end
end
private
def task_params
# ...
end
end
end
И опять обновляем наш экшен:
module Web::Controllers::Tasks
class Create
include Web::Action
expose :task
params do
# ...
end
def call(params)
return unless authenticated?
result = Interactors::CreateTask.new(params).call
if result.successful?
flash[:info] = INFO_MESSAGE
redirect_to routes.tasks_path
else
@task = result.task
self.body = Web::Views::Tasks::New.render(format: format, task: result.task,
current_user: current_user, params: params, updated_csrf_token: set_csrf_token)
end
end
private
INFO_MESSAGE = 'Task had been added to moderation. You can check your task status on profile page'.freeze
end
end
Что мы получили:
- больше не нужно переживать из-за сессии, что бы проверить создание таска в тестах;
- можно воспользоваться DI и протестировать экшен с
NullInteractor
который будет возвращать нужный результат без вызова бизнес логики и работы с BD; - убрав лишние методы в экшене, нужно думать, зачем нужен метод
task_params
и почему было именно так;