Last active
July 23, 2019 16:59
-
-
Save aitor/d93eade1226ba0abb1e005da64aa7cc8 to your computer and use it in GitHub Desktop.
Service / Usecase Objects in vanilla Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe AuthenticateManagerService do | |
subject(:context) { described_class.call(username, password) } | |
describe '.call' do | |
context 'when the context is successful' do | |
let(:username) { 'correct_user' } | |
let(:password) { 'magic' } | |
it 'succeeds' do | |
expect(context).to be_success | |
end | |
end | |
context 'when the context is not successful' do | |
let(:username) { 'correct_user' } | |
let(:password) { 'wrong_password' } | |
it 'fails' do | |
expect(context).to be_failure | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Api | |
module V1 | |
class AuthsController < ApplicationController | |
def create | |
execution = AuthenticateManagerService.call(manager_params[:email], manager_params[:password]) | |
if execution.success? | |
# We'll serialize this properly with a serializer | |
# ManagerSerializer.new(execution.result).serialized_json | |
render json: { data: execution.result } | |
else | |
render json: { error: execution.errors }, status: :unauthorized | |
end | |
end | |
def manager_params | |
params.require(:manager).permit(:email, :password) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> irb | |
irb(main):001:0> require_relative "service" | |
=> true | |
irb(main):002:0> exec = AuthenticateManagerService.call("fwefe", "magic") | |
=> #<AuthenticateManagerService:0x00007fb64b890ef0 @email="fwefe", @password="magic", @result={:email=>"fwefe", :authenticated=>true}> | |
irb(main):003:0> exec.success? | |
=> true | |
irb(main):004:0> exec.result | |
=> {:email=>"fwefe", :authenticated=>true} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/services/base_service.rb | |
class BaseService | |
attr_reader :result | |
def self.call(*args) | |
new(*args).call | |
end | |
def call | |
@result = nil | |
payload | |
self | |
end | |
def success? | |
errors.empty? | |
end | |
def errors | |
# IRL we may use ActiveModel::Errors, a thin Rails wrapper for a Hash | |
# cf. https://github.com/rails/rails/blob/master/activemodel/lib/active_model/errors.rb | |
# eg. @errors ||= ActiveModel::Errors.new(self) | |
# or roll our own simple Error class. | |
@errors ||= {} | |
end | |
private def initialize(*_) | |
raise NotImplementedError, "Implement `initialize` for classes inheriting BaseService" | |
end | |
private def payload | |
raise NotImplementedError, "Implement `payload` for classes inheriting BaseService" | |
end | |
end | |
# app/services/application_service.rb | |
class ApplicationService < BaseService | |
end | |
# app/services/authenticate_manager_service.rb | |
class AuthenticateManagerService < ApplicationService | |
private | |
attr_reader :email, :password | |
def initialize(email, password) | |
@email = email | |
@password = password | |
end | |
def password_valid? | |
password == "magic" | |
end | |
def payload | |
if password_valid? | |
# IRL we'll probably return an appropiate real object here and not a hash | |
# @result = user | |
@result = { email: email, authenticated: true } | |
else | |
# If we go with ActiveModel::Errors we will return something like this | |
# errors.add(:base, I18n.t('authenticate_user_command.invalid_credentials')) | |
errors[:base] = "INVALID CREDENTIALS" | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment