I have a couple of service objects in my rails application where I am using Dry-Monads to influence a pattern of “success” or “failure” by using their Do Notation. However, in my main service object, upon calling a secondary service object, the main class returns early instead of following down to the last success call.
ApplicationService
# frozen_string_literal: true
require 'dry/monads'
class ApplicationService
include Dry::Monads[:result, :do]
def self.call(*args, **kwargs, &block)
new(*args, **kwargs, &block).call
end
def success!(val = nil)
Success(val)
end
def fail!(err)
Failure(err)
end
end
Create Check Request
class CheckRequests::Create < ApplicationService
attr_accessor :user, :params, :company, :check_request
def initialize(user:, params:)
@user = user
@check_request = nil
@company = find_company(params[:company_id])
@params = set_default_params(params)
end
def call
# Calling this service class seems to yield right here
vendor_id = yield generate_vendor_id
check_request = yield create_check_request(vendor_id)
success!({ check_request: check_request })
end
private
def create_check_request(vendor_id)
params[:vendor_id] = vendor_id if vendor_id.present?
success!(CheckRequest.create!(**params))
rescue ActiveRecord::RecordInvalid => e
fail!(e.record.errors)
end
def find_company(company_id)
Company.find(company_id)
rescue ActiveRecord::RecordNotFound => e
fail!("Cannot find company with id #{company_id}")
end
def set_default_params(params)
params = params.dup
approved = params[:status] == 'Approved'
params[:source_account_id] = company.default_source_account
if approved
params[:approved_at] = DateTime.now
params[:approver] = user
end
params
end
def generate_vendor_id
return success! unless vendor_needed?
vendor_uniq = ::Quickbooks::Query.unique(company, params[:client])
return success! unless vendor_uniq
Qbo::Vendors::Create.new(
company: company,
client: params[:client],
mailing_address: JSON.parse(params[:mailing_address])
).call
end
def vendor_needed?
params[:vendor_id] == '0'
end
end
Vendor Create Service Object
class Qbo::Vendors::Create < ApplicationService
attr_accessor :company, :client, :mailing_address
def initialize(company:, client:, mailing_address:)
@company = company
@client = client
@mailing_address = mailing_address
end
def call
yield create_qbo_vendor
end
private
def create_qbo_vendor
url = "#{ENV["QBO_BASE_URL"]}company/#{company.realm_id}/vendor"
response = RestClient.post(
url,
{ DisplayName: client, BillAddr: mailing_address }.to_json,
{
Authorization: "Bearer #{company.access_token}",
content_type: "application/json"
}
)
vendor = Hash.from_xml(response.body)
success!(vendor.dig("IntuitResponse", "Vendor", "Id"))
rescue RestClient::ExceptionWithResponse => e
body = JSON.parse(e.http_body)
err = body.dig("Fault", "Error", 0, "Detail") || e.message
fail!(err)
end
end
I’m sure I am just not looking at things correctly. Any and all help would be greatly appreciated!