0

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


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

  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

  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!

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189

0 Answers0