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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
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.