Telephone ☎️
Telephone is a light weight utility that helps you create and call service objects from anywhere within your application.
Telepone comes with a simple interface that helps with:
- Keeping your code DRY
- Encapsulating complex logic in a more readable way
- Making your code easier to test
- Gracefully handling errors and validations
Installation
Add this line to your application's Gemfile:
gem 'telephone'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install telephone
Usage
Telephone::Service
is a simple utility class for creating and calling service objects. It allows you to define arguments and validations, and provides
a simple interface for calling the service and determining its success.
To start, define an ApplicationService
and inherit from Telephone::Service
.
# /app/services/application_service.rb
class ApplicationService < Telephone::Service
end
A very simple example of a service object:
class SimpleExample < ApplicationService
def call
"Hello World"
end
end
s = SimpleExample.call #=> <#SimpleExample @result="Hello World">
s.success? #=> true
s.result #=> "Hello World"
Arguments
You can define arguments for the service object. These arguments will be passed to the service object's call
method, and will be available as an attribute.
class SimpleExample < ApplicationService
argument :name
def call
"Hello, #{name}."
end
end
SimpleExample.call(name: "Benjamin").result #=> "Hello, Benjamin."
Arguments can also be required, which will prevent the service object from executing unless they are present.
class SimpleExample < ApplicationService
argument :name, required: true
def call
"Hello, #{name}."
end
end
s = SimpleExample.call
s.success? #=> false
s.errors.full_messages #=> ["Name can't be blank"]
s.result #=> nil
You can also give a default value for an argument.
argument :name, default: "Benjamin"
Validations
Since Telephone::Service
includes ActiveModel::Model
, you can define validations in the same way you would for an ActiveRecord model.
validates :name, format: { with: /\A[a-zA-Z]+\z/ }
validate :admin_user?
def admin_user?
errors.add(:user, "not admin") unless user.admin?
end
If a validation fails, the service object will not execute and return nil
as the result of th call. You can check the status of the service object by calling success?
.
s = SomeService.call
s.success? #=> false
Best Practices
Service objects are a great way to keep your code DRY and encapsulate business logic. As a rule of thumb, try to keep your service objects to a single responsibility. If you find yourself dealing with very complex logic, consider breaking it up into smaller services.
Development
After checking out the repo, run bin/setup
to install dependencies. This will install the dependencies for each subgem and run the entire test suite.
To experiment, you can run bin/console
for an interactive prompt.
Documentation
This project is documented using YARD. All code should be well documented via the YARD syntax prior to merging.
You can access the docmentation by starting up the YARD server.
yard server --reload
The --reload
, or -r
, flag tells the server to auto reload the documentation on each request.
Once the server is running, the documentation will by available at http://localhost:8808