mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-02 12:26:37 +08:00
The `base.rb` file (640+ lines) contained 9 step classes, the `Context` class, and the `StepsHelpers` DSL module alongside the core concern. Each is now in its own file under `lib/service/base/`, following the existing `lib/service.rb` + `lib/service/*.rb` pattern. Also: - Update stale inline documentation in `lib/service.rb` - Use `NotImplementedError` instead of generic raise in `PolicyBase` and `ActionBase` - Move YARD DSL docs to a `@!parse` block at the top of `base.rb`
97 lines
3.7 KiB
Ruby
97 lines
3.7 KiB
Ruby
# frozen_string_literal: true
|
||
|
||
module Service
|
||
# Module to be included to provide steps DSL to any class. This allows to
|
||
# create easy to understand services as the whole service cycle is visible
|
||
# simply by reading the beginning of its class.
|
||
#
|
||
# Steps are executed in the order they’re defined. They will use their name
|
||
# to execute the corresponding method defined in the service class.
|
||
#
|
||
# Available step types:
|
||
#
|
||
# * +params(name = :default)+: validates input parameters using
|
||
# +ActiveModel+ validations. Fails if the contract is invalid.
|
||
# The resulting contract is available in +context[:params]+.
|
||
# * +model(name = :model)+: fetches or instantiates a model. Fails if the
|
||
# return value is falsy, empty, or invalid. The result is stored in
|
||
# +context[name]+ (+context[:model]+ by default).
|
||
# * +policy(name = :default)+: performs a check on the state of the system.
|
||
# Typically used to run guardians. Fails if the return value is falsy.
|
||
# * +step(name)+: runs arbitrary code. Does not fail based on its return
|
||
# value; call {#fail!} explicitly to mark the service as failed.
|
||
# * +transaction+: wraps other steps inside a DB transaction. Any failing
|
||
# step inside the block causes a rollback.
|
||
# * +try(*exceptions)+: wraps other steps and catches specified exceptions
|
||
# (defaults to +StandardError+). Fails if an exception is caught.
|
||
# * +lock(*keys)+: wraps other steps inside a +DistributedMutex+. Keys are
|
||
# resolved from params then the service context. Fails if the lock
|
||
# cannot be acquired.
|
||
# * +options+: defines options to parameterize service behavior, similar to
|
||
# +params+ but without validations. Cannot fail.
|
||
# * +only_if(name)+: conditionally runs the steps in its block. If the
|
||
# condition is not met, the steps are skipped without failing.
|
||
#
|
||
# The methods defined on the service are automatically provided with
|
||
# the whole context passed as keyword arguments. This allows to define in a
|
||
# very explicit way what dependencies are used by the method. If for
|
||
# whatever reason a key isn’t found in the current context, then Ruby will
|
||
# raise an exception when the method is called.
|
||
#
|
||
# Regarding contract classes, they automatically have {ActiveModel} modules
|
||
# included so all the {ActiveModel} API is available.
|
||
#
|
||
# @example An example from the {TrashChannel} service
|
||
# class TrashChannel
|
||
# include Service::Base
|
||
#
|
||
# model :channel
|
||
# policy :invalid_access
|
||
# transaction do
|
||
# step :prevents_slug_collision
|
||
# step :soft_delete_channel
|
||
# step :log_channel_deletion
|
||
# end
|
||
# step :enqueue_delete_channel_relations_job
|
||
#
|
||
# private
|
||
#
|
||
# def fetch_channel(channel_id:)
|
||
# Chat::Channel.find_by(id: channel_id)
|
||
# end
|
||
#
|
||
# def invalid_access(guardian:, channel:)
|
||
# guardian.can_preview_chat_channel?(channel) && guardian.can_delete_chat_channel?
|
||
# end
|
||
#
|
||
# def prevents_slug_collision(channel:)
|
||
# …
|
||
# end
|
||
#
|
||
# def soft_delete_channel(guardian:, channel:)
|
||
# …
|
||
# end
|
||
#
|
||
# def log_channel_deletion(guardian:, channel:)
|
||
# …
|
||
# end
|
||
#
|
||
# def enqueue_delete_channel_relations_job(channel:)
|
||
# …
|
||
# end
|
||
# end
|
||
# @example An example from the {UpdateChannelStatus} service which uses a contract
|
||
# class UpdateChannelStatus
|
||
# include Service::Base
|
||
#
|
||
# model :channel
|
||
# params do
|
||
# attribute :status
|
||
# validates :status, inclusion: { in: Chat::Channel.editable_statuses.keys }
|
||
# end
|
||
# policy :check_channel_permission
|
||
# step :change_status
|
||
#
|
||
# …
|
||
# end
|
||
end
|