moab-versioning 5.2.0 → 6.0.0.alpha

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: c20a012b9d9c481ddfeed15296e61aaf6648bed0cd1d0065a8b0662301e94a1b
4
- data.tar.gz: 8d5a96d608b7334f117b5ee9faa3cbe16f82f543bd3af0d23d11371df2181d76
3
+ metadata.gz: 6d208d4a3118a616d2ee2580e51d9a344438676755f5ef33ace82ed2dbc33bba
4
+ data.tar.gz: 7198c3f7450d8a6bb61d8b33d50404e9b0a07e52d47198f713b4400e1c2a4435
5
5
  SHA512:
6
- metadata.gz: 296fd7985772e8296f879e955066f7e3c71b00a6a83d9893aa124de5d900a427ca61d57b221f4227ae54210f3518111a5d99ae60db62ed9dbb25818871f8e87d
7
- data.tar.gz: 701adc9ee529e10fd608ed890f8f8a3f978cb3ec023f58d8a75bd11a0caa6e27f4631ec4f73c9280cf8de914da855d100be3cd077f029ec91fab4b07749c627c
6
+ metadata.gz: 884f1c7e2818e9bcb64de45939cda1ccc4a1aebf5015edd3bfefe707f3a4185253aaa629d4cc0340145be5fd2a72aac0931c7115e8a7929338dd362ff9aa894b
7
+ data.tar.gz: 2f23fc485263350eb667eed7871dc194972688151613d573227dfa3b2f5490d9f6ccc8f7625f0c4ac3fa66ac0ee01f3c470a24b6133443c89f080e90bae273aa
data/lib/moab/bagger.rb CHANGED
@@ -58,9 +58,9 @@ module Moab
58
58
  # @return [void] Generate the bagit.txt tag file
59
59
  def create_bagit_txt
60
60
  bag_pathname.mkpath
61
- bag_pathname.join("bagit.txt").open('w') do |f|
62
- f.puts "Tag-File-Character-Encoding: UTF-8"
63
- f.puts "BagIt-Version: 0.97"
61
+ bag_pathname.join('bagit.txt').open('w') do |f|
62
+ f.puts 'Tag-File-Character-Encoding: UTF-8'
63
+ f.puts 'BagIt-Version: 0.97'
64
64
  end
65
65
  end
66
66
 
@@ -209,7 +209,7 @@ module Moab
209
209
  if manifest_file[type]
210
210
  manifest_file[type].close
211
211
  manifest_pathname[type].delete if
212
- manifest_pathname[type].exist? && manifest_pathname[type].size == 0
212
+ manifest_pathname[type].exist? && manifest_pathname[type].empty?
213
213
  end
214
214
  end
215
215
  end
@@ -217,7 +217,7 @@ module Moab
217
217
  # @api internal
218
218
  # @return [void] Generate the bag-info.txt tag file
219
219
  def create_bag_info_txt
220
- bag_pathname.join("bag-info.txt").open('w') do |f|
220
+ bag_pathname.join('bag-info.txt').open('w') do |f|
221
221
  f.puts "External-Identifier: #{bag_inventory.package_id}"
222
222
  f.puts "Payload-Oxum: #{bag_inventory.byte_count}.#{bag_inventory.file_count}"
223
223
  f.puts "Bag-Size: #{bag_inventory.human_size}"
@@ -247,7 +247,7 @@ module Moab
247
247
  if manifest_file[type]
248
248
  manifest_file[type].close
249
249
  manifest_pathname[type].delete if
250
- manifest_pathname[type].exist? && manifest_pathname[type].size == 0
250
+ manifest_pathname[type].exist? && manifest_pathname[type].empty?
251
251
  end
252
252
  end
253
253
  end
@@ -23,7 +23,7 @@ module Moab
23
23
  # (see Serializable#initialize)
24
24
  def initialize(opts = {})
25
25
  @signature_hash = {}
26
- @data_source = ""
26
+ @data_source = ''
27
27
  @signatures_from_bag = nil # prevents later warning: instance variable @signatures_from_bag not initialized
28
28
  super(opts)
29
29
  end
@@ -158,14 +158,16 @@ module Moab
158
158
  end
159
159
  attr_reader :base_directory
160
160
 
161
+ # FIXME: shouldn't this method be named descendent_of_base?
161
162
  # @api internal
162
163
  # @param pathname [Pathname] The file path to be tested
163
164
  # @return [Boolean] Test whether the given path is contained within the {#base_directory}
164
165
  def is_descendent_of_base?(pathname)
165
- raise(MoabRuntimeError, "base_directory has not been set") if @base_directory.nil?
166
+ raise(MoabRuntimeError, 'base_directory has not been set') if @base_directory.nil?
166
167
 
167
168
  is_descendent = false
168
169
  pathname.expand_path.ascend { |ancestor| is_descendent ||= (ancestor == @base_directory) }
170
+ # FIXME: shouldn't it simply return false?
169
171
  raise(MoabRuntimeError, "#{pathname} is not a descendent of #{@base_directory}") unless is_descendent
170
172
 
171
173
  is_descendent
@@ -233,7 +233,7 @@ module Moab
233
233
  matching_paths.each do |path|
234
234
  fid = FileInstanceDifference.new(change: 'identical')
235
235
  fid.basis_path = path
236
- fid.other_path = "same"
236
+ fid.other_path = 'same'
237
237
  fid.signatures << signature
238
238
  subset_hash[:identical].files << fid
239
239
  end
@@ -286,7 +286,7 @@ module Moab
286
286
  matching_keys(basis_path_hash, other_path_hash).each do |path|
287
287
  fid = FileInstanceDifference.new(change: 'modified')
288
288
  fid.basis_path = path
289
- fid.other_path = "same"
289
+ fid.other_path = 'same'
290
290
  fid.signatures << basis_path_hash[path]
291
291
  fid.signatures << other_path_hash[path]
292
292
  subset_hash[:modified].files << fid
@@ -304,7 +304,7 @@ module Moab
304
304
  def tabulate_added_files(basis_path_hash, other_path_hash)
305
305
  other_only_keys(basis_path_hash, other_path_hash).each do |path|
306
306
  fid = FileInstanceDifference.new(change: 'added')
307
- fid.basis_path = ""
307
+ fid.basis_path = ''
308
308
  fid.other_path = path
309
309
  fid.signatures << other_path_hash[path]
310
310
  subset_hash[:added].files << fid
@@ -323,7 +323,7 @@ module Moab
323
323
  basis_only_keys(basis_path_hash, other_path_hash).each do |path|
324
324
  fid = FileInstanceDifference.new(change: 'deleted')
325
325
  fid.basis_path = path
326
- fid.other_path = ""
326
+ fid.other_path = ''
327
327
  fid.signatures << basis_path_hash[path]
328
328
  subset_hash[:deleted].files << fid
329
329
  end
@@ -234,9 +234,11 @@ module Moab
234
234
  count += 1
235
235
  end
236
236
  if count == 0
237
- format("%d B", size)
237
+ format('%d B', size)
238
238
  else
239
- format("%.2f %s", size, %w[B KB MB GB TB][count])
239
+ # rubocop:disable Style/FormatStringToken
240
+ format('%.2f %s', size, %w[B KB MB GB TB][count])
241
+ # rubocop:enable Style/FormatStringToken
240
242
  end
241
243
  end
242
244
 
@@ -245,13 +247,13 @@ module Moab
245
247
  # @return [String] The standard name for the serialized inventory file of the given type
246
248
  def self.xml_filename(type = nil)
247
249
  case type
248
- when "version"
250
+ when 'version'
249
251
  'versionInventory.xml'
250
- when "additions"
252
+ when 'additions'
251
253
  'versionAdditions.xml'
252
- when "manifests"
254
+ when 'manifests'
253
255
  'manifestInventory.xml'
254
- when "directory"
256
+ when 'directory'
255
257
  'directoryInventory.xml'
256
258
  else
257
259
  raise ArgumentError, "unknown inventory type: #{type}"
@@ -109,18 +109,18 @@ module Moab
109
109
 
110
110
  # @return [Hash] Serializes the data and then filters it to report only the changes
111
111
  def differences_detail
112
- #return self.summary if difference_count == 0
112
+ # return self.summary if difference_count == 0
113
113
  inv_diff = to_hash
114
- inv_diff["group_differences"].each_value do |group_diff|
114
+ inv_diff['group_differences'].each_value do |group_diff|
115
115
  delete_subsets = []
116
- group_diff["subsets"].each do |change_type, subset|
117
- delete_subsets << change_type if (change_type == "identical") || (subset["count"] == 0)
116
+ group_diff['subsets'].each do |change_type, subset|
117
+ delete_subsets << change_type if (change_type == 'identical') || (subset['count'] == 0)
118
118
  end
119
119
  delete_subsets.each do |change_type|
120
- group_diff["subsets"].delete(change_type)
121
- group_diff.delete(change_type) if change_type != "identical"
120
+ group_diff['subsets'].delete(change_type)
121
+ group_diff.delete(change_type) if change_type != 'identical'
122
122
  end
123
- group_diff.delete("subsets") if group_diff["subsets"].empty?
123
+ group_diff.delete('subsets') if group_diff['subsets'].empty?
124
124
  end
125
125
  inv_diff
126
126
  end
@@ -50,15 +50,15 @@ module Moab
50
50
 
51
51
  # @attribute
52
52
  # @return [String] The MD5 checksum value of the file
53
- attribute :md5, String, on_save: proc { |n| n.nil? ? "" : n.to_s }
53
+ attribute :md5, String, on_save: proc { |n| n.nil? ? '' : n.to_s }
54
54
 
55
55
  # @attribute
56
56
  # @return [String] The SHA1 checksum value of the file
57
- attribute :sha1, String, on_save: proc { |n| n.nil? ? "" : n.to_s }
57
+ attribute :sha1, String, on_save: proc { |n| n.nil? ? '' : n.to_s }
58
58
 
59
59
  # @attribute
60
60
  # @return [String] The SHA256 checksum value of the file
61
- attribute :sha256, String, on_save: proc { |n| n.nil? ? "" : n.to_s }
61
+ attribute :sha256, String, on_save: proc { |n| n.nil? ? '' : n.to_s }
62
62
 
63
63
  KNOWN_ALGOS = {
64
64
  md5: proc { Digest::MD5.new },
@@ -79,7 +79,7 @@ module Moab
79
79
 
80
80
  signatures = algos_to_use.to_h { |k| [k, KNOWN_ALGOS[k].call] }
81
81
 
82
- pathname.open("r") do |stream|
82
+ pathname.open('r') do |stream|
83
83
  while (buffer = stream.read(8192))
84
84
  signatures.each_value { |digest| digest.update(buffer) }
85
85
  end
@@ -124,7 +124,7 @@ module Moab
124
124
  # @param version_id [Integer] The version identifier of an object version
125
125
  # @return [String] The directory name of the version, relative to the digital object home directory (e.g v0002)
126
126
  def self.version_dirname(version_id)
127
- format("v%04d", version_id)
127
+ format('v%04d', version_id)
128
128
  end
129
129
 
130
130
  # @return [Array<Integer>] The list of all version ids for this object
@@ -193,7 +193,7 @@ module Moab
193
193
  # * Version 0 is a special case used to generate empty manifests
194
194
  # * Current version + 1 is used for creation of a new version
195
195
  def storage_object_version(version_id)
196
- raise(MoabRuntimeError, "Version ID not specified") unless version_id
196
+ raise(MoabRuntimeError, 'Version ID not specified') unless version_id
197
197
 
198
198
  StorageObjectVersion.new(self, version_id)
199
199
  end
@@ -6,17 +6,17 @@ module Moab
6
6
  # Given a druid path, are the contents actually a well-formed Moab?
7
7
  # Shameless green: repetitious code included.
8
8
  class StorageObjectValidator
9
- METADATA_DIR = "metadata"
10
- CONTENT_DIR = "content"
9
+ METADATA_DIR = 'metadata'
10
+ CONTENT_DIR = 'content'
11
11
  EXPECTED_DATA_SUB_DIRS = [CONTENT_DIR, METADATA_DIR].freeze
12
12
  IMPLICIT_DIRS = ['.', '..'].freeze # unlike Find.find, Dir.entries returns the current/parent dirs
13
- DATA_DIR = "data"
13
+ DATA_DIR = 'data'
14
14
  MANIFESTS_DIR = 'manifests'
15
15
  EXPECTED_VERSION_SUB_DIRS = [DATA_DIR, MANIFESTS_DIR].freeze
16
- MANIFEST_INVENTORY_PATH = File.join(MANIFESTS_DIR, "manifestInventory.xml").freeze
17
- SIGNATURE_CATALOG_PATH = File.join(MANIFESTS_DIR, "signatureCatalog.xml").freeze
16
+ MANIFEST_INVENTORY_PATH = File.join(MANIFESTS_DIR, 'manifestInventory.xml').freeze
17
+ SIGNATURE_CATALOG_PATH = File.join(MANIFESTS_DIR, 'signatureCatalog.xml').freeze
18
18
 
19
- FORBIDDEN_CONTENT_SUB_DIRS = ([DATA_DIR, MANIFESTS_DIR] + EXPECTED_DATA_SUB_DIRS).freeze
19
+ VERSION_DIR_PATTERN = /^v\d{4}$/
20
20
 
21
21
  # error codes
22
22
  INCORRECT_DIR_CONTENTS = 0
@@ -26,13 +26,12 @@ module Moab
26
26
  NO_SIGNATURE_CATALOG = 4
27
27
  NO_MANIFEST_INVENTORY = 5
28
28
  NO_FILES_IN_MANIFEST_DIR = 6
29
- VERSIONS_NOT_IN_ORDER = 7
29
+ TEST_OBJECT_VERSIONS_NOT_IN_ORDER = 7
30
30
  METADATA_SUB_DIRS_DETECTED = 8
31
31
  FILES_IN_VERSION_DIR = 9
32
32
  NO_FILES_IN_METADATA_DIR = 10
33
33
  NO_FILES_IN_CONTENT_DIR = 11
34
34
  CONTENT_SUB_DIRS_DETECTED = 12
35
- BAD_SUB_DIR_IN_CONTENT_DIR = 13
36
35
 
37
36
  attr_reader :storage_obj_path
38
37
 
@@ -52,48 +51,43 @@ module Moab
52
51
  def self.error_code_to_messages
53
52
  @error_code_to_messages ||=
54
53
  {
55
- INCORRECT_DIR_CONTENTS => "Incorrect items under %{addl} directory",
56
- MISSING_DIR => "Missing directory: %{addl}",
57
- EXTRA_CHILD_DETECTED => "Unexpected item in path: %{addl}",
58
- VERSION_DIR_BAD_FORMAT => "Version directory name not in 'v00xx' format: %{addl}",
59
- FILES_IN_VERSION_DIR => "Version directory %{addl} should not contain files; only the manifests and data directories",
60
- NO_SIGNATURE_CATALOG => "Version %{addl}: Missing signatureCatalog.xml",
61
- NO_MANIFEST_INVENTORY => "Version %{addl}: Missing manifestInventory.xml",
62
- NO_FILES_IN_MANIFEST_DIR => "Version %{addl}: No files present in manifest dir",
63
- METADATA_SUB_DIRS_DETECTED => "Version %{version}: metadata directory should only contain files, not directories. Found directory: %{dir}",
64
- VERSIONS_NOT_IN_ORDER => "Should contain only sequential version directories. Current directories: %{addl}",
65
- NO_FILES_IN_METADATA_DIR => "Version %{addl}: No files present in metadata dir",
66
- NO_FILES_IN_CONTENT_DIR => "Version %{addl}: No files present in content dir",
67
- CONTENT_SUB_DIRS_DETECTED => "Version %{version}: content directory should only contain files, not directories. Found directory: %{dir}",
68
- BAD_SUB_DIR_IN_CONTENT_DIR => "Version %{addl}: content directory has forbidden sub-directory name: vnnnn or #{FORBIDDEN_CONTENT_SUB_DIRS}"
54
+ INCORRECT_DIR_CONTENTS => 'Incorrect items under %<addl>s directory',
55
+ MISSING_DIR => 'Missing directory: %<addl>s',
56
+ EXTRA_CHILD_DETECTED => 'Unexpected item in path: %<addl>s',
57
+ VERSION_DIR_BAD_FORMAT => "Version directory name not in 'v00xx' format: %<addl>s",
58
+ FILES_IN_VERSION_DIR => 'Version directory %<addl>s should not contain files; only the manifests and data directories',
59
+ NO_SIGNATURE_CATALOG => 'Version %<addl>s: Missing signatureCatalog.xml',
60
+ NO_MANIFEST_INVENTORY => 'Version %<addl>s: Missing manifestInventory.xml',
61
+ NO_FILES_IN_MANIFEST_DIR => 'Version %<addl>s: No files present in manifest dir',
62
+ METADATA_SUB_DIRS_DETECTED => 'Version %<version>s: metadata directory should only contain files, not directories. Found directory: %<dir>s',
63
+ TEST_OBJECT_VERSIONS_NOT_IN_ORDER => 'Should contain only sequential version directories. Current directories: %<addl>s',
64
+ NO_FILES_IN_METADATA_DIR => 'Version %<addl>s: No files present in metadata dir',
65
+ NO_FILES_IN_CONTENT_DIR => 'Version %<addl>s: No files present in content dir',
66
+ CONTENT_SUB_DIRS_DETECTED => 'Version %<version>s: content directory should only contain files, not directories. Found directory: %<dir>s'
69
67
  }.freeze
70
68
  end
71
69
 
72
70
  private
73
71
 
74
72
  def version_directories
75
- @vdirs ||= directory_entries(storage_obj_path)
73
+ @version_directories ||= directory_entries(storage_obj_path)
76
74
  end
77
75
 
78
76
  def check_correctly_named_version_dirs
79
77
  errors = []
80
78
  errors << result_hash(MISSING_DIR, 'no versions exist') unless version_directories.count > 0
81
79
  version_directories.each do |version_dir|
82
- errors << result_hash(VERSION_DIR_BAD_FORMAT, version_dir) unless version_dir_format?(version_dir)
80
+ errors << result_hash(VERSION_DIR_BAD_FORMAT, version_dir) unless VERSION_DIR_PATTERN.match?(version_dir)
83
81
  end
84
82
  errors
85
83
  end
86
84
 
87
- def version_dir_format?(dirname)
88
- dirname =~ /^v\d{4}$/
89
- end
90
-
91
85
  # call only if the version directories are "correctly named" vdddd
92
86
  def check_sequential_version_dirs
93
87
  version_directories.each_with_index do |dir_name, index|
94
88
  next if dir_name[1..].to_i == index + 1 # version numbering starts at 1, array indexing at 0
95
89
 
96
- return [result_hash(VERSIONS_NOT_IN_ORDER, version_directories)]
90
+ return [result_hash(TEST_OBJECT_VERSIONS_NOT_IN_ORDER, version_directories)]
97
91
  end
98
92
  []
99
93
  end
@@ -151,20 +145,9 @@ module Moab
151
145
  if content_sub_dir && !allow_content_subdirs
152
146
  errors << result_hash(CONTENT_SUB_DIRS_DETECTED, version: basename(version_path), dir: content_sub_dir)
153
147
  end
154
- if allow_content_subdirs && contains_sub_dir?(content_dir_path) && contains_forbidden_content_sub_dir?(content_dir_path)
155
- errors << result_hash(BAD_SUB_DIR_IN_CONTENT_DIR, basename(version_path))
156
- end
157
148
  errors
158
149
  end
159
150
 
160
- def contains_forbidden_content_sub_dir?(path)
161
- sub_dirs(path).detect { |sub_dir| content_sub_dir_forbidden?(sub_dir) }
162
- end
163
-
164
- def content_sub_dir_forbidden?(dirname)
165
- FORBIDDEN_CONTENT_SUB_DIRS.include?(dirname) || version_dir_format?(dirname)
166
- end
167
-
168
151
  def check_metadata_dir_files_only(version_path)
169
152
  errors = []
170
153
  metadata_dir_path = File.join(version_path, DATA_DIR, METADATA_DIR)
@@ -174,7 +174,7 @@ module Moab
174
174
  # @return [void] link or copy the specified file from source location to the version directory
175
175
  def ingest_file(source_file, target_dir, use_links = true)
176
176
  if use_links
177
- FileUtils.link(source_file.to_s, target_dir.to_s) #, :force => true)
177
+ FileUtils.link(source_file.to_s, target_dir.to_s)
178
178
  else
179
179
  FileUtils.copy(source_file.to_s, target_dir.to_s)
180
180
  end
@@ -225,7 +225,7 @@ module Moab
225
225
  # @return [VerificationResult] return true if the manifest inventory matches the actual files
226
226
  def verify_manifest_inventory
227
227
  # read/parse manifestInventory.xml
228
- result = VerificationResult.new("manifest_inventory")
228
+ result = VerificationResult.new('manifest_inventory')
229
229
  manifest_inventory = file_inventory('manifests')
230
230
  result.subentities << VerificationResult.verify_value('composite_key', composite_key, manifest_inventory.composite_key)
231
231
  result.subentities << VerificationResult.verify_truth('manifests_group', !manifest_inventory.group_empty?('manifests'))
@@ -233,7 +233,7 @@ module Moab
233
233
  directory_inventory = FileInventory.new.inventory_from_directory(@version_pathname.join('manifests'), 'manifests')
234
234
  directory_inventory.digital_object_id = storage_object.digital_object_id
235
235
  directory_group = directory_inventory.group('manifests')
236
- directory_group.remove_file_having_path("manifestInventory.xml")
236
+ directory_group.remove_file_having_path('manifestInventory.xml')
237
237
  # compare the measured signatures against the values in manifestInventory.xml
238
238
  diff = FileInventoryDifference.new
239
239
  diff.compare(manifest_inventory, directory_inventory)
@@ -247,7 +247,7 @@ module Moab
247
247
 
248
248
  # @return [VerificationResult]
249
249
  def verify_signature_catalog
250
- result = VerificationResult.new("signature_catalog")
250
+ result = VerificationResult.new('signature_catalog')
251
251
  signature_catalog = self.signature_catalog
252
252
  result.subentities << VerificationResult.verify_value('signature_key', composite_key, signature_catalog.composite_key)
253
253
  found = 0
@@ -261,7 +261,7 @@ module Moab
261
261
  missing << storage_location.to_s
262
262
  end
263
263
  end
264
- file_result = VerificationResult.new("storage_location")
264
+ file_result = VerificationResult.new('storage_location')
265
265
  file_result.verified = (found == signature_catalog.file_count)
266
266
  file_result.details = {
267
267
  'expected' => signature_catalog.file_count,
@@ -275,7 +275,7 @@ module Moab
275
275
 
276
276
  # @return [Boolean] true if files & signatures listed in version inventory can all be found
277
277
  def verify_version_inventory
278
- result = VerificationResult.new("version_inventory")
278
+ result = VerificationResult.new('version_inventory')
279
279
  version_inventory = file_inventory('version')
280
280
  result.subentities << VerificationResult.verify_value('inventory_key', composite_key, version_inventory.composite_key)
281
281
  signature_catalog = self.signature_catalog
@@ -295,7 +295,7 @@ module Moab
295
295
  end
296
296
  end
297
297
  end
298
- file_result = VerificationResult.new("catalog_entry")
298
+ file_result = VerificationResult.new('catalog_entry')
299
299
  file_result.verified = (found == version_inventory.file_count)
300
300
  file_result.details = {
301
301
  'expected' => version_inventory.file_count,
@@ -309,7 +309,7 @@ module Moab
309
309
 
310
310
  # @return [Boolean] returns true if files in data folder match files listed in version addtions inventory
311
311
  def verify_version_additions
312
- result = VerificationResult.new("version_additions")
312
+ result = VerificationResult.new('version_additions')
313
313
  version_additions = file_inventory('additions')
314
314
  result.subentities << VerificationResult.verify_value('composite_key', composite_key, version_additions.composite_key)
315
315
  data_directory = @version_pathname.join('data')
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'find' # for object_size
4
+
3
5
  module Moab
4
6
  # A class to represent the SDR repository store
5
7
  #
@@ -20,10 +22,10 @@ module Moab
20
22
  # @return [Array<Pathname>] The list of filesystem root paths in which objects are stored
21
23
  def storage_roots
22
24
  unless defined?(@storage_roots)
23
- raise(MoabRuntimeError, "Moab::Config.storage_roots not found in config file") if Moab::Config.storage_roots.nil?
25
+ raise(MoabRuntimeError, 'Moab::Config.storage_roots not found in config file') if Moab::Config.storage_roots.nil?
24
26
 
25
27
  @storage_roots = [Moab::Config.storage_roots].flatten.collect { |filesystem| Pathname(filesystem) }
26
- raise(MoabRuntimeError, "Moab::Config.storage_roots empty") if @storage_roots.empty?
28
+ raise(MoabRuntimeError, 'Moab::Config.storage_roots empty') if @storage_roots.empty?
27
29
 
28
30
  @storage_roots.each { |root| raise(MoabRuntimeError, "Storage root #{root} not found on system") unless root.exist? }
29
31
  end
@@ -33,7 +35,7 @@ module Moab
33
35
  # @return [String] The trunk segment of the object storage path
34
36
  def storage_trunk
35
37
  unless defined?(@storage_trunk)
36
- raise(MoabRuntimeError, "Moab::Config.storage_trunk not found in config file") if Moab::Config.storage_trunk.nil?
38
+ raise(MoabRuntimeError, 'Moab::Config.storage_trunk not found in config file') if Moab::Config.storage_trunk.nil?
37
39
 
38
40
  @storage_trunk = Moab::Config.storage_trunk
39
41
  end
@@ -43,7 +45,7 @@ module Moab
43
45
  # @param object_id [String] The identifier of the digital object
44
46
  # @return [String] The branch segment of the object storage path
45
47
  def storage_branch(object_id)
46
- #todo This method should be customized, or overridden in a subclass
48
+ # TODO: This method should be customized, or overridden in a subclass
47
49
  # split a object ID into 2-character segments, followed by a copy of the object ID
48
50
  # for a more sophisticated pairtree implementation see https://github.com/microservices/pairtree
49
51
  path_segments = object_id.scan(/..?/) << object_id
@@ -103,34 +105,6 @@ module Moab
103
105
  create_storage_object(object_id, root)
104
106
  end
105
107
 
106
- # @param object_id [String] The identifier of the digital object
107
- # @param include_deposit [Boolean] specifies whether to look in deposit areas for objects in process of initial ingest
108
- # @return [Array<StorageObject>] Representations of a digitial object's storage directories, or an empty array if none found.
109
- def search_storage_objects(object_id, include_deposit = false)
110
- storage_objects = []
111
- # Search for the object's home directory in the storage areas
112
- branch = storage_branch(object_id)
113
- storage_roots.each do |root|
114
- root_trunk = root.join(storage_trunk)
115
- raise(MoabRuntimeError, "Storage area not found at #{root_trunk}") unless root_trunk.exist?
116
-
117
- root_trunk_branch = root_trunk.join(branch)
118
- storage_objects << create_storage_object(object_id, root) if root_trunk_branch.exist?
119
- end
120
- # Search for the object's directory in the deposit areas
121
- if include_deposit && deposit_trunk
122
- branch = deposit_branch(object_id)
123
- storage_roots.each do |root|
124
- root_trunk = root.join(deposit_trunk)
125
- raise(MoabRuntimeError, "Deposit area not found at #{root_trunk}") unless root_trunk.exist?
126
-
127
- root_trunk_branch = root_trunk.join(branch)
128
- storage_objects << create_storage_object(object_id, root) if root_trunk_branch.exist?
129
- end
130
- end
131
- storage_objects
132
- end
133
-
134
108
  # @param object_id [String] The identifier of the digital object whose size is desired
135
109
  # @param include_deposit [Boolean] specifies whether to look in deposit areas for objects in process of initial ingest
136
110
  # @return [Integer] the size occupied on disk by the storage object, in bytes. this is the entire moab (all versions).
@@ -13,81 +13,83 @@ module Moab
13
13
  # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
14
14
  # All rights reserved. See {file:LICENSE.rdoc} for details.
15
15
  class StorageServices
16
- # @return [StorageRepository] an instance of the interface to SDR storage
16
+ # @note After some discussion, consensus was that this is a thread safe use of a
17
+ # class variable, as 1) it's never mutated after the class is initialized, and 2) the
18
+ # value of the StorageRepository instance is determined from configuration that
19
+ # rarely changes and is loaded once at app start time (at least in Stanford's
20
+ # consumers; see Moab::Config.configure calls in preservation_robots, preservation_catalog,
21
+ # and technical-metadata-service).
22
+ # Sidekiq requires thread safe code, so please preserve thread safety for multiple
23
+ # concurrent callers of this service if refactoring, so Sidekiq remains an option for
24
+ # ActiveJob backend for moab-versioning consumers.
17
25
  @@repository = Moab::StorageRepository.new
18
26
 
27
+ # @return [StorageRepository] an instance of the interface to SDR storage
19
28
  def self.repository
20
29
  @@repository
21
30
  end
22
31
 
23
32
  # @return [Array<Pathname>] A list of the filesystems currently used for storage
24
33
  def self.storage_roots
25
- @@repository.storage_roots
34
+ repository.storage_roots
26
35
  end
27
36
 
28
37
  # @return [String] The trunk segment of the object deposit path
29
38
  def self.deposit_trunk
30
- @@repository.deposit_trunk
39
+ repository.deposit_trunk
31
40
  end
32
41
 
33
42
  # @param object_id [String] The identifier of the digital object
34
43
  # @return [Pathname] The branch segment of the object deposit path
35
44
  def self.deposit_branch(object_id)
36
- @@repository.deposit_branch(object_id)
45
+ repository.deposit_branch(object_id)
37
46
  end
38
47
 
39
48
  # @param object_id [String] The identifier of the digital object
40
49
  # @param [Object] include_deposit
41
50
  # @return [StorageObject] The representation of a digitial object's storage directory, which might not exist yet.
42
51
  def self.find_storage_object(object_id, include_deposit = false)
43
- @@repository.find_storage_object(object_id, include_deposit)
44
- end
45
-
46
- # @param object_id [String] The identifier of the digital object
47
- # @param [Object] include_deposit
48
- # @return [Array<StorageObject>] Representations of a digitial object's storage directories, or an empty array if none found.
49
- def self.search_storage_objects(object_id, include_deposit = false)
50
- @@repository.search_storage_objects(object_id, include_deposit)
52
+ repository.find_storage_object(object_id, include_deposit)
51
53
  end
52
54
 
53
55
  # @param object_id [String] The identifier of the digital object whose size is desired
54
56
  # @param include_deposit [Boolean] specifies whether to look in deposit areas for objects in process of initial ingest
55
57
  # @return [Integer] the size occupied on disk by the storage object, in bytes. this is the entire moab (all versions).
56
58
  def self.object_size(object_id, include_deposit = false)
57
- @@repository.object_size(object_id, include_deposit)
59
+ repository.object_size(object_id, include_deposit)
58
60
  end
59
61
 
60
62
  # @param object_id [String] The identifier of the digital object whose version is desired
61
63
  # @param create [Boolean] If true, the object home directory should be created if it does not exist
62
64
  # @return [StorageObject] The representation of a digitial object's storage directory, which must exist.
63
65
  def self.storage_object(object_id, create = false)
64
- @@repository.storage_object(object_id, create)
66
+ repository.storage_object(object_id, create)
65
67
  end
66
68
 
67
69
  # @param object_id [String] The digital object identifier of the object
68
70
  # @return [String] the location of the storage object
69
71
  def self.object_path(object_id)
70
- @@repository.storage_object(object_id).object_pathname.to_s
72
+ repository.storage_object(object_id).object_pathname.to_s
71
73
  end
72
74
 
73
75
  # @param object_id [String] The digital object identifier of the object
74
76
  # @param [Integer] version_id The ID of the version, if nil use latest version
75
77
  # @return [String] the location of the storage object version
76
78
  def self.object_version_path(object_id, version_id = nil)
77
- @@repository.storage_object(object_id).find_object_version(version_id).version_pathname.to_s
79
+ repository.storage_object(object_id).find_object_version(version_id).version_pathname.to_s
78
80
  end
79
81
 
80
82
  # @param object_id [String] The digital object identifier
81
83
  # @return [Integer] The version number of the currently highest version
82
84
  def self.current_version(object_id)
83
- @@repository.storage_object(object_id).current_version_id
85
+ repository.storage_object(object_id).current_version_id
84
86
  end
85
87
 
86
88
  # @param [String] object_id The digital object identifier of the object
87
89
  # @param [Integer] version_id The ID of the version, if nil use latest version
88
90
  # @return [FileInventory] the file inventory for the specified object version
89
91
  def self.retrieve_file_group(file_category, object_id, version_id = nil)
90
- storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
92
+ storage_object_version = repository.storage_object(object_id).find_object_version(version_id)
91
93
  inventory_type = if file_category =~ /manifest/
92
94
  file_category = 'manifests'
93
95
  else
@@ -103,7 +105,7 @@ module Moab
103
105
  # @param [Integer] version_id The ID of the version, if nil use latest version
104
106
  # @return [Pathname] Pathname object containing the full path for the specified file
105
107
  def self.retrieve_file(file_category, file_id, object_id, version_id = nil)
106
- storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
108
+ storage_object_version = repository.storage_object(object_id).find_object_version(version_id)
107
109
  storage_object_version.find_filepath(file_category, file_id)
108
110
  end
109
111
 
@@ -113,7 +115,7 @@ module Moab
113
115
  # @param [Integer] version_id The ID of the version, if nil use latest version
114
116
  # @return [Pathname] Pathname object containing the full path for the specified file
115
117
  def self.retrieve_file_using_signature(file_category, file_signature, object_id, version_id = nil)
116
- storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
118
+ storage_object_version = repository.storage_object(object_id).find_object_version(version_id)
117
119
  storage_object_version.find_filepath_using_signature(file_category, file_signature)
118
120
  end
119
121
 
@@ -123,7 +125,7 @@ module Moab
123
125
  # @param [Integer] version_id The ID of the version, if nil use latest version
124
126
  # @return [FileSignature] The signature of the file
125
127
  def self.retrieve_file_signature(file_category, file_id, object_id, version_id = nil)
126
- storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
128
+ storage_object_version = repository.storage_object(object_id).find_object_version(version_id)
127
129
  storage_object_version.find_signature(file_category, file_id)
128
130
  end
129
131
 
@@ -132,8 +134,8 @@ module Moab
132
134
  # @param [Object] compare_version_id The identifier of the version to be compared to the base version
133
135
  # @return [FileInventoryDifference] The report of the version differences
134
136
  def self.version_differences(object_id, base_version_id, compare_version_id)
135
- base_version = @@repository.storage_object(object_id).storage_object_version(base_version_id)
136
- compare_version = @@repository.storage_object(object_id).storage_object_version(compare_version_id)
137
+ base_version = repository.storage_object(object_id).storage_object_version(base_version_id)
138
+ compare_version = repository.storage_object(object_id).storage_object_version(compare_version_id)
137
139
  base_inventory = base_version.file_inventory('version')
138
140
  compare_inventory = compare_version.file_inventory('version')
139
141
  FileInventoryDifference.new.compare(base_inventory, compare_inventory)
data/lib/moab/utc_time.rb CHANGED
@@ -7,7 +7,7 @@ module Moab
7
7
  # @return [void] Convert input datetime to a Time object, or nil if input is empty.
8
8
  def self.input(datetime)
9
9
  case datetime
10
- when nil, ""
10
+ when nil, ''
11
11
  nil
12
12
  when String
13
13
  Time.parse(datetime)
@@ -22,8 +22,8 @@ module Moab
22
22
  # @return [String] Convert the datetime into a ISO 8601 formatted string
23
23
  def self.output(datetime)
24
24
  case datetime
25
- when nil, ""
26
- ""
25
+ when nil, ''
26
+ ''
27
27
  when String
28
28
  Time.parse(datetime).utc.iso8601
29
29
  when Time
@@ -22,7 +22,7 @@ module Moab
22
22
  @entity = entity.to_s # force to string
23
23
  @verified = !!verified # rubocop:disable Style/DoubleNegation
24
24
  @details = details
25
- @subentities = Array.new
25
+ @subentities = []
26
26
  end
27
27
 
28
28
  # @param entity [#to_s] The name of the entity being verified
@@ -59,9 +59,9 @@ module Serializer
59
59
  def self.write_xml_file(xml_object, parent_dir, filename = nil)
60
60
  parent_dir.mkpath
61
61
  xml_pathname(parent_dir, filename).open('w') do |f|
62
- xmlBuilder = Nokogiri::XML::Builder.new(encoding: 'UTF-8')
63
- xmlBuilder = xml_object.to_xml(xmlBuilder)
64
- f << xmlBuilder.to_xml
62
+ xml_builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8')
63
+ xml_builder = xml_object.to_xml(xml_builder)
64
+ f << xml_builder.to_xml
65
65
  end
66
66
  nil
67
67
  end
@@ -120,7 +120,7 @@ module Serializer
120
120
  # @param other [Serializable] The other object being compared
121
121
  # @return [Hash] Generate a hash containing the differences between two objects of the same type
122
122
  def diff(other)
123
- raise(Moab::MoabRuntimeError, "Cannot compare different classes") if self.class != other.class
123
+ raise(Moab::MoabRuntimeError, 'Cannot compare different classes') if self.class != other.class
124
124
 
125
125
  left = other.to_hash
126
126
  right = to_hash
data/lib/serializer.rb CHANGED
@@ -19,7 +19,6 @@ require 'nokogiri'
19
19
  require 'happymapper'
20
20
  require 'json'
21
21
  require 'psych'
22
- #require 'json/pure'
23
22
  require 'pathname'
24
23
  require 'fileutils'
25
24
  require 'time'
@@ -23,7 +23,7 @@ module Stanford
23
23
  # Many of these objects have contentMetadata with no child elements, such as this:
24
24
  # <contentMetadata objectId="bd608mj3166" type="file"/>
25
25
  # but there are also objects that have no datasteam of this name at all
26
- cm_inventory = Moab::FileInventory.new(type: "version", digital_object_id: object_id, version_id: version_id)
26
+ cm_inventory = Moab::FileInventory.new(type: 'version', digital_object_id: object_id, version_id: version_id)
27
27
  content_group = group_from_cm(content_metadata, subset)
28
28
  cm_inventory.groups << content_group
29
29
  cm_inventory
@@ -45,7 +45,7 @@ module Stanford
45
45
  when 'shelve'
46
46
  ng_doc.xpath("//file[@shelve='yes']")
47
47
  when 'all'
48
- ng_doc.xpath("//file")
48
+ ng_doc.xpath('//file')
49
49
  else
50
50
  raise(Moab::MoabRuntimeError, "Unknown disposition subset (#{subset})")
51
51
  end
@@ -86,7 +86,7 @@ module Stanford
86
86
  instance.path = node.attributes['id'].content
87
87
  instance.datetime = begin
88
88
  node.attributes['datetime'].content
89
- rescue
89
+ rescue StandardError
90
90
  nil
91
91
  end
92
92
  instance
@@ -98,8 +98,8 @@ module Stanford
98
98
  # @example {include:file:spec/features/stanford/content_metadata_write_spec.rb}
99
99
  def generate_content_metadata(file_group, object_id, version_id)
100
100
  cm = Nokogiri::XML::Builder.new do |xml|
101
- xml.contentMetadata(type: "sample", objectId: object_id) do
102
- xml.resource(type: "version", sequence: "1", id: "version-#{version_id}") do
101
+ xml.contentMetadata(type: 'sample', objectId: object_id) do
102
+ xml.resource(type: 'version', sequence: '1', id: "version-#{version_id}") do
103
103
  file_group.files.each do |file_manifestation|
104
104
  signature = file_manifestation.signature
105
105
  file_manifestation.instances.each do |instance|
@@ -112,9 +112,9 @@ module Stanford
112
112
  preserve: 'yes'
113
113
  ) do
114
114
  fixity = signature.fixity
115
- xml.checksum(type: "MD5") { xml.text signature.md5 } if fixity[:md5]
116
- xml.checksum(type: "SHA-1") { xml.text signature.sha1 } if fixity[:sha1]
117
- xml.checksum(type: "SHA-256") { xml.text signature.sha256 } if fixity[:sha256]
115
+ xml.checksum(type: 'MD5') { xml.text signature.md5 } if fixity[:md5]
116
+ xml.checksum(type: 'SHA-1') { xml.text signature.sha1 } if fixity[:sha1]
117
+ xml.checksum(type: 'SHA-256') { xml.text signature.sha256 } if fixity[:sha256]
118
118
  end
119
119
  end
120
120
  end
@@ -139,16 +139,16 @@ module Stanford
139
139
  result = []
140
140
  content_metadata_doc =
141
141
  case content_metadata.class.name
142
- when "String"
142
+ when 'String'
143
143
  Nokogiri::XML(content_metadata)
144
- when "Pathname"
144
+ when 'Pathname'
145
145
  Nokogiri::XML(content_metadata.read)
146
- when "Nokogiri::XML::Document"
146
+ when 'Nokogiri::XML::Document'
147
147
  content_metadata
148
148
  else
149
- raise Moab::InvalidMetadataException, "Content Metadata is in unrecognized format"
149
+ raise Moab::InvalidMetadataException, 'Content Metadata is in unrecognized format'
150
150
  end
151
- nodeset = content_metadata_doc.xpath("//file")
151
+ nodeset = content_metadata_doc.xpath('//file')
152
152
  nodeset.each do |file_node|
153
153
  missing = %w[id size md5 sha1]
154
154
  missing.delete('id') if file_node.has_attribute?('id')
@@ -184,7 +184,7 @@ module Stanford
184
184
  @type_for_name = Moab::FileSignature.checksum_type_for_name
185
185
  @names_for_type = Moab::FileSignature.checksum_names_for_type
186
186
  ng_doc = Nokogiri::XML(content_metadata, &:noblanks)
187
- nodeset = ng_doc.xpath("//file")
187
+ nodeset = ng_doc.xpath('//file')
188
188
  nodeset.each do |file_node|
189
189
  filepath = file_node['id']
190
190
  signature = signature_for_path[filepath]
@@ -3,9 +3,22 @@
3
3
  module Stanford
4
4
  # An interface class to support access to SDR storage via a RESTful server
5
5
  class StorageServices < Moab::StorageServices
6
- # @return [StorageRepository] an instance of the interface to SDR storage
6
+ # @note After some discussion, consensus was that this is a thread safe use of a
7
+ # class variable, as 1) it's never mutated after the class is initialized, and 2) the
8
+ # value of the StorageRepository instance is determined from configuration that
9
+ # rarely changes and is loaded once at app start time (at least in Stanford's
10
+ # consumers; see Moab::Config.configure calls in preservation_robots, preservation_catalog,
11
+ # and technical-metadata-service).
12
+ # Sidekiq requires thread safe code, so please preserve thread safety for multiple
13
+ # concurrent callers of this service if refactoring, so Sidekiq remains an option for
14
+ # ActiveJob backend for moab-versioning consumers.
7
15
  @@repository = Stanford::StorageRepository.new
8
16
 
17
+ # @return [StorageRepository] an instance of the interface to SDR storage
18
+ def self.repository
19
+ @@repository
20
+ end
21
+
9
22
  # @param new_content_metadata [String] The content metadata to be compared to the base
10
23
  # @param object_id [String] The digital object identifier of the object whose version inventory is the basis of the
11
24
  # comparison
@@ -42,7 +55,7 @@ module Stanford
42
55
  begin
43
56
  # ObjectNotFoundException is raised if the object does not exist in storage
44
57
  version_id ||= current_version(object_id)
45
- storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
58
+ storage_object_version = repository.storage_object(object_id).find_object_version(version_id)
46
59
  signature_catalog = storage_object_version.signature_catalog
47
60
  rescue Moab::ObjectNotFoundException
48
61
  storage_object = Moab::StorageObject.new(object_id, 'dummy')
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moab-versioning
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 6.0.0.alpha
5
5
  platform: ruby
6
6
  authors:
7
- - Darren Weber
8
- - Richard Anderson
9
- - Lynn McRae
10
- - Hannah Frost
11
- autorequire:
7
+ - Naomi Dushay
8
+ - Justin Coyne
9
+ - Tony Zanella
10
+ - Mike Giarlo
11
+ - John Martin
12
+ autorequire:
12
13
  bindir: bin
13
14
  cert_chain: []
14
- date: 2022-08-02 00:00:00.000000000 Z
15
+ date: 2023-01-17 00:00:00.000000000 Z
15
16
  dependencies:
16
17
  - !ruby/object:Gem::Dependency
17
18
  name: druid-tools
@@ -169,7 +170,11 @@ dependencies:
169
170
  version: '0'
170
171
  description: Contains classes to process digital object version content and metadata
171
172
  email:
172
- - darren.weber@stanford.edu
173
+ - ndushay@stanford.edu
174
+ - jcoyne85@stanford.edu
175
+ - azanella@stanford.edu
176
+ - mjgiarlo@stanford.edu
177
+ - suntzu@stanford.edu
173
178
  executables: []
174
179
  extensions: []
175
180
  extra_rdoc_files: []
@@ -210,7 +215,7 @@ licenses:
210
215
  - Apache-2.0
211
216
  metadata:
212
217
  rubygems_mfa_required: 'true'
213
- post_install_message:
218
+ post_install_message:
214
219
  rdoc_options: []
215
220
  require_paths:
216
221
  - lib
@@ -218,16 +223,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
218
223
  requirements:
219
224
  - - ">="
220
225
  - !ruby/object:Gem::Version
221
- version: '2.7'
226
+ version: '3.0'
222
227
  required_rubygems_version: !ruby/object:Gem::Requirement
223
228
  requirements:
224
- - - ">="
229
+ - - ">"
225
230
  - !ruby/object:Gem::Version
226
- version: '0'
231
+ version: 1.3.1
227
232
  requirements: []
228
- rubygems_version: 3.3.7
229
- signing_key:
233
+ rubygems_version: 3.2.32
234
+ signing_key:
230
235
  specification_version: 4
231
- summary: Ruby implementation of digital object versioning toolkit used by the SULAIR
232
- Digital Library
236
+ summary: Ruby implementation of digital object versioning toolkit used by Stanford
237
+ University Libraries
233
238
  test_files: []