Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sample Service Base Class and Result Class
class UserController < ActionController::Base
def new
@user = User.new
end
def create
result = SignupUser.call(params[:user])
if result.successful?
redirect_to root_url, notice: "A welcome e-mail is on it's way!"
else
@user = result.user
render
end
end
end
class SignupUser < Service::Call
attribute :name, String
attribute :email, String
attribute :wants_email, Boolean, default: ->(signup_user, attribute) { true }
class Result < Service::Result
attribute :user, User
end
def call
user = User.new(name: name, email: email, accepts_email: wants_email)
result.user = user
if user.save
UserMailer.welcome_email(user.email).deliver
result.successful!
else
result.add_errors(user)
end
result
end
end
class Service::Call
include Virtus.model
attribute :result, Service::Result, default: ->(service,_) { service.build_result }
def self.call(*args)
new(*args).call
end
protected
def build_result
klass = self.class.const_get("Result") rescue Service::Result
klass.new
end
def with_exception_catching
begin
yield
rescue StandardError => exception
result.exception = exception
result.add_errors(exception.record) if exception.respond_to?(:record)
end
end
end
class Service::Result
include Virtus.model
attribute :status, String, default: Status::ERROR
attribute :errors, Object, default: ->(sr,_) { ActiveModel::Errors.new(sr) }
attribute :exception, StandardError
module Status
SUCCESSFUL = 'successful'
ERROR = 'error'
end
def successful!; self.status = Status::SUCCESSFUL; self; end
def successful?; self.status == Status::SUCCESSFUL; end
def error!; self.status = Status::ERROR; self; end
def error?; self.status == Status::ERROR; end
def add_error_message(message)
errors.add(:base, message)
end
def add_errors(obj_with_errors)
Array(obj_with_errors).each do |obj|
self.errors.messages.merge!(obj.errors) unless obj.errors.empty?
end
end
def add_errors_or_be_successful!(obj_with_errors)
add_errors(obj_with_errors)
successful! if errors.empty?
self
end
end
@mattapayne

This comment has been minimized.

Copy link

commented May 29, 2014

Hey Adam,

Just FYI, I had to have Service::Result extend ActiveModel::Translation in Rails 4.x in order for stuff like add_errors to work properly. I was getting "undefined method `human_attribute_name'". Just thought I'd add that here in case it helps someone out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.