ocfl-tools 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 915f99359028f587423cb4125cc197215fd2d6f0ae3a663c2d421ba2fa636d04
4
- data.tar.gz: 7aaa9773441a5a672b9a94852946cdb7819d6aecc58ee176995b1cfd64ea8173
3
+ metadata.gz: 817dfd488b3cb6f05522175c68cba6385c76007950dc03af3162579517a1a653
4
+ data.tar.gz: 402eb44681f151b91e1403ed5fa289c632f62480c1f3199f7e8f468ca6f4e567
5
5
  SHA512:
6
- metadata.gz: b06efb97d5cb2dc0a0376310ffefb66c93bce411a932de1afe7f49f4ee200f774c9d6465c424207cc390c35804151b03e9b370b8ca4397fb44b161d0cf9aee40
7
- data.tar.gz: b29499a9bcbba6cb3dedfc140d6208a42ad712af0d70a1fd4b96802f4f58bc2f02532b97bb7d63138e3a93b17005939de6ed6ffe909545fe452f2b4e5e30fc0e
6
+ metadata.gz: a8a98f2d7bfab21babf2103857d970ad8d8e6e2cb1b6131b39f654568f161055ab4b4c567c2360d0d772bf709bb791d8125df0eff161de07eafec04d1c2fdffa
7
+ data.tar.gz: a8e692758b075775d124939bb533761b56f03b5b5d79a2ed3f916105f61b2361c1cd024f49886d554fd2578ac7e768c155c703ff85238b399a6b204047782476
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.14
1
+ 0.9.15
@@ -1,21 +1,71 @@
1
1
  module OcflTools
2
2
  module Errors
3
+ # See this for a nice model.
4
+ # https://github.com/ryanb/cancan/blob/master/lib/cancan/exceptions.rb
5
+ class ValidationError < StandardError
6
+ attr_accessor :details
7
+ attr_accessor :msg
8
+ def initialize(msg: "A validation error has occured.", details: {} )
9
+ @msg = msg
10
+ @details = details
11
+ end
12
+ end
13
+
14
+ # For bad client requests
15
+ class ClientError < StandardError; end
16
+
17
+ class SyntaxError < StandardError
18
+ def initialize(msg="Generic syntax error.")
19
+ end
20
+ end
21
+
22
+ class UnableToLoadInventoryFile < StandardError
23
+ def initialize(msg="Requested inventory file failed to load. See downstream errors for details.")
24
+ end
25
+ end
26
+
27
+ ### Client errors (you asked for the wrong stuff)
28
+ class RequestedKeyNotFound < ClientError
29
+ # You ask for key 'foo', but you are dumb and key 'foo' is not in the spec.
30
+ def initialize(msg="Requested key not found in provided inventory.json.")
31
+ end
32
+ end
33
+
34
+ class RequestedFileNotFound < ClientError
35
+ def initialize(msg="Requested file does not exist.")
36
+ end
37
+ end
38
+
39
+ class RequestedDirectoryNotFound < ClientError
40
+ def initialize(msg="Requested directory does not exist.")
41
+ end
42
+ end
43
+
44
+ class FileMissingFromVersionState < ClientError
45
+ def initialize(msg="The requested file cannot be found in the provided version state block.")
46
+ end
47
+ end
48
+
49
+ class FileDigestMismatch < ClientError
50
+ def initialize(msg="The requested file already exists in inventory with different digest.")
51
+ end
52
+ end
3
53
 
4
- class Error211 < StandardError
5
- def initialize(msg="inventory.json is not valid JSON.")
6
- super
54
+ # You asked for version -1, or version 44c.
55
+ class NonCompliantValue < ClientError
56
+ def initialize(msg="Value provided is outside of specification bounds.")
7
57
  end
8
58
  end
9
59
 
10
- class Error216 < StandardError
11
- def initialize(msg="Unable to find required key in inventory.json.")
12
- super
60
+ ### Validation errors (stuff that MUST be true, per spec, is not)
61
+ class RequiredKeyNotFound < ValidationError
62
+ # key 'foo' is in the spec and should be in the inventory. Fail if not.
63
+ def initialize(msg="Required key not found in provided inventory.json.")
13
64
  end
14
65
  end
15
66
 
16
- class Error217 < StandardError
17
- def initialize(msg="Required key in inventory.json must contain a value.")
18
- super
67
+ class CannotEditPreviousVersion < ValidationError
68
+ def initialize(msg="Previous version state blocks are considered read-only.")
19
69
  end
20
70
  end
21
71
 
@@ -54,9 +54,13 @@ module OcflTools
54
54
  begin
55
55
  JSON.parse(File.read(file))
56
56
  rescue JSON::ParserError
57
- raise OcflTools::Errors::Error211
58
- rescue StandardError
59
- raise "An unknown error occured reading file #{file}" # catch/encapsulate any JSON::Parser or FileIO issues
57
+ raise OcflTools::Errors::ValidationError, details: { "E211" => ["#{file} is not valid JSON."] }
58
+ rescue Errno::ENOENT
59
+ # raise OcflTools::Errors::Error215, "expected inventory file #{file} not found!"
60
+ raise OcflTools::Errors::ValidationError, details: { "E215" => ["expected inventory file #{file} not found!"] }
61
+ # rescue Errno::EACCES Don't think we need to explicitly raise file permissions; let StdErr take it.
62
+ rescue StandardError => e
63
+ raise "An unknown error occured reading file #{file}: #{e}" # catch/encapsulate any FileIO issues
60
64
  end
61
65
  end
62
66
 
@@ -67,22 +71,31 @@ module OcflTools
67
71
  import_hash = read_json(file)
68
72
 
69
73
  # REQUIRED keys; raise exception if not found.
74
+ e216_errors = []
75
+ e217_errors = []
76
+ error_hash = {}
70
77
  [ 'id', 'head', 'type', 'digestAlgorithm', 'manifest', 'versions' ].each do | key |
71
78
  unless import_hash.key?(key)
72
- raise OcflTools::Errors::Error216, "Required key #{key} not found"
79
+ e216_errors << "Required key #{key} not found in #{file}"
80
+ error_hash["E216"] = e216_errors # we'll keep updating this value as new errors are recorded.
73
81
  end
74
- if import_hash[key].empty?
75
- raise OcflTools::Errors::Error217, "Required key #{key} must contain a value"
82
+ if import_hash.key?(key) && import_hash[key].empty?
83
+ # If the key exists but it's empty, that's also a problem!
84
+ e217_errors << "Required key #{key} in #{file} must contain a value"
85
+ error_hash["E217"] = e217_errors
76
86
  end
77
87
  end
88
+ # Raise a problem if we have anything in error_hash.
89
+ if error_hash.size > 0
90
+ raise OcflTools::Errors::ValidationError, details: error_hash
91
+ end
92
+
93
+ # The actual values of these keys may be hot garbage, but it's not ocfl_inventory's place to check.
78
94
 
79
95
  @id = import_hash['id']
80
96
  @head = import_hash['head']
81
97
  @type = import_hash['type']
82
98
  @digestAlgorithm = import_hash['digestAlgorithm']
83
- # if import_hash.key?('contentDirectory')
84
- # @contentDirectory = import_hash['contentDirectory']
85
- # end
86
99
  @manifest = import_hash['manifest']
87
100
  @versions = import_hash['versions']
88
101
  # Optional keys - contentDirectory and fixity block.
@@ -52,7 +52,7 @@ module OcflTools
52
52
  # @note will raise an exception if you attempt to query a non-existent version.
53
53
  def set_version_message(version, message)
54
54
  unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
55
- raise "Version #{version} does not yet exist!"
55
+ raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
56
56
  end
57
57
 
58
58
  @versions[OcflTools::Utils.version_int_to_string(version)]['message'] = message
@@ -64,7 +64,7 @@ module OcflTools
64
64
  # @note will raise an exception if you attempt to query a non-existent version.
65
65
  def get_version_message(version)
66
66
  unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
67
- raise "Version #{version} does not yet exist!"
67
+ raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
68
68
  end
69
69
 
70
70
  @versions[OcflTools::Utils.version_int_to_string(version)]['message']
@@ -76,7 +76,7 @@ module OcflTools
76
76
  # @note will raise an exception if you attempt to query a non-existent version.
77
77
  def set_version_created(version, created)
78
78
  unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
79
- raise "Version #{version} does not yet exist!"
79
+ raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
80
80
  end
81
81
 
82
82
  @versions[OcflTools::Utils.version_int_to_string(version)]['created'] = created
@@ -88,7 +88,7 @@ module OcflTools
88
88
  # @note will raise an exception if you attempt to query a non-existent version.
89
89
  def get_version_created(version)
90
90
  unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
91
- raise "Version #{version} does not yet exist!"
91
+ raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
92
92
  end
93
93
 
94
94
  @versions[OcflTools::Utils.version_int_to_string(version)]['created']
@@ -100,7 +100,7 @@ module OcflTools
100
100
  # @note will raise an exception if you attempt to query a nonexistent version.
101
101
  def set_version_user(version, user)
102
102
  unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
103
- raise "Version #{version} does not yet exist!"
103
+ raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
104
104
  end
105
105
 
106
106
  @versions[OcflTools::Utils.version_int_to_string(version)]['user'] = user
@@ -112,7 +112,7 @@ module OcflTools
112
112
  # @note will raise an exception if you attempt to query a nonexistent version.
113
113
  def get_version_user(version)
114
114
  unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
115
- raise "Version #{version} does not yet exist!"
115
+ raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
116
116
  end
117
117
 
118
118
  @versions[OcflTools::Utils.version_int_to_string(version)]['user']
@@ -184,7 +184,7 @@ module OcflTools
184
184
  my_state = get_state(version)
185
185
 
186
186
  unless version == version_id_list.max
187
- raise "Can't edit prior versions! Only version #{version_id_list.max} can be modified now."
187
+ raise OcflTools::Errors::CannotEditPreviousVersion, "Can't edit prior versions! Only version #{version_id_list.max} can be modified now."
188
188
  end
189
189
 
190
190
  # if the key is not in the manifest, assume that we meant to add it.
@@ -205,7 +205,7 @@ module OcflTools
205
205
  # If so; fail. We don't do implicit / soft adds. You want that, be explict: do an update_file instead.
206
206
  existing_files = get_files(version)
207
207
  if existing_files.key?(file)
208
- raise 'File already exists with different digest in this version! Consider update instead.'
208
+ raise OcflTools::Errors::FileDigestMismatch, "#{file} already exists with different digest in version #{version}. Consider update instead."
209
209
  end
210
210
 
211
211
  # if it's not in State already, just add it.
@@ -256,7 +256,7 @@ module OcflTools
256
256
  # Does Digest exist in @manifest? Fail if not.
257
257
  # Doe fixityAlgorithm exist as a key in @fixity? Add if not.
258
258
  unless @manifest.key?(digest) == true
259
- raise "Unable to find digest #{digest} in manifest!"
259
+ raise OcflTools::Errors::RequestedKeyNotFound, "Unable to find digest #{digest} in manifest!"
260
260
  end
261
261
 
262
262
  filepaths = @manifest[digest]
@@ -289,7 +289,7 @@ module OcflTools
289
289
  my_state = get_state(version) # Creates version & copies state from prior version if doesn't exist.
290
290
 
291
291
  unless version == version_id_list.max
292
- raise "Can't edit prior versions! Only version #{version} can be modified now."
292
+ raise OcflTools::Errors::CannotEditPreviousVersion, "Can't edit prior versions! Only version #{version_id_list.max} can be modified now."
293
293
  end
294
294
 
295
295
  my_digest = get_digest(file, version)
@@ -364,7 +364,7 @@ module OcflTools
364
364
  end
365
365
  # Now see if the requested file is actually here.
366
366
  unless my_files.key?(file)
367
- raise "Get_digest can't find requested file in given version!"
367
+ raise OcflTools::Errors::FileMissingFromVersionState, "Get_digest can't find requested file #{file} in version #{version}."
368
368
  end
369
369
 
370
370
  my_files[file]
@@ -377,7 +377,7 @@ module OcflTools
377
377
  # @note If a (n-1) version exists in the object, and the requested version does not yet exist, this method will copy that version's state block into the new version.
378
378
  def get_version(version)
379
379
  unless version > 0
380
- raise "OCFL object version cannot be zero!"
380
+ raise OcflTools::Errors::NonCompliantValue, "Requested value '#{version}' for object version does not comply with specification."
381
381
  end
382
382
  if @versions.key?(OcflTools::Utils.version_int_to_string(version))
383
383
  @versions[OcflTools::Utils.version_int_to_string(version)]
@@ -414,11 +414,15 @@ module OcflTools
414
414
  # @param [Hash] hash use this hash for the content of the new OCFL version block.
415
415
  def set_version(version, hash)
416
416
  # SAN Check to make sure passed Hash has all expected keys.
417
+ e216_errors = []
417
418
  %w[created message user state].each do |key|
418
419
  if hash.key?(key) == false
419
- raise "version #{version} hash block is missing required #{key} key"
420
+ e216_errors << "version #{version} hash block is missing required #{key} key."
420
421
  end
421
422
  end
423
+ if e216_errors.size > 0
424
+ raise OcflTools::Errors::ValidationError, details: { "E216" => e216_errors }
425
+ end
422
426
  @versions[OcflTools::Utils.version_int_to_string(version)] = hash
423
427
  end
424
428
  end
@@ -63,7 +63,14 @@ module OcflTools
63
63
  # @return {OcflTools::OcflResults} of event results
64
64
  def verify_fixity(inventory_file: "#{@ocfl_object_root}/inventory.json", digest: 'md5')
65
65
  # Gets the appropriate fixity block, calls compare_hash_checksums
66
- @inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
66
+
67
+ begin
68
+ @inventory = load_inventory(inventory_file)
69
+ rescue OcflTools::Errors::ValidationError
70
+ @my_results.error('E210', 'verify_fixity', "Unable to process inventory file #{inventory_file}.")
71
+ return @my_results
72
+ end
73
+
67
74
  # Since fixity blocks are not required to be complete, we just validate what's there.
68
75
  # So get the fixity block, flip it, expand it, checksum it against the same files on disk.
69
76
 
@@ -121,9 +128,9 @@ module OcflTools
121
128
  return @my_results
122
129
  end
123
130
 
124
- if load_inventory(inventory_file) == true
125
- @inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
126
- else
131
+ begin
132
+ @inventory = load_inventory(inventory_file)
133
+ rescue OcflTools::Errors::ValidationError
127
134
  @my_results.error('E210', 'verify_fixity', "Unable to process inventory file #{inventory_file}.")
128
135
  return @my_results
129
136
  end
@@ -260,9 +267,9 @@ module OcflTools
260
267
  return @my_results
261
268
  end
262
269
 
263
- if load_inventory(inventory_file) == true
264
- @inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
265
- else
270
+ begin
271
+ @inventory = load_inventory(inventory_file)
272
+ rescue OcflTools::Errors::ValidationError
266
273
  @my_results.error('E210', 'verify_checksums', "Unable to process inventory file #{inventory_file}.")
267
274
  return @my_results
268
275
  end
@@ -312,7 +319,8 @@ module OcflTools
312
319
  # 1. use get_version_format to determine the format used for version directories.
313
320
  # If we can't deduce it by inspection of the object_root, ERROR and try and process using site-wide defaults.
314
321
  if get_version_format == false
315
- error = true
322
+ @my_results.error('E111', 'verify_structure', 'OCFL unable to determine version format by inspection of directories.')
323
+ @error = true
316
324
  end
317
325
 
318
326
  object_root_dirs = []
@@ -339,12 +347,13 @@ module OcflTools
339
347
  # 2b. What's the highest version we should find here?
340
348
  # 2c. What should our contentDirectory value be?
341
349
  if File.exist? "#{@ocfl_object_root}/inventory.json"
342
- if load_inventory("#{@ocfl_object_root}/inventory.json") == true
350
+ begin
351
+ @inventory = load_inventory("#{@ocfl_object_root}/inventory.json")
343
352
  json_digest = OcflTools::Utils::Inventory.get_digestAlgorithm("#{@ocfl_object_root}/inventory.json")
344
353
  contentDirectory = OcflTools::Utils::Inventory.get_contentDirectory("#{@ocfl_object_root}/inventory.json")
345
354
  expect_head = OcflTools::Utils::Inventory.get_value("#{@ocfl_object_root}/inventory.json", 'head')
346
355
  file_checks << "inventory.json.#{json_digest}"
347
- else
356
+ rescue OcflTools::Errors::ValidationError
348
357
  # We couldn't load up the inventory; use site defaults.
349
358
  contentDirectory = OcflTools.config.content_directory
350
359
  json_digest = OcflTools.config.digest_algorithm
@@ -444,7 +453,17 @@ module OcflTools
444
453
  object_root_dirs.delete('extensions')
445
454
  end
446
455
 
447
- version_directories = OcflTools::Utils::Files.get_version_directories(@ocfl_object_root)
456
+ begin
457
+ version_directories = OcflTools::Utils::Files.get_version_directories(@ocfl_object_root)
458
+ rescue OcflTools::Errors::ValidationError => e
459
+ e.details.each do | code, messages |
460
+ messages.each do | msg |
461
+ @my_results.error(code, 'verify_structure', msg)
462
+ end
463
+ end
464
+ # If we actually throw a validation error, we can't proceed: no version directories found!
465
+ return @my_results
466
+ end
448
467
 
449
468
  remaining_dirs = object_root_dirs - version_directories
450
469
 
@@ -466,7 +485,7 @@ module OcflTools
466
485
  # just that they're valid version dir names, sorted in ascending order, and they exist.
467
486
  if version_directories.include? expected_directory
468
487
  # Could verbose log this here.
469
- # @my_results.ok('O200', 'verify_sructure', "Expected version directory #{expected_directory} found.")
488
+ # @my_results.info('I200', 'verify_sructure', "Expected version directory #{expected_directory} found.")
470
489
  else
471
490
  @my_results.error('E013', 'verify_structure', "Expected version directory #{expected_directory} missing from directory list #{version_directories} ")
472
491
  error = true
@@ -499,7 +518,8 @@ module OcflTools
499
518
  # 9. Warn if inventory.json and sidecar are not present in version directory.
500
519
  file_checks = []
501
520
  if File.exist? "#{@ocfl_object_root}/#{ver}/inventory.json"
502
- if load_inventory("#{@ocfl_object_root}/inventory.json") == true
521
+ begin
522
+ @inventory = load_inventory("#{@ocfl_object_root}/#{ver}/inventory.json")
503
523
  json_digest = OcflTools::Utils::Inventory.get_digestAlgorithm("#{@ocfl_object_root}/#{ver}/inventory.json")
504
524
  file_checks << 'inventory.json'
505
525
  file_checks << "inventory.json.#{json_digest}"
@@ -508,12 +528,14 @@ module OcflTools
508
528
  @my_results.error('E111', 'verify_structure', "contentDirectory value #{versionContentDirectory} in version #{ver} does not match expected contentDirectory value #{contentDirectory}.")
509
529
  error = true
510
530
  end
511
- else
531
+ rescue OcflTools::Errors::ValidationError
532
+ # We couldn't load up the inventory; use site defaults.
533
+ # We should also record the error in @my_results?
512
534
  json_digest = OcflTools.config.digest_algorithm
513
535
  file_checks << 'inventory.json'
514
536
  file_checks << "inventory.json.#{json_digest}"
537
+ error = true
515
538
  end
516
-
517
539
  else
518
540
  file_checks << 'inventory.json' # We look for it, even though we know we won't find it, so we can log the omission.
519
541
  file_checks << 'inventory.json.sha512' # We look for it, even though we know we won't find it, so we can log the omission.
@@ -557,12 +579,6 @@ module OcflTools
557
579
  # 11. WARN if a contentDirectory exists, but is empty.
558
580
  if version_dirs.include? contentDirectory
559
581
  version_dirs.delete(contentDirectory)
560
- # if Dir.empty?(contentDirectory)
561
- # @my_results.warn('W102', 'verify_structure', "OCFL 3.3.1 version #{ver} contentDirectory should not be empty.")
562
- # end
563
- # else
564
- # # Informational message that contentDir does not exist. Not necssarily a problem!
565
- # @my_results.info('I101', 'verify_structure', "OCFL 3.3.1 version #{ver} does not contain a contentDirectory.")
566
582
  end
567
583
 
568
584
  # 12. Warn if any directories other than the expected 'content' directory are found in the version directory.
@@ -587,11 +603,8 @@ module OcflTools
587
603
  # @return {OcflTools::OcflResults} of verify events
588
604
  def verify_directory(version)
589
605
  # start by getting version format and directories.
590
- if @version_format.nil?
591
- @version_format = OcflTools::Utils::Files.get_version_format(@ocfl_object_root)
592
- end
606
+ get_version_format # sets @version_format, one way or another.
593
607
 
594
- # result = OcflTools.config.version_format % version.to_i
595
608
  version_name = @version_format % version.to_i
596
609
  # Make sure this directory actually exists.
597
610
  unless File.directory?("#{@ocfl_object_root}/#{version_name}")
@@ -653,44 +666,38 @@ module OcflTools
653
666
  def verify_inventory(inventory_file = "#{@ocfl_object_root}/inventory.json")
654
667
  # Load up the object with ocfl_inventory, push it through ocfl_verify.
655
668
  @my_results ||= OcflTools::OcflResults.new
656
- # Inventory file does not exist; create a results object, record this epic fail, and return.
657
- if File.exist?(inventory_file)
658
- if load_inventory("#{@ocfl_object_root}/inventory.json") == true
659
- @inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
660
- @verify = OcflTools::OcflVerify.new(@inventory)
661
- @verify.check_all # creates & returns @results object from OcflVerify
662
- else
663
- # The inventory had problems; we can't run verify.
664
- @my_results.error('E210', 'verify_inventory', "Unable to process inventory file #{inventory_file}.")
665
- return @my_results
666
- end
667
- else
668
- @my_results.error('E215', 'verify_inventory', "Expected inventory file #{inventory_file} not found.")
669
+ # If inventory_file does not exist, load_inventory will throw and log an E215.
670
+ begin
671
+ @inventory = load_inventory(inventory_file)
672
+ @inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
673
+ @verify = OcflTools::OcflVerify.new(@inventory)
674
+ @verify.check_all # creates & returns @results object from OcflVerify
675
+ # This could be OcflTools::Errors::ValidationError now.
676
+ rescue OcflTools::Errors::ValidationError
677
+ # I don't think we need to throw this E210 any more.
678
+ @my_results.error('E210', 'verify_inventory', "Unable to process inventory file #{inventory_file}.")
669
679
  return @my_results
670
680
  end
671
681
  end
672
682
 
673
683
  private
674
684
  # load up an inventory file and handle any errors.
675
- # Returns true if the inventory file is syntatically correct; false if otherwise.
685
+ # Returns an inventory file is syntatically correct; false if otherwise.
676
686
  def load_inventory(inventory_file)
677
- begin
678
- @my_results ||= OcflTools::OcflResults.new
679
- OcflTools::OcflInventory.new.from_file(inventory_file)
680
- return true
681
- rescue RuntimeError
682
- @my_results.error('E210', 'load_inventory', "Unable to read Inventory file #{inventory_file}")
683
- return false
684
- rescue OcflTools::Errors::Error211
685
- @my_results.error('E211', 'load_inventory', "#{inventory_file} is not valid JSON.")
686
- return false
687
- rescue OcflTools::Errors::Error216 => e
688
- @my_results.error('E216', 'load_inventory', "#{e} in #{inventory_file}")
689
- return false
690
- rescue OcflTools::Errors::Error217 => e
691
- @my_results.error('E217', 'load_inventory', "#{e} in #{inventory_file}")
692
- return false
693
- end
687
+ @my_results ||= OcflTools::OcflResults.new
688
+ OcflTools::OcflInventory.new.from_file(inventory_file)
689
+ # The generic 'something went wrong but I don't know what'; not sure if we should keep this.
690
+ rescue RuntimeError => e
691
+ @my_results.error('E210', 'load_inventory', "#{e}")
692
+ raise
693
+ rescue OcflTools::Errors::ValidationError => e
694
+ e.details.each do | code, messages |
695
+ # code is a string, messages is an array.
696
+ messages.each do | msg |
697
+ @my_results.error(code, 'load_inventory', msg)
698
+ end
699
+ end
700
+ raise # re-raise the error.
694
701
  end
695
702
 
696
703
  # Compares the state blocks for all versions across all inventories in the object,
@@ -784,13 +791,22 @@ module OcflTools
784
791
 
785
792
  def get_version_format
786
793
  begin
794
+ @my_results ||= OcflTools::OcflResults.new
787
795
  @version_format ||= OcflTools::Utils::Files.get_version_format(@ocfl_object_root)
788
796
  @my_results.ok('O111', 'version_format', 'OCFL conforming first version directory found.')
789
- return true
790
- rescue StandardError
797
+ return @version_format
798
+ rescue OcflTools::Errors::ValidationError => e
799
+ # OcflTools::Utils::Files.get_version_format doesn't set errors, so capture them here.
800
+ e.details.each do | code, messages |
801
+ # code is a string, messages is an array.
802
+ messages.each do | msg |
803
+ @my_results.error(code, 'load_inventory', msg)
804
+ end
805
+ end
806
+ # Add on another error explaining how we got here.
791
807
  @my_results.error('E111', 'version_format', 'OCFL unable to determine version format by inspection of directories.')
792
- @version_format = OcflTools.config.version_format
793
808
  @my_results.warn('W111', 'version_format', "Attempting to process using default value: #{OcflTools.config.version_format}")
809
+ @version_format = OcflTools.config.version_format
794
810
  return false
795
811
  end
796
812
  end
@@ -52,7 +52,8 @@ module OcflTools
52
52
  # Given an object root directory, deduce and return the version directories by inspecting disk.
53
53
  def self.get_version_directories(object_root_dir)
54
54
  unless Dir.exist?(object_root_dir) == true
55
- raise 'Directory does not exist!'
55
+ # This is a client error.
56
+ raise OcflTools::Errors::RequestedDirectoryNotFound, "#{object_root_dir} does not exist!"
56
57
  end
57
58
 
58
59
  object_root_dirs = []
@@ -62,7 +63,8 @@ module OcflTools
62
63
  object_root_dirs << file if File.directory? file
63
64
  end
64
65
  if object_root_dirs.empty?
65
- raise "No directories found in #{object_root_dir}!"
66
+ # This is a validation error.
67
+ raise OcflTools::Errors::ValidationError, details: { "E100" => ["#{object_root_dir} is empty!"] }
66
68
  end
67
69
 
68
70
  # Needs to call get version_format method here.
@@ -71,15 +73,18 @@ module OcflTools
71
73
  version_directories << i
72
74
  end
73
75
  end
74
- raise 'No version directories found!' if version_directories.empty?
75
-
76
+ # This is a validation error; we expect to find matching version directories.
77
+ if version_directories.empty?
78
+ raise OcflTools::Errors::ValidationError, details: { "E013" => ["#{object_root_dir} must contain at least one identifiable version directory."] }
79
+ end
76
80
  version_directories.sort! # sort it, to be nice.
77
81
  end
78
82
 
79
83
  # Given an object_root_directory, deduce the format used to describe version directories.
80
84
  def self.get_version_format(object_root_dir)
81
85
  unless Dir.exist?(object_root_dir) == true
82
- raise 'Directory does not exist!'
86
+ # This is a client error.
87
+ raise OcflTools::Errors::RequestedDirectoryNotFound, "#{object_root_dir} does not exist!"
83
88
  end
84
89
 
85
90
  # Get all directories starting with 'v', sort them.
@@ -98,14 +103,16 @@ module OcflTools
98
103
  first_version.slice!(0, 1) # cut the leading 'v' from the string.
99
104
  if first_version.length == 1 # A length of 1 for the first version implies 'v1'
100
105
  unless first_version.to_i == 1
101
- raise "#{object_root_dir}/#{first_version} is not the first version directory!"
106
+ # This is a validation error; there must be a v1 directory.
107
+ # E015 "OCFL 3.5.3 Expected version sequence not found. Expected version #{count}, found version #{my_versions[count]}."
108
+ raise OcflTools::Errors::ValidationError, details: { "E015" => ["Expected version 1 not found. Found version #{first_version.to_i} instead."] }
102
109
  end
103
110
 
104
111
  version_format = 'v%d'
105
112
  else
106
113
  # Make sure this is Integer 1.
107
114
  unless first_version.to_i == 1
108
- raise "#{object_root_dir}/#{first_version} is not the first version directory!"
115
+ raise OcflTools::Errors::ValidationError, details: { "E015" => ["Expected version 1 not found. Found version #{first_version.to_i} instead."] }
109
116
  end
110
117
 
111
118
  version_format = "v%0#{first_version.length}d"
@@ -170,24 +177,34 @@ module OcflTools
170
177
  # g_v_d returns a sorted array already. Reverse it, so we start with highest version.
171
178
  my_versions = OcflTools::Utils::Files.get_version_directories(object_root_dir).reverse
172
179
  case
180
+ # Return the inventory file in the highest version dir, if it exists.
173
181
  when File.exist?("#{object_root_dir}/#{my_versions[0]}/inventory.json")
174
182
  return "#{object_root_dir}/#{my_versions[0]}/inventory.json"
183
+ # Otherwise, return the inventory file in the root, if it exists.
175
184
  when File.exist?("#{object_root_dir}/inventory.json")
176
185
  return "#{object_root_dir}/inventory.json"
177
186
  else
178
- # Quit out here if there was only 1 version directory
187
+ # We don't have a highest-version inventory, and we don't have a root inventory.
188
+ # This is a problem! But there might be an inventory file in a non-highest-version dir.
189
+ # Quit out here if there was only 1 version directory (We've already checked this)
179
190
  unless my_versions.size > 1
180
- raise "No inventory file found in #{object_root_dir}!"
191
+ # This is a validation error; no inventory files found.
192
+ raise OcflTools::Errors::ValidationError, details: { "E215" => ["OCFL 3.1 Expected inventory file not found in #{object_root_dir} or discovered version directories."] }
181
193
  end
182
194
 
183
- my_versions.delete_at(0) # drop the first element.
195
+ my_versions.delete_at(0) # drop the first element (we've already checked it).
184
196
  my_versions.each do |v|
197
+ # Return the highest version inventory file we find.
198
+ # Note, this is technically a non-compliant object root, but we
199
+ # want to return a result if we have one - we might be attempting object
200
+ # recovery and something is better than nothing.
185
201
  if File.exist?("#{object_root_dir}/#{v}/inventory.json")
186
202
  return "#{object_root_dir}/#{v}/inventory.json"
187
203
  end
188
204
  end
189
- # If we get here, no inventory file found!
190
- raise "No inventory file found in #{object_root_dir}!"
205
+ # If we get here, no inventory file found in any version dirs or the object root.
206
+ # This is a validation error.
207
+ raise OcflTools::Errors::ValidationError, details: { "E215" => ["OCFL 3.1 Expected inventory file not found in #{object_root_dir} or discovered version directories."] }
191
208
  end
192
209
  end
193
210
  end
@@ -12,7 +12,7 @@ module OcflTools
12
12
  # @return [String or nil] the value of the requested key, or nil if not found.
13
13
  def self.get_value(inventory_file, key)
14
14
  unless %w[contentDirectory digestAlgorithm head type id].include?(key)
15
- raise "#{key} is not a valid OCFL inventory header key"
15
+ raise OcflTools::Errors::RequestedKeyNotFound, "#{key} is not a valid OCFL inventory header key"
16
16
  end
17
17
 
18
18
  inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
@@ -8,10 +8,10 @@ Gem::Specification.new do |gem|
8
8
  gem.homepage = 'https://github.com/sul-dlss-labs/OCFL-Tools'
9
9
  gem.licenses = ['Apache-2.0']
10
10
 
11
+ # Don't include fixtures in the gem.
11
12
  gem.files = `git ls-files -z`.split("\x0").reject do |f|
12
13
  f.match(%r{^(test|spec|features)/})
13
14
  end
14
- # gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
15
15
 
16
16
  gem.add_runtime_dependency 'anyway_config', '~> 1.0'
17
17
  gem.add_runtime_dependency 'fileutils', '~> 1.3'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ocfl-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.14
4
+ version: 0.9.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian M. Morley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-18 00:00:00.000000000 Z
11
+ date: 2020-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anyway_config
@@ -42,22 +42,22 @@ dependencies:
42
42
  name: json
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 2.2.0
48
45
  - - "~>"
49
46
  - !ruby/object:Gem::Version
50
47
  version: '2.2'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 2.2.0
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 2.2.0
58
55
  - - "~>"
59
56
  - !ruby/object:Gem::Version
60
57
  version: '2.2'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.2.0
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: pry-byebug
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -183,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  - !ruby/object:Gem::Version
184
184
  version: '0'
185
185
  requirements: []
186
- rubygems_version: 3.0.3
186
+ rubygems_version: 3.1.2
187
187
  signing_key:
188
188
  specification_version: 4
189
189
  summary: Tools to create, manipulate and write Oxford Common File Layout (OCFL) preservation