ocfl-tools 0.9.14 → 0.9.15

Sign up to get free protection for your applications and to get access to all the features.
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