diff --git a/lib/digital_scriptorium.rb b/lib/digital_scriptorium.rb index 75c45e6..659a761 100644 --- a/lib/digital_scriptorium.rb +++ b/lib/digital_scriptorium.rb @@ -4,6 +4,8 @@ require 'digital_scriptorium/property_id' require 'digital_scriptorium/ds_item' +require 'digital_scriptorium/no_current_holding_exception' +require 'digital_scriptorium/multiple_current_holdings_exception' require 'digital_scriptorium/ds_meta' require 'digital_scriptorium/holding' require 'digital_scriptorium/manuscript' diff --git a/lib/digital_scriptorium/ds_meta.rb b/lib/digital_scriptorium/ds_meta.rb index c13acd6..efccd08 100644 --- a/lib/digital_scriptorium/ds_meta.rb +++ b/lib/digital_scriptorium/ds_meta.rb @@ -12,11 +12,7 @@ def initialize(record, export_hash) manuscript = export_hash[record.described_manuscript_id] current_holdings = current_holdings(manuscript, export_hash) - if current_holdings.size != 1 - raise "Manuscripts must have exactly 1 current holding, found #{current_holdings.size}" - end - - @holding = current_holdings.first + @holding = validate_holdings_count(manuscript, current_holdings) @manuscript = manuscript @record = record end @@ -24,5 +20,18 @@ def initialize(record, export_hash) def current_holdings(manuscript, export_hash) manuscript.holding_ids.filter_map { |id| export_hash[id] if export_hash[id]&.current? } end + + private + + def validate_holdings_count(manuscript, current_holdings) + case current_holdings.size + when 0 + raise NoCurrentHoldingException, manuscript.id + when 1 + current_holdings.first + else + raise MultipleCurrentHoldingsException.new(manuscript.id, current_holdings.size) + end + end end end diff --git a/lib/digital_scriptorium/multiple_current_holdings_exception.rb b/lib/digital_scriptorium/multiple_current_holdings_exception.rb new file mode 100644 index 0000000..3d52f42 --- /dev/null +++ b/lib/digital_scriptorium/multiple_current_holdings_exception.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module DigitalScriptorium + # Exception raised when a manuscript has multiple current holdings, which is an invalid state + # that requires correction in the data. + class MultipleCurrentHoldingsException < StandardError + attr_reader :manuscript_id, :holdings_count + + def initialize(manuscript_id, holdings_count) + @manuscript_id = manuscript_id + @holdings_count = holdings_count + super("Manuscript #{manuscript_id} has #{holdings_count} current holdings, expected at most 1") + end + end +end diff --git a/lib/digital_scriptorium/no_current_holding_exception.rb b/lib/digital_scriptorium/no_current_holding_exception.rb new file mode 100644 index 0000000..3201ca1 --- /dev/null +++ b/lib/digital_scriptorium/no_current_holding_exception.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module DigitalScriptorium + # Exception raised when a manuscript has no current holding, which is a valid state + # but requires special handling in processing workflows. + class NoCurrentHoldingException < StandardError + attr_reader :manuscript_id + + def initialize(manuscript_id) + @manuscript_id = manuscript_id + super("Manuscript #{manuscript_id} has no current holding") + end + end +end diff --git a/spec/digital_scriptorium/ds_meta_spec.rb b/spec/digital_scriptorium/ds_meta_spec.rb index 0c4e0d1..7adb6c3 100644 --- a/spec/digital_scriptorium/ds_meta_spec.rb +++ b/spec/digital_scriptorium/ds_meta_spec.rb @@ -6,14 +6,50 @@ module DigitalScriptorium include WikibaseRepresentable::Representers RSpec.describe DsMeta do + let(:record) { export_hash.fetch('Q544') } + let(:manuscript) { export_hash.fetch('Q543') } + context 'with a record concerning a manuscript with a single current holding' do it 'correctly sets the holding' do - record = export_hash.fetch('Q544') meta = described_class.new(record, export_hash) expect(meta.holding).to eq export_hash.fetch('Q542') end end + context 'with a record concerning a manuscript with no current holdings' do + let(:manuscript_no_holdings) do + manuscript.tap { |m| allow(m).to receive(:holding_ids).and_return([]) } + end + + it 'raises NoCurrentHoldingException' do + allow(export_hash).to receive(:[]).with(record.described_manuscript_id).and_return(manuscript_no_holdings) + + expect { described_class.new(record, export_hash) } + .to raise_error(DigitalScriptorium::NoCurrentHoldingException, + 'Manuscript Q543 has no current holding') + end + end + + context 'with a record concerning a manuscript with multiple current holdings' do + let(:first_holding) { instance_double(DigitalScriptorium::Holding, current?: true) } + let(:second_holding) { instance_double(DigitalScriptorium::Holding, current?: true) } + let(:manuscript_multi_holdings) do + manuscript.tap { |m| allow(m).to receive(:holding_ids).and_return(%w[H1 H2]) } + end + + before do + allow(export_hash).to receive(:[]).with(record.described_manuscript_id).and_return(manuscript_multi_holdings) + allow(export_hash).to receive(:[]).with('H1').and_return(first_holding) + allow(export_hash).to receive(:[]).with('H2').and_return(second_holding) + end + + it 'raises MultipleCurrentHoldingsException' do + expect { described_class.new(record, export_hash) } + .to raise_error(DigitalScriptorium::MultipleCurrentHoldingsException, + 'Manuscript Q543 has 2 current holdings, expected at most 1') + end + end + # TODO: Create setters in wikibase_representable and use them to build objects for testing # (currently) counterfactual holdings scenarios (i.e., other than exactly one current holding) end