CivilService
CivilService is a tiny framework for service objects in Rails apps. With CivilService, you can use ActiveModel validations to do pre-flight checks before the service runs, and create your own result object classes to capture the results of complex operations.
CivilService was extracted from Intercode, a web app for convention organizers and participants.
Installation
Add this line to your application's Gemfile:
gem 'civil_service'
And then execute:
$ bundle
Or install it yourself as:
$ gem install civil_service
What CivilService does
CivilService::Service is really a pretty tiny class. It does, however, have some opinions that create a potentially-useful abstraction for app developers:
- When called, services always return a result object that responds to (at least)
#success?
,#failure?
, and#errors
. This lets your code paths that call services be consistent and simple. (If you want to return more information as a result of running the service, it's easy to define a custom result class for your service.) - Services include
ActiveModel::Validations
so they can easily do pre-flight checks. That means you can callmy_service.valid?
andmy_service.errors
just like you can for a model, and it also means that the service will fail if it's not valid.
Basic example
Here's a simple service that changes a user's password in a hypothetical Rails app, and sends a notification email about it:
class PasswordChangeService < CivilService::Service
validate :ensure_valid_password
attr_reader :user, :new_password
def initialize(user:, new_password:)
@user = user
@new_password = new_password
end
private
def inner_call
user.update!(password: new_password)
UserMailer.password_changed(user).deliver_later
success
end
def ensure_valid_password
return if new_password.length >= 8
errors.add(:base, "Passwords must be at least 8 characters long")
end
end
You might call this from a controller action like this:
class UsersController < ApplicationController
def change_password
service = PasswordChangeService.new(user: current_user, new_password: params[:password])
result = service.call
if result.success?
redirect_to root_url, notice: "Your password has been changed."
else
flash[:alert] = result.errors..join(', ')
end
end
end
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
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/neinteractiveliterature/civil_service.