Utilisable
Simple gem with some useful classes, that will help you maintain DRY principles and keep your models and controllers thin.
Heavily inspired by rectify gem, but has lesser dependencies, smaller and has some differences (for example with listeners).
Service object
Interface for running services - a place to move your business logic.
Example:
class TestService < Utilisable::Service
attr_reader :user_id
def initialize(user_id)
@user_id = user_id
end
def call
return broadcast(:fail) unless user_valid?
do_some_stuff
book = user.books.last
user.delete
broadcast(:ok, book)
end
private
def user_valid?
user.name == 'Kyle'
end
def do_some_stuff
#..more_code_here..
end
def user
@user ||= User.find_by(id: user_id)
end
end
Results:
@user = User.last # Name is 'Kyle'
@loan = nil
TestService.call(@user.id)
# Will delete user and broadcast method won't take affect
TestService.call(@user.id) do |obj|
obj.on(:fail) { raise 'Some error' } # Listener for fail broadcast
obj.on(:ok) { |book| @book = book } # Listener for ok broadcast
end
# User will be deleted and @book variable will be assigned
@user.update(name: 'Stan')
TestService.call(@user.id) do |obj|
obj.on(:fail) { raise 'Some error' }
obj.on(:ok) { |book| @book = book }
end
# User won't be deleted and Some error' will be raised
Background service
Simple interface for running background jobs as services. Meant to be inherited like this:
class SidekiqService < Utilisable::BackgroundService
def initialize()
super(, ServiceObjectWorker)
end
end
Needs a sidekiq worker in order to work, that should look like this:
class ServiceObjectWorker
include Sidekiq::Worker
queue: :default, retry: true
def perform(klass, *args)
klass.constantize.call(args)
end
end
Now you can create a service like this:
class SimpleService < SidekiqService
attr_reader :foo
def initialize(foo, = {})
super()
@foo = foo
end
private
def perform
puts foo
end
end
And invoke it like this:
SimpleService.call('foo', background: true, perform_in: 15.minutes)
# Will set sidekiq worker to be performd in 15 minutes,
# that will print 'foo' in sidekiq console
Note that listeners won't work when service is being called as background job.
Form object
Form object is used to move validations from models. Usually used when similar model needs different validations on different forms. Can be used to build attributes for model to save.
Example:
class UserForm < Utilisable::Form
attribute :user_id, Integer
attribute :user_name, String
attribute :sibling_name, String, remove_from_hash: true
include_in_hash :sibling_id
validates :user_id, :user_name, presence: true
validate :is_kyle
private
def is_kyle
return true if user_name == 'Kyle'
errors.add(:user_name, 'should be Kyle')
end
def sibling_id
@sibling_id ||= User.find_by(name: sibling_name)
end
end
Results:
user_params = {user_id: 1, user_name: 'Kyle', sibling_name: 'John'}
form = UserForm.new(user_params)
form.valid? # true
form = UserForm.new(user_params.merge(user_name: 'Steve'))
form.valid? # false
form.errors. # ["User name should be Kyle"]
form = UserForm.new(user_params.merge(user_id: 'test'))
form.valid? # false
form.errors. # ["User can't be blank"]
form = UserForm.new(user_params.merge(user_id: '1')) # Will convert user_id into Integer
form.to_h # { user_id: 1, user_name: 'Kyle', sibling_id: 12 }