### Installing Max Option Helper Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Provides examples for installing the `Servactory::ToolKit::DynamicOptions::Max` option helper for input, internal, and output attributes. ```ruby input_option_helpers([ Servactory::ToolKit::DynamicOptions::Max.use ]) internal_option_helpers([ Servactory::ToolKit::DynamicOptions::Max.use(:maximum) ]) output_option_helpers([ Servactory::ToolKit::DynamicOptions::Max.use ]) ``` -------------------------------- ### Installing Min Option Helper Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Shows how to install the `Servactory::ToolKit::DynamicOptions::Min` option helper for input, internal, and output attributes. ```ruby input_option_helpers([ Servactory::ToolKit::DynamicOptions::Min.use ]) internal_option_helpers([ Servactory::ToolKit::DynamicOptions::Min.use(:minimum) ]) output_option_helpers([ Servactory::ToolKit::DynamicOptions::Min.use ]) ``` -------------------------------- ### Bundle Install Source: https://github.com/servactory/servactory.com/blob/main/docs/featury/getting-started.md Execute the bundle install command in your terminal to install the Featury gem. ```shell bundle install ``` -------------------------------- ### Run Development Server Source: https://github.com/servactory/servactory.com/blob/main/README.md Starts the local development server for the documentation. ```bash yarn docs:dev ``` -------------------------------- ### Install and Use `target` Option Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Shows how to install the `target` option for input, internal, and output helpers. The `target` option validates that a value is a specific class or one of a list of allowed classes. ```ruby input_option_helpers([ Servactory::ToolKit::DynamicOptions::Target.use ]) internal_option_helpers([ Servactory::ToolKit::DynamicOptions::Target.use(:expect) ]) output_option_helpers([ Servactory::ToolKit::DynamicOptions::Target.use ]) ``` ```ruby input :service_class, type: Class, target: MyFirstService internal :service_class, type: Class, expect: { in: [MyFirstService, MySecondService] } output :service_class, type: Class, target: { in: [MyFirstService, MySecondService], message: lambda do |output:, value:, option_value:, **| "Output `#{output.name}`: #{value.inspect} is not allowed. " \ "Allowed: #{Array(option_value).map(&:name).join(', ')}" end } ``` -------------------------------- ### Install Dependencies Source: https://github.com/servactory/servactory.com/blob/main/README.md Installs the necessary project dependencies using Yarn. ```bash yarn install ``` -------------------------------- ### Example Service Definition Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/legacy.md A sample service definition demonstrating inputs, outputs, and internal logic. ```ruby class Users::Create < ApplicationService::Base input :first_name, type: String input :middle_name, type: String, required: false input :last_name, type: String output :full_name, type: String make :assign_full_name private def assign_full_name outputs.full_name = [ inputs.first_name, inputs.middle_name, inputs.last_name ].compact.join(" ") end end ``` -------------------------------- ### Install and Use `multiple_of` Option Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Demonstrates how to install the `multiple_of` option for input, internal, and output helpers. The `multiple_of` option validates that a value is a multiple of a specified number. ```ruby input_option_helpers([ Servactory::ToolKit::DynamicOptions::MultipleOf.use ]) internal_option_helpers([ Servactory::ToolKit::DynamicOptions::MultipleOf.use(:divisible_by) ]) output_option_helpers([ Servactory::ToolKit::DynamicOptions::MultipleOf.use ]) ``` ```ruby input :data, type: Integer, multiple_of: 2 internal :data, type: Integer, divisible_by: { is: 2 } output :data, type: Float, multiple_of: { is: 2, message: lambda do |output:, value:, option_value:, **| "Output `#{output.name}` has the value `#{value}`, " \ "which is not a multiple of `#{option_value}`" end } ``` -------------------------------- ### Use Simple Mode `action_shortcuts` Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md Example demonstrating how `assign` and `perform` shortcuts are used to define methods like `assign_model` and `perform_request`. ```ruby class CMS::API::Posts::Create < CMS::API::Base # ... assign :model perform :request private def assign_model # Build model for API request end def perform_request # Perform API request end # ... end ``` -------------------------------- ### Usage Example for Authorization Extension Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Demonstrates how to use the `authorize_with` DSL method in a service to specify an authorization check. ```ruby class Posts::Delete < ApplicationService::Base input :post, type: Post input :user, type: User authorize_with :user_can_delete? make :delete_post private def user_can_delete?(args) args[:user].admin? || args[:post].author_id == args[:user].id end def delete_post inputs.post.destroy! end end ``` -------------------------------- ### Install Featury Gem Source: https://github.com/servactory/servactory.com/blob/main/docs/featury/getting-started.md Add the Featury gem to your Gemfile and run bundle install to include it in your project. ```ruby gem "featury" ``` -------------------------------- ### Generate Servactory Installation Files Source: https://github.com/servactory/servactory.com/blob/main/docs/getting-started.md Run the servactory:install Rails generator to automatically create necessary files for Servactory integration. ```shell bundle exec rails g servactory:install ``` -------------------------------- ### Install RSpec testing helpers Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/fluent.md Include the RSpec testing helpers and matchers in your `rails_helper.rb` file. ```ruby require "servactory/test_kit/rspec/helpers" require "servactory/test_kit/rspec/matchers" ``` ```ruby RSpec.configure do |config| config.include Servactory::TestKit::Rspec::Helpers config.include Servactory::TestKit::Rspec::Matchers # ... end ``` -------------------------------- ### Usage Example for Transactional Extension Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Demonstrates how to use the `transactional!` DSL method to wrap a service's execution in a database transaction. ```ruby class Orders::Create < ApplicationService::Base transactional! transaction_class: ActiveRecord::Base input :user, type: User input :items, type: Array output :order, type: Order make :create_order make :create_line_items make :charge_payment private def create_order outputs.order = Order.create!(user: inputs.user) end def create_line_items inputs.items.each do |item| outputs.order.line_items.create!(item) end end def charge_payment Payments::Charge.call!(amount: outputs.order.total_amount) end end ``` -------------------------------- ### Install Servactory Base Infrastructure Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/rails/generators.md Sets up the base service infrastructure for Servactory. Use the `--namespace` option to specify a custom base namespace and `--path` to define the output directory. ```shell bundle exec rails g servactory:install ``` ```shell bundle exec rails g servactory:install --namespace=MyApp::Services ``` ```shell bundle exec rails g servactory:install --path=lib/services ``` -------------------------------- ### Writing Extension Settings Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Configure extension settings within the `ClassMethods` of your service. This example shows how to set the authorization method name. ```ruby def authorize_with(method_name) stroma.settings[:actions][:authorization][:method_name] = method_name end ``` -------------------------------- ### Reading Extension Settings Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Access extension settings within the `InstanceMethods` of your service. This example retrieves the configured authorization method name. ```ruby def call!(**) method_name = self.class.stroma.settings[:actions][:authorization][:method_name] # ... super end ``` -------------------------------- ### Add Custom Input Option Helper for 'prepare' Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md Define a custom helper for the 'prepare' option on inputs. This example converts input values to Money objects. ```ruby module ApplicationService class Base < Servactory::Base configuration do input_option_helpers( [ Servactory::Maintenance::Attributes::OptionHelper.new( name: :to_money, equivalent: { prepare: ->(value:) { Money.from_cents(value, :USD) } } ) ] ) end end end ``` -------------------------------- ### Input validation error example Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/fluent.md When using `with`, the helper validates that specified inputs exist in the service. This example shows an error for an unknown input. ```ruby # Raises ValidationError: unknown_input is not defined in ServiceClass allow_service!(ServiceClass) .with(unknown_input: "value") .succeeds(result: "ok") ``` -------------------------------- ### Prepare Input Value with `prepare` Option Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/usage.md Use the `prepare` option for simple preparatory actions on input values. Complex logic should be handled by `make` actions. This example shows preparing an integer cents value into a Money object. ```ruby class Payments::Create < ApplicationService::Base input :amount_cents, as: :amount, type: Integer, prepare: ->(value:) { Money.from_cents(value, :USD) } # ... def create! outputs.payment = Payment.create!(amount: inputs.amount) end end ``` -------------------------------- ### ApplicationService::Exceptions::Output Exception Details Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md This example shows the structure of an `ApplicationService::Exceptions::Output` exception, illustrating the information available such as the service instance, detailed message, output name, and meta data. ```ruby exception.service # => exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Output) exception.message # => Invalid invoice number exception.output_name # => :invoice_number exception.meta # => {:received_invoice_number=>"BB-7650AE"} ``` -------------------------------- ### Call a Service and Get a Successful Result Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/result.md Demonstrates calling a service and receiving a successful result object. The result object will have `success?` as true and `failure?` as false. ```ruby service_result = Users::Accept.call(user: User.first) ``` ```ruby # => ``` -------------------------------- ### ApplicationService::Exceptions::Input Exception Details Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md This example shows the structure of an `ApplicationService::Exceptions::Input` exception, illustrating the information available such as the service instance, detailed message, input name, and meta data. ```ruby exception.service # => exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Input) exception.message # => Invalid invoice number exception.input_name # => :invoice_number exception.meta # => {:received_invoice_number=>"BB-7650AE"} ``` -------------------------------- ### Usage of Publishable Extension Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Example of a service class using the publishable extension to declare that a 'user_created' event should be published with a specific payload after the service executes. Requires defining input, output, and make steps. ```ruby class Users::Create < ApplicationService::Base publishes :user_created, with: :user_payload, event_bus: EventPublisher input :email, type: String input :name, type: String output :user, type: User make :create_user private def create_user outputs.user = User.create!(email: inputs.email, name: inputs.name) end def user_payload { user_id: outputs.user.id, email: outputs.user.email } end end ``` -------------------------------- ### Custom Input Helper: Prepare to Money Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/attributes/input.md Use a custom helper like `:to_money` to transform an input attribute, aliasing it with `as:` if needed. This example prepares an `amount_cents` integer into a money format. ```ruby class Payments::Create < ApplicationService::Base input :amount_cents, :to_money, as: :amount, type: Integer # ... end ``` -------------------------------- ### Fail Output Example Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md Use `fail_output!` to throw an exception for an output attribute. It accepts the attribute name, a message, and optional meta information. This will raise an `ApplicationService::Exceptions::Output` exception. ```ruby make :check! def check! return if outputs.invoice_number.start_with?("AA") fail_output!( :invoice_number, message: "Invalid invoice number", meta: { received_invoice_number: outputs.invoice_number } ) end ``` -------------------------------- ### ApplicationService::Exceptions::Internal Exception Details Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md This example shows the structure of an `ApplicationService::Exceptions::Internal` exception, illustrating the information available such as the service instance, detailed message, internal name, and meta data. ```ruby exception.service # => exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Internal) exception.message # => Invalid invoice number exception.internal_name # => :invoice_number exception.meta # => {:received_invoice_number=>"BB-7650AE"} ``` -------------------------------- ### Output validation error for unknown output Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/fluent.md The helper validates that specified outputs exist in the service. This example shows an error for an unknown output. ```ruby # Raises ValidationError: unknown_output is not defined in ServiceClass allow_service!(ServiceClass) .succeeds(unknown_output: "value") ``` -------------------------------- ### Process Service Result Using `success?` Method Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/result.md Example of processing a service result by checking the `success?` method. If the service did not succeed, it fails again with a custom message and meta information. ```ruby service_result = Notifications::Slack::Error::Send.call(...) return if service_result.success? fail!( message: "The message was not sent to Slack", meta: { reason: service_result.error.message } ) ``` -------------------------------- ### Add Custom Output Option Helper for 'must' Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md Define a custom helper for the 'must' option on outputs. This example validates that output values adhere to a specific format or length. ```ruby module ApplicationService class Base < Servactory::Base configuration do output_option_helpers( [ Servactory::Maintenance::Attributes::OptionHelper.new( name: :must_be_6_characters, equivalent: { must: { be_6_characters: { is: ->(value:, output:) { value.all? { |id| id.size == 6 } }, message: lambda do |output:, **| "Wrong IDs in `#{output.name}`" end } } } ) ] ) end end end ``` -------------------------------- ### Method `describe` Source: https://github.com/servactory/servactory.com/blob/main/docs/datory/guide/info.md Use the `describe` method to get a human-readable description of the Datory object's attributes, including their source and target representations. ```APIDOC ## Method `describe` ### Description Provides a formatted, human-readable table describing the attributes of a Datory object, including source and target mappings. ### Method Signature `SerialDto.describe` ### Request Example ```ruby SerialDto.describe ``` ### Response Example ```text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | SerialDto | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Attribute | From | To | As | Include | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | id | String | id | String | | | status | String | status | String | | | title | String | title | String | | | poster | [ImageDto, Hash] | poster | [ImageDto, Hash] | ImageDto | | ratings | [RatingsDto, Hash] | ratings | [RatingsDto, Hash] | RatingsDto | | countries | Array | countries | Array | CountryDto | | genres | Array | genres | Array | GenreDto | | seasons | Array | seasons | Array | SeasonDto | | premieredOn | String | premiered_on | Date | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` ``` -------------------------------- ### Configure Inclusion for Input Attribute Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/advanced.md The `inclusion` option restricts the allowed values for an attribute to a predefined set. This example shows configuration for an input attribute. ```ruby input :event_name, type: String, inclusion: { in: %w[created rejected approved] } ``` -------------------------------- ### Fail Input Example Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md Use `fail_input!` to throw an exception for an input attribute. It accepts the attribute name, a message, and optional meta information. This will raise an `ApplicationService::Exceptions::Input` exception. ```ruby make :check! def check! return if inputs.invoice_number.start_with?("AA") fail_input!( :invoice_number, message: "Invalid invoice number", meta: { received_invoice_number: inputs.invoice_number } ) end ``` -------------------------------- ### Use Extension in a Service Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Apply the custom extension's functionality within a service by calling its DSL method. This example shows how to use the 'status_active!' method provided by the extension. ```ruby class Posts::Create < ApplicationService::Base input :user, type: User input :title, type: String status_active! :user make :create_post private def create_post # ... end end ``` -------------------------------- ### Define ApplicationFeature Base Class with ActiveRecord Source: https://github.com/servactory/servactory.com/blob/main/docs/featury/getting-started.md Create a base feature class that inherits from Featury::Base. This example demonstrates defining actions like enabled?, disabled?, enable, and disable using an ActiveRecord model for feature flags. It also includes before and after hooks for notifications. ```ruby module ApplicationFeature class Base < Featury::Base action :enabled? do |features:, **options| features.all? do |feature| FeatureFlag .find_or_create_by!(code: feature, actor: options[:user]) .enabled? end end action :disabled? do |features:, **options| features.any? do |feature| !FeatureFlag .find_or_create_by!(code: feature, actor: options[:user]) .enabled? end end action :enable do |features:, **options| features.all? do |feature| FeatureFlag .find_or_create_by!(code: feature, actor: options[:user]) .update!(enabled: true) end end action :disable do |features:, **options| features.all? do |feature| FeatureFlag .find_or_create_by!(code: feature, actor: options[:user]) .update!(enabled: false) end end before do |action:, features:| Slack::API::Notify.call!(action: action, features: features) end after :enabled?, :disabled? do |action:, features:| Slack::API::Notify.call!(action: action, features: features) end end end ``` -------------------------------- ### Generate RSpec Test File for a Service Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/rails/generators.md Creates an RSpec test file for a given service. If inputs are provided, they are used to generate spec examples. For existing services, it generates a basic spec structure. ```shell bundle exec rails g servactory:rspec users/create first_name last_name email ``` ```shell bundle exec rails g servactory:rspec orders/process ``` -------------------------------- ### Using Simple Action Shortcuts Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/actions/usage.md Applies the configured simple action shortcuts (`assign`, `perform`) to shorten the `make` calls and improve readability. ```ruby class CMS::API::Posts::Create < CMS::API::Base # ... assign :model perform :request private def assign_model # Build model for API request end def perform_request # Perform API request end # ... end ``` -------------------------------- ### Configure Simple Mode `action_shortcuts` Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md In simple mode, `action_shortcuts` accepts an array of symbols. These symbols become prefixes for instance methods, providing shortcuts for common `make` patterns. ```ruby module ApplicationService class Base < Servactory::Base configuration do action_shortcuts( %i[assign perform] ) end end end ``` -------------------------------- ### Get Featury Object Info Source: https://github.com/servactory/servactory.com/blob/main/docs/featury/guide/info.md Retrieve information about a Featury object. This is the entry point for accessing feature flag details. ```ruby info = User::OnboardingFeature.info ``` -------------------------------- ### Service with Multiple Actions Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/actions/usage.md Demonstrates defining and calling multiple sequential actions within a service using the `make` method for each step. ```ruby class Posts::Create < ApplicationService::Base # ... make :assign_api_model make :perform_api_request make :process_result def assign_api_model internals.api_model = APIModel.new(...) end def perform_api_request internals.response = APIClient.resource.create(internals.api_model) end def process_result ARModel.create!(internals.response) end end ``` -------------------------------- ### Define a Complex Service with Inputs, Internals, and Outputs Source: https://github.com/servactory/servactory.com/blob/main/docs/introduction.md Build a sophisticated service with defined inputs, internal states, and outputs. Use 'make' to define the sequence of operations. ```ruby class Notifications::Send < ApplicationService::Base input :comment, type: Comment input :provider, type: NotificationProvider internal :user, type: User internal :status, type: String internal :response, type: NotificatorApi::Models::Notification output :notification, type: Notification make :assign_user make :assign_status make :create_notification! make :send_notification make :update_notification! make :update_comment! make :assign_status private def assign_user internals.user = inputs.comment.user end def assign_status internals.status = StatusEnum::NOTIFIED end def create_notification! outputs.notification = Notification.create!(user:, comment: inputs.comment, provider: inputs.provider) end def send_notification service_result = Notifications::API::Send.call(notification: outputs.notification) return fail!(message: service_result.error.message) if service_result.failure? internals.response = service_result.response end def update_notification! outputs.notification.update!(original_data: internals.response) end def update_comment! inputs.comment.update!(status: internals.status) end end ``` -------------------------------- ### Customizing Email and Adding Invoice Format Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Demonstrates how to customize existing formats like 'email' and add new custom formats such as 'invoice' using the `use` method for the `Servactory::ToolKit::DynamicOptions::Format` option. ```ruby Servactory::ToolKit::DynamicOptions::Format.use( formats: { email: { pattern: /@/, validator: ->(value:) { value.present? } }, invoice: { pattern: /^([A]{2})-([0-9A-Z]{6})$/, validator: ->(value:) { value.present? } } } ) ``` -------------------------------- ### Access service input attributes Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/info.md Get a list of all defined input attributes for a service. This is useful for validating or understanding the expected parameters of a service. ```ruby BuildFullName.info.inputs # => [:first_name, :middle_name, :last_name] ``` -------------------------------- ### Create a Minimal Service Source: https://github.com/servactory/servactory.com/blob/main/docs/introduction.md Define a basic service class inheriting from ApplicationService::Base. Use this for straightforward operations. ```ruby class MinimalService < ApplicationService::Base def call # ... end end ``` -------------------------------- ### Simple Action Shortcut Configuration Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/actions/usage.md Configures `action_shortcuts` with an array of symbols for simple mode, enabling shorter `make` lines by defining common prefixes. ```ruby configuration do action_shortcuts %i[assign perform] end ``` -------------------------------- ### Minimal `fail!` Example Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md Use `fail!` with only a message to raise a `Servactory::Exceptions::Failure` with the default `:base` type. This is suitable for general validation failures. ```ruby make :check! def check! return if inputs.invoice_number.start_with?("AA") fail!(message: "Invalid invoice number") end ``` -------------------------------- ### RSpec Service Test Structure Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/legacy.md The overall structure for testing a service using RSpec, including setup and nested describe blocks for validations and execution contexts. ```ruby RSpec.describe Users::Create, type: :service do describe ".call!" do subject(:perform) { described_class.call!(**attributes) } let(:attributes) do { first_name: first_name, middle_name: middle_name, last_name: last_name } end let(:first_name) { "John" } let(:middle_name) { "Fitzgerald" } let(:last_name) { "Kennedy" } describe "validations" do describe "inputs" do it do expect { perform }.to( have_input(:first_name) .valid_with(attributes) .type(String) .required ) end it do expect { perform }.to( have_input(:middle_name) .valid_with(attributes) .type(String) .optional ) end it do expect { perform }.to( have_input(:last_name) .valid_with(attributes) .type(String) .required ) end end describe "outputs" do it do expect(perform).to( have_output(:full_name) .instance_of(String) ) end end end context "when required data for work is valid" do it { expect(perform).to be_success_service } it do expect(perform).to( have_output(:full_name) .contains("John Fitzgerald Kennedy") ) end describe "even if `middle_name` is not specified" do let(:middle_name) { nil } it do expect(perform).to( have_output(:full_name) .contains("John Kennedy") ) end end end end end ``` -------------------------------- ### Example of a Failed Result Object Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/result.md Illustrates the structure of a failed result object. It includes an `@error` attribute detailing the reason for failure and has `success?` as false and `failure?` as true. ```ruby # => ``` -------------------------------- ### Generate New Service with Typed Inputs Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/rails/generators.md Creates a new service with typed inputs. Supports type shortcuts for common types like String, Integer, Boolean, Array, and Hash. Use nested namespaces by separating directory levels with '/'. ```shell bundle exec rails g servactory:service users/create ``` ```shell bundle exec rails g servactory:service orders/process user:User amount:integer ``` ```shell bundle exec rails g servactory:service admin/reports/generate started_on:date ended_on:date ``` -------------------------------- ### Output validation error for type mismatch Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/fluent.md The helper validates that specified outputs match expected types. This example shows an error when the output type does not match the expected type. ```ruby # Raises ValidationError: order_number expects Integer, got String allow_service!(ServiceClass) .succeeds(order_number: "not_an_integer") ``` -------------------------------- ### Using Min Option for Output Attribute Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Shows how to apply the `min` option to an output attribute, defining a minimum size of 1 for an array and providing a custom error message. ```ruby output :data, type: Array, min: { is: 1, message: lambda do |output:, value:, option_value:, **| "The size of the `#{output.name}` value must be greater than or " \ "equal to `#{option_value}` (got: `#{value}`)" end } ``` -------------------------------- ### Add Custom Internal Option Helper for 'must' Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md Define a custom helper for the 'must' option on internal attributes. This example ensures internal values meet a specific length requirement. ```ruby module ApplicationService class Base < Servactory::Base configuration do internal_option_helpers( [ Servactory::Maintenance::Attributes::OptionHelper.new( name: :must_be_6_characters, equivalent: { must: { be_6_characters: { is: ->(value:, internal:) { value.all? { |id| id.size == 6 } }, message: lambda do |internal:, **| "Wrong IDs in `#{internal.name}`" end } } } ) ] ) end end end ``` -------------------------------- ### Add Custom Input Option Helper for 'must' Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md Define a custom helper for the 'must' option on inputs. This example enforces that all IDs in an input must be exactly 6 characters long. ```ruby module ApplicationService class Base < Servactory::Base configuration do input_option_helpers( [ Servactory::Maintenance::Attributes::OptionHelper.new( name: :must_be_6_characters, equivalent: { must: { be_6_characters: { is: ->(value:, input:) { value.all? { |id| id.size == 6 } }, message: lambda do |input:, **| "Wrong IDs in `#{input.name}`" end } } } ) ] ) end end end ``` -------------------------------- ### Fail Internal Example Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/exceptions/failure.md Use `fail_internal!` to throw an exception for an internal attribute. It accepts the attribute name, a message, and optional meta information. This will raise an `ApplicationService::Exceptions::Internal` exception. ```ruby make :check! def check! return if internals.invoice_number.start_with?("AA") fail_internal!( :invoice_number, message: "Invalid invoice number", meta: { received_invoice_number: internals.invoice_number } ) end ``` -------------------------------- ### Define Before and After Hooks Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Use the `extensions` block to define `before` and `after` hooks for service actions. ```ruby class ApplicationService::Base < Servactory::Base extensions do before :actions, ApplicationService::Extensions::Authorization::DSL after :actions, ApplicationService::Extensions::Publishable::DSL end end ``` -------------------------------- ### Connect Extension to Base Service Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Integrate a custom extension into your base service by using the 'extensions' block and specifying the 'before' hook. This ensures the extension's logic runs at the desired stage. ```ruby module ApplicationService class Base < Servactory::Base extensions do before :actions, ApplicationService::Extensions::StatusActive::DSL end end end ``` -------------------------------- ### Configure Advanced Mode `action_shortcuts` Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/configuration.md In advanced mode, `action_shortcuts` accepts a hash for more complex shortcut definitions, including custom prefixes and suffixes for generated methods. ```ruby module ApplicationService class Base < Servactory::Base configuration do action_shortcuts( %i[assign], { restrict: { # replacement for make prefix: :create, # method name prefix suffix: :restriction # method name suffix } } ) end end end ``` -------------------------------- ### Configure Extended Action Shortcuts Source: https://github.com/servactory/servactory.com/blob/main/docs/releases/2.14.md Use this to configure extended mode for `action_shortcuts`, allowing for custom prefixes and suffixes for generated methods. ```ruby configuration do action_shortcuts( { restrict: { # replacement for make prefix: :create, # method name prefix suffix: :restriction # method name suffix } } ) end ``` -------------------------------- ### Get Datory Object Information Source: https://github.com/servactory/servactory.com/blob/main/docs/datory/guide/info.md Use the `info` method to retrieve detailed information about a Datory object. This method returns a structured object containing attribute details from both 'from' and 'to' perspectives. ```ruby SerialDto.info ``` ```text # {:from=>{:name=>:id, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>:uuid}, :to=>{:name=>:id, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>:uuid, :required=>true, :default=>nil, :include=>nil}}, :status=> {:from=>{:name=>:status, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>nil}, :to=>{:name=>:status, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>nil, :required=>true, :default=>nil, :include=>nil}}, :title=> {:from=>{:name=>:title, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>nil}, :to=>{:name=>:title, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>nil, :required=>true, :default=>nil, :include=>nil}}, :poster=> {:from=>{:name=>:poster, :type=>[ImageDto, Hash], :min=>nil, :max=>nil, :consists_of=>false, :format=>nil}, :to=>{:name=>:poster, :type=>[ImageDto, Hash], :min=>nil, :max=>nil, :consists_of=>false, :format=>nil, :required=>true, :default=>nil, :include=>ImageDto}}, :ratings=> {:from=>{:name=>:ratings, :type=>[RatingsDto, Hash], :min=>nil, :max=>nil, :consists_of=>false, :format=>nil}, :to=>{:name=>:ratings, :type=>[RatingsDto, Hash], :min=>nil, :max=>nil, :consists_of=>false, :format=>nil, :required=>true, :default=>nil, :include=>RatingsDto}}, :countries=> {:from=>{:name=>:countries, :type=>Array, :min=>nil, :max=>nil, :consists_of=>[CountryDto, Hash], :format=>nil}, :to=>{:name=>:countries, :type=>Array, :min=>nil, :max=>nil, :consists_of=>[CountryDto, Hash], :format=>nil, :required=>true, :default=>nil, :include=>CountryDto}}, :genres=> {:from=>{:name=>:genres, :type=>Array, :min=>nil, :max=>nil, :consists_of=>[GenreDto, Hash], :format=>nil}, :to=>{:name=>:genres, :type=>Array, :min=>nil, :max=>nil, :consists_of=>[GenreDto, Hash], :format=>nil, :required=>true, :default=>nil, :include=>GenreDto}}, :seasons=> {:from=>{:name=>:seasons, :type=>Array, :min=>nil, :max=>nil, :consists_of=>[SeasonDto, Hash], :format=>nil}, :to=>{:name=>:seasons, :type=>Array, :min=>nil, :max=>nil, :consists_of=>[SeasonDto, Hash], :format=>nil, :required=>true, :default=>nil, :include=>SeasonDto}}, :premieredOn=> {:from=>{:name=>:premieredOn, :type=>String, :min=>nil, :max=>nil, :consists_of=>false, :format=>:date}, :to=>{:name=>:premiered_on, :type=>Date, :min=>nil, :max=>nil, :consists_of=>false, :format=>nil, :required=>true, :default=>nil, :include=>nil}}}> ``` -------------------------------- ### Migrate Basic Success Mock Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/migration.md Use `allow_service!.succeeds(output)` to mock a service that is expected to succeed with specific output. ```ruby before do allow_service_as_success!(Users::Create) do { user: user } end end ``` ```ruby before do allow_service!(Users::Create) .succeeds(user: user) end ``` -------------------------------- ### Custom Internal Validation with `must` Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/usage.md Define custom validation logic for internal service attributes using the `must` option. This example ensures all internal invoice numbers are exactly 6 characters long. ```ruby class Events::Send < ApplicationService::Base # ... internal :invoice_numbers, type: Array, consists_of: String, must: { be_6_characters: { is: ->(value:, internal:) { value.all? { |id| id.size == 6 } } } } # ... end ``` -------------------------------- ### Using Format Option for Output Attribute Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Shows how to use the `format` option for an output attribute, specifying the 'email' format and providing a custom error message. ```ruby output :data, type: String, format: { is: :email, message: lambda do |output:, value:, option_value:, **| "Incorrect `email` format in `#{output.name}`" end } ``` -------------------------------- ### Migrate Extension Syntax: 3.x vs 2.x Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Use the `extensions do` block for defining extensions in 3.x, replacing the deprecated `Servactory::DSL.with_extensions` method used in 2.x. ```ruby module ApplicationService class Base < Servactory::Base extensions do before :actions, ApplicationService::Extensions::StatusActive::DSL end end end ``` ```ruby module ApplicationService class Base include Servactory::DSL.with_extensions( ApplicationService::Extensions::StatusActive::DSL ) end end ``` -------------------------------- ### Migrate Success Mock with Input Matching Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/testing/rspec/migration.md Use `allow_service!(Service).with(input).succeeds(output)` to mock a service that must receive specific input and succeed with given output. ```ruby before do allow_service_as_success!(PaymentService, with: { amount: 100 }) do { transaction_id: "txn_123" } end end ``` ```ruby before do allow_service!(PaymentService) .with(amount: 100) .succeeds(transaction_id: "txn_123") end ``` -------------------------------- ### Create Base Form Class Source: https://github.com/servactory/servactory.com/blob/main/docs/datory/getting-started.md Prepare a base class for your forms by inheriting from Datory::Base. ```ruby module ApplicationForm class Base < Datory::Base end end ``` -------------------------------- ### Dynamic Input Options: Format Source: https://github.com/servactory/servactory.com/blob/main/docs/releases/2.4.md Use the `format` option to specify expected data formats for inputs like email and password. ```ruby input :email, type: String, format: :email ``` ```ruby input :password, type: String, format: :password ``` -------------------------------- ### Using Min Option for Input Attribute Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Illustrates how to use the `min` option for an input attribute, specifying a minimum value of 1 for an integer type. ```ruby input :data, type: Integer, min: 1 ``` -------------------------------- ### Define and use internal attributes in a service Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/attributes/internal.md Define internal attributes using the `internal` method and access them via `internals=` and `internals` methods. This example shows setting and using an internal `full_name` attribute. ```ruby class Users::Create < ApplicationService::Base input :first_name, type: String input :middle_name, type: String input :last_name, type: String internal :full_name, type: String output :user, type: User make :assign_full_name make :create! def assign_full_name internals.full_name = [ inputs.first_name, inputs.middle_name, inputs.last_name ].join(" ") end def create! outputs.user = User.create!(full_name: internals.full_name) end end ``` -------------------------------- ### Usage of Rollbackable Extension Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md Example of a service class using the rollbackable extension to define a cleanup method that will be executed if any exception (other than Servactory::Exceptions::Success) is raised during the service's execution. This ensures resources are released or transactions are reverted. ```ruby class Payments::Process < ApplicationService::Base on_rollback :cleanup_resources input :order, type: Order input :payment_method, type: PaymentMethod output :payment, type: Payment make :reserve_inventory make :charge_payment make :confirm_order private def reserve_inventory Inventory::Reserve.call!(items: inputs.order.items) end def charge_payment result = Payments::Charge.call!( payment_method: inputs.payment_method, amount: inputs.order.total_amount ) outputs.payment = result.payment end def confirm_order inputs.order.confirm! end def cleanup_resources Inventory::Release.call!(items: inputs.order.items) Payments::Refund.call!(payment: outputs.payment) if outputs.payment.present? end end ``` -------------------------------- ### Define a service with inputs, internals, and outputs Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/info.md Define a service with various types of attributes including inputs, internal variables, and outputs. This sets up the structure for service operations. ```ruby class BuildFullName < ApplicationService::Base input :first_name, type: String input :middle_name, type: String, required: false input :last_name, type: String internal :prepared_full_name, type: String output :full_name, type: String # ... end ``` -------------------------------- ### Dynamic Input Options: Min/Max Source: https://github.com/servactory/servactory.com/blob/main/docs/releases/2.4.md Use `min` and `max` options to set numerical constraints for inputs, such as page numbers and sizes. ```ruby input :page_number, type: Integer, min: 1 ``` ```ruby input :page_size, type: Integer, min: 1, max: 20 ``` -------------------------------- ### Describe Datory Object Schema Source: https://github.com/servactory/servactory.com/blob/main/docs/datory/guide/info.md Use the `describe` method to get a human-readable, tabular summary of the Datory object's schema. It outlines attribute names, their source types, target types, and inclusion details for nested objects. ```ruby SerialDto.describe ``` ```text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | SerialDto | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Attribute | From | To | As | Include | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | id | String | id | String | | | status | String | status | String | | | title | String | title | String | | | poster | [ImageDto, Hash] | poster | [ImageDto, Hash] | ImageDto | | | ratings | [RatingsDto, Hash] | ratings | [RatingsDto, Hash] | RatingsDto | | | countries | Array | countries | Array | CountryDto | | genres | Array | genres | Array | GenreDto | | seasons | Array | seasons | Array | SeasonDto | | premieredOn | String | premiered_on | Date | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` -------------------------------- ### Prepare and Validate Data with `form` Source: https://github.com/servactory/servactory.com/blob/main/docs/datory/guide/usage/serialization.md Use the `form` method to prepare data for validation and serialization. Pass the prepared data to this method. It returns a form object that can be used for validation checks. ```ruby form = SerialDto.form(serial) ``` -------------------------------- ### Define Multiple Before and After Hooks Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/extensions.md You can define multiple `before` and `after` hooks, which will execute in the order they are declared. ```ruby class ApplicationService::Base < Servactory::Base extensions do # Before hooks (execute in order) before :actions, ApplicationService::Extensions::Authorization::DSL before :actions, ApplicationService::Extensions::StatusActive::DSL # After hooks (execute in order) after :actions, ApplicationService::Extensions::Publishable::DSL after :actions, ApplicationService::Extensions::PostCondition::DSL end end ``` -------------------------------- ### Call service with .call! (Ruby) Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/call.md Use `.call!` to execute a service and automatically raise an exception if any problem occurs during execution. This is useful when you expect the service to succeed and want immediate feedback on failures. ```ruby Users::Accept.call!(user: User.first) ``` ```ruby # => # ``` ```ruby # => ApplicationService::Exceptions::Input: [Users::Accept] Required input `user` is missing # => ApplicationService::Exceptions::Failure: There is some problem with the user ``` -------------------------------- ### Create Base DTO Class Source: https://github.com/servactory/servactory.com/blob/main/docs/datory/getting-started.md Prepare a base class for your Data Transfer Objects by inheriting from Datory::Base. ```ruby module ApplicationDTO class Base < Datory::Base end end ``` -------------------------------- ### Using Max Option for Output Attribute Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/options/dynamic.md Shows how to apply the `max` option to an output attribute, setting a maximum size of 10 for an array and defining a custom error message. ```ruby output :data, type: Array, max: { is: 10, message: lambda do |output:, value:, option_value:, **| "The size of the `#{output.name}` value must be less than or " \ "equal to `#{option_value}` (got: `#{value}`)" end } ``` -------------------------------- ### Process Service Result Using `failure?` Method (Default Type) Source: https://github.com/servactory/servactory.com/blob/main/docs/guide/usage/result.md Shows how to use the `failure?` method to check if a service result failed. If it failed, it proceeds to fail again with specific error details. ```ruby service_result = Notifications::Slack::Error::Send.call(...) return unless service_result.failure? fail!( message: "The message was not sent to Slack", meta: { reason: service_result.error.message } ) ```