diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 37235a69c..d7429e042 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -40,6 +40,8 @@ These changes are merged into the `main` branch, but have not been released. Aft === Dev +* Improve error handling for resolution of lookups for individual jobs (PR#191) + == Releases === 4.0.1 - 2023-09-13 diff --git a/lib/kiba/extend/error.rb b/lib/kiba/extend/error.rb index 2c12700bd..98e0e4728 100644 --- a/lib/kiba/extend/error.rb +++ b/lib/kiba/extend/error.rb @@ -47,6 +47,83 @@ class IterativeCleanupSettingUndefinedError < StandardError include Kiba::Extend::ErrMod end + class JobCannotBeUsedAsLookupError < TypeError + include Kiba::Extend::ErrMod + def initialize(key, klass, for_job) + @key = key + @klass = klass + @for_job = for_job + @msg = ":#{key} cannot be used as a lookup in :#{for_job} because "\ + "its src_class (#{klass}) does not include "\ + "Kiba::Extend::Soures::Lookupable" + super(msg) + end + + def formatted + <<~STR + JOB FAILED: LOOKUP FILE SETUP ERROR FOR: #{for_job} + #{msg} + STR + end + + private + + attr_reader :key, :klass, :for_job, :type, :msg + end + + # Exception raised if {Kiba::Extend::FileRegistry} contains no lookup + # key for file + class NoLookupOnError < NameError + include Kiba::Extend::ErrMod + # @param filekey [Symbol] key not found in + # {Kiba::Extend::FileRegistry} + def initialize(filekey, for_job) + @filekey = filekey + @for_job = for_job + msg = "No lookup_on value in registry entry hash for :#{filekey} -- "\ + "Used as lookup in job: :#{for_job})" + super(msg) + end + + def formatted + <<~STR + JOB FAILED: LOOKUP FILE SETUP ERROR FOR: #{for_job} + To fix: Add `lookup_on` to registry entry hash for + :#{filekey} + STR + end + + private + + attr_reader :filekey, :for_job + end + + # Exception raised if the lookup key value for the file is not a Symbol + class NonSymbolLookupOnError < TypeError + include Kiba::Extend::ErrMod + # @param filekey [Symbol] registry entry key having the non-symbol + # `lookup_on` value + def initialize(filekey, for_job) + @filekey = filekey + @for_job = for_job + @msg = "The `lookup_on` value in the registry entry hash for "\ + ":#{filekey} is not a Ruby Symbol. "\ + "Prepend a : to the field name to fix." + super(msg) + end + + def formatted + <<~STR + JOB FAILED: LOOKUP FILE SETUP ERROR FOR: #{for_job} + #{msg} + STR + end + + private + + attr_reader :filekey, :for_job, :msg + end + class ProjectSettingUndefinedError < StandardError include Kiba::Extend::ErrMod end diff --git a/lib/kiba/extend/jobs/base_job.rb b/lib/kiba/extend/jobs/base_job.rb index 9c7148c28..476974129 100644 --- a/lib/kiba/extend/jobs/base_job.rb +++ b/lib/kiba/extend/jobs/base_job.rb @@ -25,6 +25,10 @@ class BaseJob # running any necessary jobs to create sources and/or lookups needed # by the job. :run does all of the above and runs the job. Since 4.0.0 def initialize(files:, transformer:, mode: :run) + @destination_key = files[:destination].is_a?(Symbol) ? + files[:destination] : + files[:destination].first + if caller(2, 5).join(" ")["block in handle_requirements"] @dependency = true end @@ -60,6 +64,8 @@ def run private + attr_reader :destination_key + def job_data @files[:destination].first.data end @@ -70,7 +76,8 @@ def setup_files(files) tmp = {} files.each do |type, arr| meth = Kiba::Extend.registry.method("as_#{type}") - tmp[type] = [arr].flatten.map { |key| prep_file(meth, key) } + tmp[type] = [arr].flatten + .map { |key| prep_file(meth, key, destination_key) } end tmp end @@ -90,13 +97,15 @@ def pre_process end end - def prep_file(meth, key) - meth.call(key) - # rubocop:todo Layout/LineLength - rescue Kiba::Extend::Registry::FileRegistry::KeyNotRegisteredError => err - # rubocop:enable Layout/LineLength - puts "JOB FAILED: TRANSFORM ERROR IN: #{err.calling_job}" - err.info + def prep_file(meth, key, for_job) + meth.call(key, for_job) + rescue Kiba::Extend::ErrMod => err + if err.respond_to?(:formatted) + puts err.formatted + else + puts "JOB FAILED: TRANSFORM ERROR IN: #{err.calling_job}" + err.info + end exit end diff --git a/lib/kiba/extend/registry/file_registry.rb b/lib/kiba/extend/registry/file_registry.rb index a7389b11a..b5b58018a 100644 --- a/lib/kiba/extend/registry/file_registry.rb +++ b/lib/kiba/extend/registry/file_registry.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -# rubocop:todo Layout/LineLength - require "dry-container" require_relative "registered_source" @@ -13,10 +11,12 @@ module Kiba module Extend module Registry - # Transforms a file_registry hash into an object that can return source, lookup, or destination - # config for that file, for passing to jobs + # Transforms a file_registry hash into an object that can return + # source, lookup, or destination config for that file, for + # passing to jobs # - # An example of a file registry setup in a project can be found at: + # An example of a file registry setup in a project can be found + # at: # https://github.com/lyrasis/fwm-cspace-migration/blob/main/lib/fwm/registry_data.rb class FileRegistry include Dry::Container::Mixin @@ -39,26 +39,41 @@ def initialize(key, type = nil) end end - # @param filekey [String, Symbol] file registry key for file to be used as destination + # @param filekey [String, Symbol] file registry key for file + # to be used as destination # @return [Kiba::Extend::Registry::RegisteredDestination] - def as_destination(filekey) - RegisteredDestination.new(key: filekey, data: lookup(filekey)) + def as_destination(filekey, for_job) + RegisteredDestination.new( + key: filekey, + data: lookup(filekey), + for_job: for_job + ) rescue KeyNotRegisteredError => err raise KeyNotRegisteredError.new(err.key, :destination) end - # @param filekey [String, Symbol] file registry key for file to be used as a lookup source + # @param filekey [String, Symbol] file registry key for file to be used + # as a lookup source # @return [Kiba::Extend::Registry::RegisteredLookup] - def as_lookup(filekey) - RegisteredLookup.new(key: filekey, data: lookup(filekey)) + def as_lookup(filekey, for_job) + RegisteredLookup.new( + key: filekey, + data: lookup(filekey), + for_job: for_job + ) rescue KeyNotRegisteredError => err raise KeyNotRegisteredError.new(err.key, :lookup) end - # @param filekey [String, Symbol] file registry key for file to be used as a source + # @param filekey [String, Symbol] file registry key for file to be used + # as a source # @return [Kiba::Extend::Registry::RegisteredSource] - def as_source(filekey) - RegisteredSource.new(key: filekey, data: lookup(filekey)) + def as_source(filekey, for_job) + RegisteredSource.new( + key: filekey, + data: lookup(filekey), + for_job: for_job + ) rescue KeyNotRegisteredError => err raise KeyNotRegisteredError.new(err.key, :source) end @@ -68,15 +83,18 @@ def entries @entries ||= populate_entries end - # Convenience method combining the steps of transforming initial registry entry hashes - # into FileRegistryEntry objects, and then making the registry immutable for the - # rest of the application's run. `:freeze` is from dry-container. + # Convenience method combining the steps of transforming + # initial registry entry hashes into FileRegistryEntry + # objects, and then making the registry immutable for the + # rest of the application's run. `:freeze` is from + # dry-container. def finalize transform freeze end - # Transforms registered file hashes into Kiba::Extend::Registry::FileRegistryEntry objects + # Transforms registered file hashes into + # {Kiba::Extend::Registry::FileRegistryEntry} objects def transform each { |key, val| decorate(key) { FileRegistryEntry.new(val) } } @entries = populate_entries @@ -123,7 +141,8 @@ def verify_supplied_files_exist end.map(&:path).uniq.each do |file| next if file.exist? - puts %(#{Kiba::Extend.warning_label}: Missing supplied file: #{file}) + puts "#{Kiba::Extend.warning_label}: Missing supplied "\ + "file: #{file}" end end @@ -134,4 +153,3 @@ def validator end end end -# rubocop:enable Layout/LineLength diff --git a/lib/kiba/extend/registry/registered_destination.rb b/lib/kiba/extend/registry/registered_destination.rb index 2d1d447f0..d8cb4007a 100644 --- a/lib/kiba/extend/registry/registered_destination.rb +++ b/lib/kiba/extend/registry/registered_destination.rb @@ -16,7 +16,7 @@ def initialize(entry_key) end end - def initialize(key:, data:) + def initialize(key:, data:, for_job:) super fail SuppliedEntryError.new(key) if supplied end diff --git a/lib/kiba/extend/registry/registered_file.rb b/lib/kiba/extend/registry/registered_file.rb index bf4bddddc..8c4c51ca1 100644 --- a/lib/kiba/extend/registry/registered_file.rb +++ b/lib/kiba/extend/registry/registered_file.rb @@ -1,18 +1,19 @@ # frozen_string_literal: true -# rubocop:todo Layout/LineLength - module Kiba module Extend module Registry - # Abstract base class defining interface for destination files, lookup files, and source files - # returned by {Kiba::Extend::FileRegistry} + # Abstract base class defining interface for destination files, + # lookup files, and source files returned by + # {Kiba::Extend::FileRegistry} class RegisteredFile # Exception raised if no path is given in {FileRegistry} hash class NoFilePathError < StandardError - # @param filekey [Symbol] key for which a file path was not found in {Kiba::Extend::FileRegistry} + # @param filekey [Symbol] key for which a file path was not found in + # {Kiba::Extend::FileRegistry} def initialize(filekey) - msg = "No file path for :#{filekey} is recorded in file registry hash" + msg = "No file path for :#{filekey} is recorded in file registry "\ + "hash" super(msg) end end @@ -24,13 +25,17 @@ def initialize(filekey) :desc # @param key [Symbol] the {Kiba::Extend::FileRegistry} lookup key - # @param data [Hash] the hash of data for the file from {Kiba::Extend::FileRegistry} - def initialize(key:, data:) + # @param data [Hash] the hash of data for the file from + # {Kiba::Extend::FileRegistry} + # @param for_job [Symbol] registry entry job key of the job for which + # this registered file is being prepared + def initialize(key:, data:, for_job:) raise FileNotRegisteredError, key unless data raise NoFilePathError, key if data.errors.keys.any?(:missing_path) @key = key @data = data + @for_job = for_job @path = data.path.to_s @dest_class = data.dest_class @dest_opt = data.dest_opt @@ -47,6 +52,8 @@ def src_class private + attr_reader :for_job + # returns equivalent source class for given destination class def dest_src src = dest_class.as_source_class @@ -58,4 +65,3 @@ def dest_src end end end -# rubocop:enable Layout/LineLength diff --git a/lib/kiba/extend/registry/registered_lookup.rb b/lib/kiba/extend/registry/registered_lookup.rb index af8025b30..2f211d2be 100644 --- a/lib/kiba/extend/registry/registered_lookup.rb +++ b/lib/kiba/extend/registry/registered_lookup.rb @@ -8,51 +8,30 @@ module Kiba module Extend module Registry - # Value object representing a file registered in a {Kiba::Extend::FileRegistry} that is being - # called into another job as a lookup table + # Value object representing a file registered in a + # {Kiba::Extend::FileRegistry} that is being called into + # another job as a lookup table # # Assumes this file will be used to build a {Kiba::Extend::Lookup} class RegisteredLookup < RegisteredFile include RequirableFile - class CannotBeUsedAsLookupError < TypeError - include Kiba::Extend::ErrMod - def initialize(klass) - super("The result of a registry entry with a #{klass} "\ - "dest_class cannot be used as source file in a job") - end - end - - # Exception raised if {Kiba::Extend::FileRegistry} contains no lookup key for file - class NoLookupKeyError < NameError - include Kiba::Extend::ErrMod - # @param filekey [Symbol] key not found in {Kiba::Extend::FileRegistry} - def initialize(filekey) - msg = "No lookup key column found for :#{filekey} in file registry hash" - super(msg) - end - end - - # Exception raised if the lookup key value for the file is not a Symbol - class NonSymbolLookupKeyError < TypeError - include Kiba::Extend::ErrMod - # @param filekey [Symbol] key not found in {Kiba::Extend::FileRegistry} - def initialize(filekey) - msg = "Lookup key found for :#{filekey} is not a Ruby Symbol. Prepend a : to the field name to fix." - super(msg) - end - end - # @param key [Symbol] file key from {FileRegistry} data hash # @param data [Hash] file data from {FileRegistry} - def initialize(key:, data:) + # @param for_job [Symbol] registry entry job key of the job for which + # this registered file is being prepared + def initialize(key:, data:, for_job:) super unless src_class.respond_to?(:is_lookupable?) - fail CannotBeUsedAsLookupError.new(src_class) + fail Kiba::Extend::JobCannotBeUsedAsLookupError.new( + key, src_class, for_job + ) + end + unless lookup_on + fail Kiba::Extend::NoLookupOnError.new(key, for_job) end - fail NoLookupKeyError, @key unless lookup_on unless lookup_on.is_a?(Symbol) - fail NonSymbolLookupKeyError, @key + fail Kiba::Extend::NonSymbolLookupOnError.new(key, for_job) end end diff --git a/spec/kiba/extend/registry/file_registry_spec.rb b/spec/kiba/extend/registry/file_registry_spec.rb index 6695e62e3..4c8465126 100644 --- a/spec/kiba/extend/registry/file_registry_spec.rb +++ b/spec/kiba/extend/registry/file_registry_spec.rb @@ -113,7 +113,7 @@ end describe "as destination" do - let(:result) { registry.as_destination(filekey) } + let(:result) { registry.as_destination(filekey, :foo) } context "with job entry key" do let(:filekey) { :foo } @@ -145,14 +145,14 @@ end describe "as lookup" do - let(:result) { registry.as_lookup(filekey) } + let(:result) { registry.as_lookup(filekey, :foo) } it "returns lookup file config" do expect(result).to be_a(Kiba::Extend::Registry::RegisteredLookup) end end describe "as source" do - let(:result) { registry.as_source(filekey) } + let(:result) { registry.as_source(filekey, :foo) } it "returns source file config" do expect(result).to be_a(Kiba::Extend::Registry::RegisteredSource) end diff --git a/spec/kiba/extend/registry/registered_destination_spec.rb b/spec/kiba/extend/registry/registered_destination_spec.rb index f96f5e074..e08e28595 100644 --- a/spec/kiba/extend/registry/registered_destination_spec.rb +++ b/spec/kiba/extend/registry/registered_destination_spec.rb @@ -11,7 +11,8 @@ let(:dest) do Kiba::Extend::Registry::RegisteredDestination.new( key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data) + data: Kiba::Extend::Registry::FileRegistryEntry.new(data), + for_job: :foo ) end let(:optres) { {csv_options: Kiba::Extend.csvopts} } diff --git a/spec/kiba/extend/registry/registered_file_spec.rb b/spec/kiba/extend/registry/registered_file_spec.rb index 26490a0cd..7ff04babb 100644 --- a/spec/kiba/extend/registry/registered_file_spec.rb +++ b/spec/kiba/extend/registry/registered_file_spec.rb @@ -10,7 +10,8 @@ let(:dest) do Kiba::Extend::Registry::RegisteredFile.new( key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data) + data: Kiba::Extend::Registry::FileRegistryEntry.new(data), + for_job: :foo ) end @@ -18,12 +19,7 @@ let(:data) { {description: "blah"} } it "raises FileNotRegisteredError" do msg = "No file path for :#{filekey} is recorded in file registry hash" - expect do - Kiba::Extend::Registry::RegisteredFile.new( - key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data) - ) - end.to raise_error( + expect { dest }.to raise_error( Kiba::Extend::Registry::RegisteredFile::NoFilePathError, msg ) end diff --git a/spec/kiba/extend/registry/registered_lookup_spec.rb b/spec/kiba/extend/registry/registered_lookup_spec.rb index c621b8436..efe295155 100644 --- a/spec/kiba/extend/registry/registered_lookup_spec.rb +++ b/spec/kiba/extend/registry/registered_lookup_spec.rb @@ -13,18 +13,29 @@ let(:lookup) do Kiba::Extend::Registry::RegisteredLookup.new( key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data) + data: Kiba::Extend::Registry::FileRegistryEntry.new(data), + for_job: :bar ) end - context "when called without lookup key" do + context "when called without lookup_on value" do let(:data) { {path: path} } - it "raises NoLookupKeyError" do - msg = "No lookup key column found for :#{filekey} in file registry hash" + it "raises NoLookupOnError" do expect do lookup end.to raise_error( - Kiba::Extend::Registry::RegisteredLookup::NoLookupKeyError, msg + Kiba::Extend::NoLookupOnError + ) + end + end + + context "when called with non-Symbol lookup_on value" do + let(:data) { {path: path, lookup_on: "bar"} } + it "raises NonSymbolLookupOnError" do + expect do + lookup + end.to raise_error( + Kiba::Extend::NonSymbolLookupOnError ) end end @@ -39,9 +50,12 @@ } end - it "raises CannotBeUsedAsLookupError" do + it "raises JobCannotBeUsedAsLookupError" do expect { lookup }.to raise_error( - Kiba::Extend::Registry::RegisteredLookup::CannotBeUsedAsLookupError + Kiba::Extend::JobCannotBeUsedAsLookupError, + ":fkey cannot be used as a lookup in :bar because its src_class "\ + "(Kiba::Extend::Sources::Marc) does not include "\ + "Kiba::Extend::Soures::Lookupable" ) end end @@ -56,9 +70,12 @@ } end - it "raises CannotBeUsedAsLookupError" do + it "raises JobCannotBeUsedAsLookupError" do expect { lookup }.to raise_error( - Kiba::Extend::Registry::RegisteredLookup::CannotBeUsedAsLookupError + Kiba::Extend::JobCannotBeUsedAsLookupError, + ":fkey cannot be used as a lookup in :bar because its src_class "\ + "(Kiba::Extend::Sources::Marc) does not include "\ + "Kiba::Extend::Soures::Lookupable" ) end end diff --git a/spec/kiba/extend/registry/registered_source_spec.rb b/spec/kiba/extend/registry/registered_source_spec.rb index 1bd5b41c1..8d73f58c7 100644 --- a/spec/kiba/extend/registry/registered_source_spec.rb +++ b/spec/kiba/extend/registry/registered_source_spec.rb @@ -10,7 +10,8 @@ let(:source) do Kiba::Extend::Registry::RegisteredSource.new( key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data) + data: Kiba::Extend::Registry::FileRegistryEntry.new(data), + for_job: :foo ) end diff --git a/spec/kiba/extend/registry/requirable_file_spec.rb b/spec/kiba/extend/registry/requirable_file_spec.rb index 42e9d433d..4aa40844f 100644 --- a/spec/kiba/extend/registry/requirable_file_spec.rb +++ b/spec/kiba/extend/registry/requirable_file_spec.rb @@ -15,17 +15,21 @@ class TestClass < Kiba::Extend::Registry::RegisteredFile end let(:klass) do TestClass.new(key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data)) + data: Kiba::Extend::Registry::FileRegistryEntry.new(data), + for_job: :foo) end context "when called without creator" do let(:data) { {path: path} } + let(:klass) do + TestClass.new(key: filekey, + data: Kiba::Extend::Registry::FileRegistryEntry.new(data), + for_job: :foo) + end + it "raises NoDependencyCreatorError" do msg = "No creator method found for :#{filekey} in file registry" - expect do - TestClass.new(key: filekey, - data: Kiba::Extend::Registry::FileRegistryEntry.new(data)).required - end.to raise_error( + expect { klass.required }.to raise_error( Kiba::Extend::Registry::RequirableFile::NoDependencyCreatorError, msg ) end