Light Services
Light Services is a simple yet powerful way to organize your business logic. This Ruby gem helps you build services that are easy to test, maintain, and understand.
QuickstartFeatures
- π§© Simple: Define your service as a class with arguments,steps, andoutputs
- π’ Transactions: Automatically rollback database changes if any step fails
- π΅ Inheritance: Inherit from other services to reuse logic seamlessly
- π¨ Error Handling: Collect errors from steps and handle them your way
- βοΈ Context: Run multiple services sequentially within the same context
- π€ Framework Agnostic: Compatible with Rails, Hanami, or any Ruby framework
- ποΈ Modularity: Isolate and test your services with ease
- π 100% Test Coverage: Bugs are not welcome here!
- π‘οΈ Battle-Tested: In production use since 2017
Simple Example
class GreetService < Light::Services::Base
  # Arguments
  arg :name
  arg :age
  # Steps
  step :build_message
  step :send_message
  # Outputs
  output :message
  private
  def build_message
    self.message = "Hello, #{name}! You are #{age} years old."
  end
  def send_message
    # Send logic goes here
  end
end
Advanced Example
class User::ResetPassword < Light::Services::Base
  # Arguments
  arg :user, type: User, optional: true
  arg :email, type: :string, optional: true
  arg :send_email, type: :boolean, default: true
  # 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
  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
Do you like what you see? Get started with Light Services today!
Quickstart