Sebastian

Version circleci

Sebastian makes it easy for you to have service objects in Ruby. It gives you a place to put your business logic. It also helps you write safer code by validating that your inputs conform to your expectations.

Installation

Add this line to your Gemfile:

gem 'sebastian'

Or install it manually:

$ gem install sebastian

Usage

To define a service object, create a subclass of Sebastian::Base. Then you need to do two things:

  1. Define your attributes.
  2. Define your business logic. Do this by implementing the #execute method. Each attribute you defined will be available. If any of the attributes are invalid, #execute won't be run.

That covers the basics. Let's put it all together into a simple example that squares a number.

class CreatePayment < Sebastian::Base
  param :amount
  param :email

  validates :amount, numericality: { only_integer: true }

  private

  def execute
    'payment_created' if create_payment
    errors.add(:payment)
  end

  def create_payment
    Payment.create(customer: create_customer, amount: amount)
  end

  def create_customer
    Customer.create(email: email)
  end
end

Call .perform on your service to execute it. You must pass a single hash to .perform. It will return an instance of your service. By convention, we call this an result. You can use the #ok? method to ask the result if it's ok. If it's not ok, take a look at its errors with #errors. When #ok?, the value returned from #execute will be stored in #value.

result = CreatePayment.perform(email: '[email protected]', amount: 500)
result.ok?
# => true
result.value
# => "payment_created"

result = CreatePayment.perform(amount: 500)
result.ok?
# => false
result.errors.messages
# => {:payment=>["is not a valid"]}
result.value!
# => Sebastian::InvalidResultError: Payment is not valid

You can also use .perform! to execute. It's like .perform but more dangerous. It doesn't return an result. If the result would be invalid, it will instead raise an error. But if the result would be valid, it simply returns the value.

CreatePayment.perform!(email: '[email protected]', amount: 500)
# => "payment_created"
CreatePayment.perform!(amount: 500)
# => Sebastian::InvalidResultError: Payment is not valid

Validations

These validations aren't provided by Sebastian. They're from ActiveModel. You can also use any custom validations you wrote yourself in your services.

class ValidatePayment < Sebastian::Validation
  params :amount, :email

  validates :email, presence: true
  validates :amount, numericality: { only_integer: true }
end

This works the same as Sebastian::Base except that it is not needed to specify #execute.

result = ValidatePayment.perform(amount: '5,00')
result.ok?
# => false
result.errors.messages
# => {{:email=>["can't be blank"], :amount=>["is not a number"]}

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/paypronl/sebastian.

License

The gem is available as open source under the terms of the MIT License.