controllers (or any other business logic) and the persistence layer » Service Objects (Rails) » Use Case Objects/Interactor (Hanami) » Operations (Trailblazer)
def initialize(params) @params = params @user = User.new(@params) end # THIS WON'T BE INVOKED BECAUSE #valid? WILL RETURN false def call @user = UserRepository.persist(@user) end private # super always return true # It's desined to be overriden in each child class def valid? false end end
passed to the #call method, only to #new.1 » cause of very limited Dependecy Injection » Interactor instance cannot be reused with other parameters 1 This will be changed in hanami-utils 1.1.0. See this PR for more details.
assign_checklist checklist_details = params[:task][:checklist] checklist = current_user.organisation.checklists.find(checklist_details[:id]) tasks = Task.transaction do checklist.items.map do |description| task = scope.new(resource_params.merge(description: description)) authorize! :create, task unless task.save render_errors(task) raise ActiveRecord::Rollback.new end task end end if tasks.present? render json: TaskMapper.all_as_json(tasks, scope: current_user) end end end
Checklist item » Authorize creation of each task » Task resources are all the same (passed on resource_params), except for description that is set to the Checklist item » Handles database transaction rollback if any Task fails to be saved » Handles rendering of errors or tasks depending if any error happened or not
for whole action » Pass params, and any other argument necessary, to a service that handle the actual creation work » Handle rendering of errors or tasks based on service result
authorize! :create_from_checklist, Task # resource_params comes from our private API interactor = ChecklistTasksFactory.new(checklist_params, resource_params, current_user) # ... end private def checklist_params params.require(:task).permit(checklist: [:id]) end end
def initialize(checklist_params, task_params, current_user) @checklist = current_user.organisation.checklists.find(checklist_params[:id]) @task_params = task_params @scope = current_user.organisation.tasks @tasks = [] end def call @tasks = Task.transaction do @checklist.items.map do |description| build_task(description) save_task! @task end end end private def build_task(description) @task = @scope.new(@task_params.merge(description: description)) end def save_task! return if @task.save add_error(@task) raise ActiveRecord::Rollback end end
authorize! :create_from_checklist, Task # resource_params comes from our private API interactor = ChecklistTasksFactory.new(checklist_params, resource_params, current_user) result = interactor.call if result.successful? # result.tasks will raise NoMethodError as tasks is not exposed by the interactor render json: TaskMapper.all_as_json(result.tasks, scope: current_user) else # Interactor::Result exposes #errors by default render_errors(result.errors) end end private def checklist_params params.require(:task).permit(checklist: [:id]) end end