Skip to main content
Version: 1.9

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.

caution

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.

Configuration example

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

note

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
}
}