diff --git a/.rubocop.yml b/.rubocop.yml index 7caa745..d2ff9da 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -42,7 +42,7 @@ Metrics/BlockLength: - 'sea_food.gemspec' - config/**/* - spec/**/* - ExcludedMethods: + AllowedMethods: - class_methods Metrics/BlockNesting: @@ -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 \ No newline at end of file + Enabled: false + +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/**/*' \ No newline at end of file diff --git a/lib/sea_food/service.rb b/lib/sea_food/service.rb index d2b22c5..f569768 100644 --- a/lib/sea_food/service.rb +++ b/lib/sea_food/service.rb @@ -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 @@ -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 diff --git a/sea_food.gemspec b/sea_food.gemspec index dbc2359..7563beb 100644 --- a/sea_food.gemspec +++ b/sea_food.gemspec @@ -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. @@ -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 diff --git a/spec/sea_food/service/service_spec.rb b/spec/sea_food/service/service_spec.rb index 44b881c..9ab7299 100644 --- a/spec/sea_food/service/service_spec.rb +++ b/spec/sea_food/service/service_spec.rb @@ -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 @@ -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( @@ -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 @@ -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: 'hi@example.com') success(email: @email) @@ -230,10 +228,10 @@ def call def initialize(email:) @email = email end - + def call fail!(email: 'hi@example.com') - success!(email: @email) + success(email: @email) end end @@ -242,5 +240,104 @@ def call expect(result).to be_fail expect(result.email).to eq('hi@example.com') 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: 'inner@service.com') + success(email: @email) + end + end + + result = TestParentFailService.call(email: 'outer@service.com') + + expect(result).to be_success + expect(result.email).to eq('outer@service.com') + 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: 'inner@service.com') + success(email: @email) + end + end + + result = TestParentFailService.call(email: 'outer@service.com') + expect(result).to be_fail + expect(result.email).to eq('inner@service.com') + 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: 'second@service.com') + 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: 'first@service.com') + expect(result).to be_success + expect(result.email).to eq('first@service.com') + end + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9d1b875..3808dd0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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