Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] - Add call! method to halt the execution of the current service #7

Merged
merged 1 commit into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Metrics/BlockLength:
- 'sea_food.gemspec'
- config/**/*
- spec/**/*
ExcludedMethods:
AllowedMethods:
- class_methods

Metrics/BlockNesting:
Expand Down Expand Up @@ -97,9 +97,14 @@ Style/MissingRespondToMissing:
Exclude:
- 'lib/sea_food/service.rb'

Style/MethodMissingSuper:
Lint/MissingSuper:
Exclude:
- 'lib/sea_food/service.rb'
- spec/**/*

Naming/PredicateName:
Enabled: false
Enabled: false

Lint/ConstantDefinitionInBlock:
Exclude:
- 'spec/**/*'
18 changes: 15 additions & 3 deletions lib/sea_food/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ class << self
# @param args [Hash] Arguments to pass to the service.
# @return [ServiceResult] The result of the service call.
def call(params = {})
# debugger
service = new(**params)
service.call
service.result || ServiceResult.new
rescue ServiceError => e
service.result
service.result || e.try(:result)
end

def call!(params = {})
result = call(params)
return result || ServiceResult.new unless result.fail?

raise ServiceError, result
end
end

Expand Down Expand Up @@ -113,7 +119,13 @@ def method_missing(key)
end
end

class ServiceError < StandardError; end
class ServiceError < StandardError
attr_reader :result

def initialize(result)
@result = result
end
end

private

Expand Down
3 changes: 2 additions & 1 deletion sea_food.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
spec.summary = 'A Ruby gem for seamlessly integrating form and service object patterns.'
spec.homepage = 'https://github.com/eagerworks/sea_food'
spec.license = 'MIT'
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')

# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
# to allow pushing to a single host or delete this section to allow pushing to any host.
Expand Down Expand Up @@ -43,6 +44,6 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'debug'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'rubocop', '~> 0.80.0'
spec.add_development_dependency 'rubocop', '~> 1.0'
spec.add_development_dependency 'sqlite3', '~> 1.5.0'
end
117 changes: 107 additions & 10 deletions spec/sea_food/service/service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def call
# Test enforcing the interface of the service
######

context "when the configuration is set to enforce the interface" do
context 'when the configuration is set to enforce the interface' do
before do
SeaFood.configure do |config|
config.enforce_interface = true
Expand All @@ -79,8 +79,7 @@ def call

it 'rasies an error when the #initialize method is not implemented' do
TestService = Class.new(SeaFood::Service) do
def call
end
def call; end
end

expect { TestService.call }.to raise_error(
Expand Down Expand Up @@ -148,9 +147,9 @@ def call; end
expect(result.email).to be_nil
end

######
# Test the difference behavior of #success #fail #fail!
######
######
# Test the difference behavior of #success #fail #fail!
######

it 'call #success twice' do
TestSuccessService = Class.new(SeaFood::Service) do
Expand Down Expand Up @@ -208,11 +207,10 @@ def call

it '#fail then #success' do
TestFailService = Class.new(SeaFood::Service) do

def initialize(email:)
@email = email
end

def call
fail(email: '[email protected]')
success(email: @email)
Expand All @@ -230,10 +228,10 @@ def call
def initialize(email:)
@email = email
end

def call
fail!(email: '[email protected]')
success!(email: @email)
success(email: @email)
end
end

Expand All @@ -242,5 +240,104 @@ def call
expect(result).to be_fail
expect(result.email).to eq('[email protected]')
end

context 'testing nested services with call' do
it 'it fails on the first service but not on the second one' do
TestInnerFailService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
fail!(email: @email)
end
end

TestParentFailService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
TestInnerFailService.call(email: '[email protected]')
success(email: @email)
end
end

result = TestParentFailService.call(email: '[email protected]')

expect(result).to be_success
expect(result.email).to eq('[email protected]')
end
end

context 'testing nested services with call!' do
it 'it fails on the first service so it fails the second one' do
TestInnerFailService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
fail!(email: @email)
end
end

TestParentFailService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
TestInnerFailService.call!(email: '[email protected]')
success(email: @email)
end
end

result = TestParentFailService.call(email: '[email protected]')
expect(result).to be_fail
expect(result.email).to eq('[email protected]')
end
end

context 'testing three levels nested services with call!' do
it 'it fails on the third level service but does not cascade' do
TestThirdFailService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
fail!(email: @email)
end
end

TestSecondFailService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
TestInnerFailService.call!(email: '[email protected]')
success(email: @email)
end
end

TestFirstService = Class.new(SeaFood::Service) do
def initialize(email:)
@email = email
end

def call
TestInnerFailService.call(email: 'a')
success(email: @email)
end
end

result = TestFirstService.call(email: '[email protected]')
expect(result).to be_success
expect(result.email).to eq('[email protected]')
end
end
end
end
10 changes: 5 additions & 5 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'

require 'sea_food'
load File.dirname(__FILE__) + '/support/schema.rb'
require File.dirname(__FILE__) + '/support/form/user_form.rb'
require File.dirname(__FILE__) + '/support/form/address_form.rb'
require File.dirname(__FILE__) + '/support/user.rb'
require File.dirname(__FILE__) + '/support/address.rb'
load "#{File.dirname(__FILE__)}/support/schema.rb"
require "#{File.dirname(__FILE__)}/support/form/user_form.rb"
require "#{File.dirname(__FILE__)}/support/form/address_form.rb"
require "#{File.dirname(__FILE__)}/support/user.rb"
require "#{File.dirname(__FILE__)}/support/address.rb"

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
Expand Down
Loading