Last updated
class GreetService < Operandi::Base
# Arguments
arg :name, type: String
arg :age, type: Integer
# Steps
step :build_message
step :send_message
# Outputs
output :message, type: String
private
def build_message
self.message = "Hello, #{name}! You are #{age} years old."
end
def send_message
# Send logic goes here
end
endclass User::ResetPassword < Operandi::Base
# Arguments with Sorbet types
arg :user, type: User, optional: true
arg :email, type: String, optional: true
arg :send_email, type: T::Boolean, default: true
arg :metadata, type: T::Hash[Symbol, String], default: {}
arg :notify_channels, type: T::Array[Symbol], default: [:email]
# Steps
step :validate
step :find_user, unless: :user?
step :generate_reset_token
step :save_reset_token
step :send_reset_email, if: :send_email?
# Outputs
output :user, type: User
output :reset_token, type: String
output :notifications_sent, type: T::Array[Symbol]
private
def validate
errors.add(:base, "user or email is required") if !user? && !email?
end
def find_user
self.user = User.find_by("LOWER(email) = ?", email.downcase)
errors.add(:email, "not found") unless user
end
def generate_reset_token
self.reset_token = SecureRandom.hex(32)
end
def save_reset_token
user.update!(
reset_password_token: reset_token,
reset_password_sent_at: Time.current,
)
rescue ActiveRecord::RecordInvalid => e
errors.from_record(e.record)
end
def send_reset_email
Mailer::SendEmail
.with(self) # Call sub-service with the same context
.run(template: :reset_password, user:, reset_token:)
end
end