Input
All arguments that service must expect should be described through input
method.
If the service receives an argument that hasn't been described through input method, it will return an error.
Usage
The use of the arguments included in the service is done through the inputs
method or its inp
alias.
class UsersService::Create < ApplicationService::Base
input :nickname, type: String
# ...
def create!
outputs.user = User.create!(nickname: inputs.nickname)
# or
# outputs.user = User.create!(nickname: inp.nickname)
end
end
Options
Option type
This option is validation.
It will check if the value set to input
corresponds to the specified type (class).
The is_a?
method is used.
Always required to specify. May contain one or more classes.
class UsersService::Accept < ApplicationService::Base
input :user,
type: User
# ...
end
class ToggleService < ApplicationService::Base
input :flag,
type: [TrueClass, FalseClass]
# ...
end
Option required
This option is validation.
Checks that the value set to input
is not empty.
The present?
method is used to check if the value is not nil
or an empty string.
By default, required
is set to true
.
class UsersService::Create < ApplicationService::Base
input :first_name,
type: String
input :middle_name,
type: String,
required: false
input :last_name,
type: String
# ...
end
Option as
This option is not validation. Used to prepare the input argument. This option changes the name of the input within the service.
class NotificationService::Create < ApplicationService::Base
input :customer,
as: :user,
type: User
output :notification,
type: Notification
make :create_notification!
private
def create_notification!
outputs.notification = Notification.create!(user: inputs.user)
end
end
Option array
This option is validation.
It will check if the value set to input
is an array and corresponds to the specified type (class).
The is_a?
method is used. Works together with options type
and required
.
class PymentsService::Send < ApplicationService::Base
input :invoice_numbers,
type: String,
array: true
# ...
end
Option inclusion
This option is validation.
Checks that the value set in input
is in the specified array.
The include?
method is used.
class EventService::Send < ApplicationService::Base
input :event_name,
type: String,
inclusion: %w[created rejected approved]
# ...
end
Option must
This option is validation.
Unlike other validation options, must
allows to describe the validation internally.
class PymentsService::Send < ApplicationService::Base
input :invoice_numbers,
type: String,
array: true,
must: {
be_6_characters: {
is: ->(value:) { value.all? { |id| id.size == 6 } }
}
}
# ...
end
Option prepare
This option is not validation. Used to prepare the value of the input argument.
Use the prepare
option carefully and only for simple actions.
class PymentsService::Send < ApplicationService::Base
input :amount_cents,
as: :amount,
type: Integer,
prepare: ->(value:) { Money.new(cents: value, currency: :USD) }
# then `inputs.balance` is used in the service
# ...
end
Helpers
Helper optional
This helper is equivalent to required: false
.
class UsersService::Create < ApplicationService::Base
input :first_name,
type: String
input :middle_name,
:optional,
type: String
input :last_name,
type: String
# ...
end
Helper as_array
This helper is equivalent to array: true
.
class PymentsService::Send < ApplicationService::Base
input :invoice_numbers,
:as_array,
type: String
# ...
end
Custom
It is possible to add custom helpers.
It is based on the must
and prepare
options.
Adding is done via the input_option_helpers
method in configuration
.
Example with must
class PymentsService::Send < ApplicationService::Base
input :invoice_numbers,
:must_be_6_characters,
type: String,
array: true
# ...
end
Example with prepare
class PymentsService::Send < ApplicationService::Base
input :amount_cents,
:to_money,
as: :amount,
type: Integer
# ...
end
Predicate methods
Every input has a method with a question mark. The data processing logic can be found here.
input :first_name, type: String
# ...
def something
return unless inputs.user? # instead of `inputs.user.present?`
# ...
end
Advanced mode
Advanced mode provides more detailed work with the option.
Option required
input :first_name,
type: String,
required: {
is: true,
message: "Input `first_name` is required"
}
input :first_name,
type: String,
required: {
message: lambda do |service_class_name:, input:, value:|
"Input `first_name` is required"
end
}
Option inclusion
input :event_name,
type: String,
inclusion: {
in: %w[created rejected approved]
}
input :event_name,
type: String,
inclusion: {
in: %w[created rejected approved],
message: lambda do |service_class_name:, input:, value:|
value.present? ? "Incorrect `event_name` specified: `#{value}`" : "Event name not specified"
end
}
Option must
The must
option can work only in advanced mode.
input :invoice_numbers,
type: String,
array: true,
must: {
be_6_characters: {
is: ->(value:) { value.all? { |id| id.size == 6 } }
}
}
input :invoice_numbers,
type: String,
array: true,
must: {
be_6_characters: {
is: ->(value:) { value.all? { |id| id.size == 6 } },
message: lambda do |service_class_name:, input:, value:, code:|
"Wrong IDs in `#{input.name}`"
end
}
}