Логика небольших приложений может присутствовать как серия транзакций. Используя шаблон сценариев транзакции, мы получаем приложение, которое легче поддерживать, чтобы покрыть тестами и масштабированием.
Настройка проблемы
Есть ruby на приложении API Rails, который имеет пост, пользователь и как модели. Пользователи должны быть в состоянии понравиться сообщения.
Давайте добавим контроллер, который обрабатывает кнопки, нажмите на кнопки.
module Api class LikesController < ApplicationController def update @user = User.find(params['user_id']) @post = Post.find(params['post_id']) like = @post.likes.find_or_initialize_by(user: @user) if like.persisted? like.destroy! render json: { isLiked: false } else like.save! render json: { isLiked: true } end end end end
Очевидные недостатки этого кода являются:
- Контроллер реализует бизнес-логику, хотя контроллеры должны быть только координировать взаимодействие между пользователем, представлениями и моделью.
- Чтобы покрыть этот код тестами, вам необходимо проверить все действие контроллера. Чтобы проверить контроллер, вам обычно нужно делать больше предварительных условий.
- Если у вас есть два контроллера для любит, вам нужно повторить код.
Первые тесты
Давайте напишем тест и, таким образом, решить две проблемы:
- Определите интерфейс обслуживания.
- Получите готовый тест.
require 'test_helper' class ToggleLikeActionScriptTest < ActiveSupport::TestCase def setup @user = users(:john_doe) @post = posts(:lorem_ipsum) end def test_it_creates_like result = ToggleLikeActionScript.new(@user, @post).perform assert result.value assert @user.likes.where(post: @post).present? end def test_it_destroys_like @user.likes.create(post: @post) result = ToggleLikeActionScript.new(@user, @post).perform assert_not result.value assert @user.likes.where(post: @post).blank? end def test_it_show_errros_on_fail_case result = ToggleLikeActionScript.new(@user, nil).perform assert_nil result.value assert_not_empty result.errors end end
Теперь у вас есть тест, и вы можете внедрить услугу.
Реализация услуг
Из тестов вы можете увидеть, что сервис наиболее тесно следует за Шаблон сценария транзакции или вид сервисного объекта.
Сценарий транзакции должен соответствовать единственному принципу ответственности.
class ToggleLikeActionScript Result = Struct.new(:success?, :errors, :value) def initialize(user, post) @user = user @post = post end def perform like = @post.likes.find_or_initialize_by(user: @user) if like.persisted? like.destroy! Result.new(true, [], false) else like.save! Result.new(true, [], true) end rescue StandardError => e Result.new(false, [e], nil) end end
Итак, у нас есть услуга и тест. Давайте очистим контроллер:
module Api class LikesController < ApplicationController def update @user = User.find(params["user_id"]) @post = Post.find(params["post_id"]) result = ToggleLikeActionScript.new(@user, @post).perform if result.success? render json: { isLiked: result.value } else render json: { errors: result.errors } end end end end
Как видите, наш контроллер теперь выглядел чище. « Toggle, как «функциональность» теперь берет только одну строку и по имени класса, мы можем сразу понять, что происходит в скрипте транзакции.
Рекомендуемая структура сценария транзакции
- Метод инициализации с входящими аргументами.
- Одиночный общественный метод, который запускает действие.
- Отключите с успехом? и либо полезная нагрузка, либо ошибка в результате. (Этот предмет желательно, но не требуется)
Когда использовать сценарии транзакции
- Действие сложно
- Действие включает несколько моделей
- Используя внутренние услуги
- Когда вы собираетесь добавить обратный вызов ActiveRecord, но только для одного случая
Плюсы использования сценариев транзакции
- Контроллер не имеет дело с бизнес-логикой
- Код может быть повторно использован без дублирования
- Проще тестировать, репродукция тестовой среды не сложно
- Структура приложений становится очистителем и легче поддерживать
Оригинальный пост: https://jtway.co/how-to-use-a-transaction-script-aka-service-objects-in-ruby-on-rails-simple-example-hample-61b7e228942.
Оригинал: “https://dev.to/jetthoughts/how-to-use-a-transaction-script-aka-service-objects-in-ruby-on-rails-simple-example-3ll8”