moab-versioning 4.2.1 → 4.2.2

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.
@@ -1,5 +1,3 @@
1
- require 'moab'
2
-
3
1
  module Moab
4
2
  # Compares two {FileInventory} instances based primarily on file signatures and secondarily on file pathnames.
5
3
  # Although the usual use will be to compare the content of 2 different temporal versions of the same object,
@@ -24,7 +22,7 @@ module Moab
24
22
 
25
23
  # (see Serializable#initialize)
26
24
  def initialize(opts = {})
27
- @group_differences = Array.new
25
+ @group_differences = []
28
26
  super(opts)
29
27
  end
30
28
 
@@ -34,7 +32,7 @@ module Moab
34
32
 
35
33
  # @attribute
36
34
  # @return [Integer] the number of differences found between the two inventories that were compared (dynamically calculated)
37
- attribute :difference_count, Integer, :tag => 'differenceCount', :on_save => Proc.new { |i| i.to_s }
35
+ attribute :difference_count, Integer, :tag => 'differenceCount', :on_save => proc { |i| i.to_s }
38
36
 
39
37
  def difference_count
40
38
  @group_differences.inject(0) { |sum, group| sum + group.difference_count }
@@ -66,7 +64,7 @@ module Moab
66
64
 
67
65
  # @return [Array<String>] The data fields to include in summary reports
68
66
  def summary_fields
69
- %w{digital_object_id difference_count basis other report_datetime group_differences}
67
+ %w[digital_object_id difference_count basis other report_datetime group_differences]
70
68
  end
71
69
 
72
70
  # @param [String] group_id The identifer of the group to be selected
@@ -110,11 +108,11 @@ module Moab
110
108
  # @return [Hash] Serializes the data and then filters it to report only the changes
111
109
  def differences_detail
112
110
  #return self.summary if difference_count == 0
113
- inv_diff = self.to_hash
111
+ inv_diff = to_hash
114
112
  inv_diff["group_differences"].each_value do |group_diff|
115
113
  delete_subsets = []
116
114
  group_diff["subsets"].each do |change_type, subset|
117
- delete_subsets << change_type if change_type == "identical" or subset["count"] == 0
115
+ delete_subsets << change_type if (change_type == "identical") || (subset["count"] == 0)
118
116
  end
119
117
  delete_subsets.each do |change_type|
120
118
  group_diff["subsets"].delete(change_type)
@@ -1,5 +1,3 @@
1
- require 'moab'
2
-
3
1
  module Moab
4
2
  # A container for a file signature and all the physical file instances that have that signature
5
3
  # This element has one child {FileSignature} element, and one or more {FileInstance} elements
@@ -24,7 +22,7 @@ module Moab
24
22
 
25
23
  # (see Serializable#initialize)
26
24
  def initialize(opts = {})
27
- @instances = Array.new
25
+ @instances = []
28
26
  super(opts)
29
27
  end
30
28
 
@@ -47,7 +45,7 @@ module Moab
47
45
  # @api internal
48
46
  # @return [Array<String>] Create an array from all the file paths of the child {FileInstance} objects
49
47
  def paths
50
- instances.collect { |i| i.path }
48
+ instances.collect(&:path)
51
49
  end
52
50
 
53
51
  # @api internal
@@ -76,8 +74,8 @@ module Moab
76
74
  # @param other [FileManifestation] The {FileManifestation} object to compare with self
77
75
  # @return [Boolean] True if {FileManifestation} objects have same content
78
76
  def ==(other)
79
- return false unless (other.respond_to?(:signature) && other.respond_to?(:instances)) # Cannot equal an incomparable type!
80
- (self.signature == other.signature) && (self.instances == other.instances)
77
+ return false unless other.respond_to?(:signature) && other.respond_to?(:instances) # Cannot equal an incomparable type!
78
+ (signature == other.signature) && (instances == other.instances)
81
79
  end
82
80
  end
83
81
  end
@@ -1,5 +1,3 @@
1
- require 'moab'
2
-
3
1
  module Moab
4
2
  # The fixity properties of a file, used to determine file content equivalence regardless of filename.
5
3
  # Placing this data in a class by itself facilitates using file size together with the MD5 and SHA1 checksums
@@ -46,19 +44,19 @@ module Moab
46
44
 
47
45
  # @attribute
48
46
  # @return [Integer] The size of the file in bytes
49
- attribute :size, Integer, :on_save => Proc.new { |n| n.to_s }
47
+ attribute :size, Integer, :on_save => proc { |n| n.to_s }
50
48
 
51
49
  # @attribute
52
50
  # @return [String] The MD5 checksum value of the file
53
- attribute :md5, String, :on_save => Proc.new { |n| n.nil? ? "" : n.to_s }
51
+ attribute :md5, String, :on_save => proc { |n| n.nil? ? "" : n.to_s }
54
52
 
55
53
  # @attribute
56
54
  # @return [String] The SHA1 checksum value of the file
57
- attribute :sha1, String, :on_save => Proc.new { |n| n.nil? ? "" : n.to_s }
55
+ attribute :sha1, String, :on_save => proc { |n| n.nil? ? "" : n.to_s }
58
56
 
59
57
  # @attribute
60
58
  # @return [String] The SHA256 checksum value of the file
61
- attribute :sha256, String, :on_save => Proc.new { |n| n.nil? ? "" : n.to_s }
59
+ attribute :sha256, String, :on_save => proc { |n| n.nil? ? "" : n.to_s }
62
60
 
63
61
  KNOWN_ALGOS = {
64
62
  md5: proc { Digest::MD5.new },
@@ -106,12 +104,11 @@ module Moab
106
104
 
107
105
  # @return [Hash<Symbol,String>] A hash of the checksum data
108
106
  def checksums
109
- checksum_hash = Hash.new
110
- checksum_hash[:md5] = @md5
111
- checksum_hash[:sha1] = @sha1
112
- checksum_hash[:sha256] = @sha256
113
- checksum_hash.delete_if { |_key, value| value.nil? or value.empty? }
114
- checksum_hash
107
+ {
108
+ md5: md5,
109
+ sha1: sha1,
110
+ sha256: sha256
111
+ }.reject { |_key, value| value.nil? || value.empty? }
115
112
  end
116
113
 
117
114
  # @return [Boolean] The signature contains all of the 3 desired checksums
@@ -120,24 +117,21 @@ module Moab
120
117
  end
121
118
 
122
119
  # @api internal
123
- # @return [Hash<Symbol,String>] A hash of fixity data from this signataure object
120
+ # @return [Hash<Symbol => String>] A hash of fixity data from this signataure object
124
121
  def fixity
125
- fixity_hash = Hash.new
126
- fixity_hash[:size] = @size.to_s
127
- fixity_hash.merge!(checksums)
128
- fixity_hash
122
+ { size: size.to_s }.merge(checksums)
129
123
  end
130
124
 
131
125
  # @api internal
132
126
  # @param other [FileSignature] The other file signature being compared to this signature
133
127
  # @return [Boolean] Returns true if self and other have comparable fixity data.
134
128
  def eql?(other)
135
- return false unless (other.respond_to?(:size) && other.respond_to?(:checksums))
136
- return false if self.size.to_i != other.size.to_i
137
- self_checksums = self.checksums
129
+ return false unless other.respond_to?(:size) && other.respond_to?(:checksums)
130
+ return false if size.to_i != other.size.to_i
131
+ self_checksums = checksums
138
132
  other_checksums = other.checksums
139
133
  matching_keys = self_checksums.keys & other_checksums.keys
140
- return false if matching_keys.size == 0
134
+ return false if matching_keys.empty?
141
135
  matching_keys.each do |key|
142
136
  return false if self_checksums[key] != other_checksums[key]
143
137
  end
@@ -181,31 +175,26 @@ module Moab
181
175
  # @return [FileSignature] The full signature derived from the file, unless the fixity is inconsistent with current values
182
176
  def normalized_signature(pathname)
183
177
  sig_from_file = FileSignature.new.signature_from_file(pathname)
184
- if self.eql?(sig_from_file)
185
- # The full signature from file is consistent with current values
186
- return sig_from_file
187
- else
188
- # One or more of the fixity values is inconsistent, so raise an exception
189
- raise "Signature inconsistent between inventory and file for #{pathname}: #{self.diff(sig_from_file).inspect}"
190
- end
178
+ return sig_from_file if eql?(sig_from_file)
179
+ # The full signature from file is consistent with current values, or...
180
+ # One or more of the fixity values is inconsistent, so raise an exception
181
+ raise "Signature inconsistent between inventory and file for #{pathname}: #{diff(sig_from_file).inspect}"
191
182
  end
192
183
 
193
184
  # @return [Hash<Symbol,String>] Key is type (e.g. :sha1), value is checksum names (e.g. ['SHA-1', 'SHA1'])
194
- def FileSignature.checksum_names_for_type
195
- names_for_type = Hash.new
196
- names_for_type[:md5] = ['MD5']
197
- names_for_type[:sha1] = ['SHA-1', 'SHA1']
198
- names_for_type[:sha256] = ['SHA-256', 'SHA256']
199
- names_for_type
185
+ def self.checksum_names_for_type
186
+ {
187
+ md5: ['MD5'],
188
+ sha1: ['SHA-1', 'SHA1'],
189
+ sha256: ['SHA-256', 'SHA256']
190
+ }
200
191
  end
201
192
 
202
193
  # @return [Hash<String, Symbol>] Key is checksum name (e.g. MD5), value is checksum type (e.g. :md5)
203
- def FileSignature.checksum_type_for_name
204
- type_for_name = Hash.new
205
- self.checksum_names_for_type.each do |type, names|
206
- names.each do |name|
207
- type_for_name[name] = type
208
- end
194
+ def self.checksum_type_for_name
195
+ type_for_name = {}
196
+ checksum_names_for_type.each do |type, names|
197
+ names.each { |name| type_for_name[name] = type }
209
198
  end
210
199
  type_for_name
211
200
  end
@@ -1,5 +1,3 @@
1
- require 'moab'
2
-
3
1
  module Moab
4
2
  # A digital object's Signature Catalog is derived from an filtered aggregation of the file inventories
5
3
  # of a digital object's set of versions. (see {#update})
@@ -32,8 +30,8 @@ module Moab
32
30
 
33
31
  # (see Serializable#initialize)
34
32
  def initialize(opts = {})
35
- @entries = Array.new
36
- @signature_hash = Hash.new
33
+ @entries = []
34
+ @signature_hash = {}
37
35
  super(opts)
38
36
  end
39
37
 
@@ -43,7 +41,7 @@ module Moab
43
41
 
44
42
  # @attribute
45
43
  # @return [Integer] The ordinal version number
46
- attribute :version_id, Integer, :tag => 'versionId', :key => true, :on_save => Proc.new { |n| n.to_s }
44
+ attribute :version_id, Integer, :tag => 'versionId', :key => true, :on_save => proc { |n| n.to_s }
47
45
 
48
46
  # @return [String] The unique identifier concatenating digital object id with version id
49
47
  def composite_key
@@ -64,7 +62,7 @@ module Moab
64
62
 
65
63
  # @attribute
66
64
  # @return [Integer] The total number of data files (dynamically calculated)
67
- attribute :file_count, Integer, :tag => 'fileCount', :on_save => Proc.new { |t| t.to_s }
65
+ attribute :file_count, Integer, :tag => 'fileCount', :on_save => proc { |t| t.to_s }
68
66
 
69
67
  def file_count
70
68
  entries.size
@@ -72,7 +70,7 @@ module Moab
72
70
 
73
71
  # @attribute
74
72
  # @return [Integer] The total size (in bytes) of all data files (dynamically calculated)
75
- attribute :byte_count, Integer, :tag => 'byteCount', :on_save => Proc.new { |t| t.to_s }
73
+ attribute :byte_count, Integer, :tag => 'byteCount', :on_save => proc { |t| t.to_s }
76
74
 
77
75
  def byte_count
78
76
  entries.inject(0) { |sum, entry| sum + entry.signature.size.to_i }
@@ -80,7 +78,7 @@ module Moab
80
78
 
81
79
  # @attribute
82
80
  # @return [Integer] The total disk usage (in 1 kB blocks) of all data files (estimating du -k result) (dynamically calculated)
83
- attribute :block_count, Integer, :tag => 'blockCount', :on_save => Proc.new { |t| t.to_s }
81
+ attribute :block_count, Integer, :tag => 'blockCount', :on_save => proc { |t| t.to_s }
84
82
 
85
83
  def block_count
86
84
  block_size = 1024
@@ -89,7 +87,7 @@ module Moab
89
87
 
90
88
  # @return [Array<String>] The data fields to include in summary reports
91
89
  def summary_fields
92
- %w{digital_object_id version_id catalog_datetime file_count byte_count block_count}
90
+ %w[digital_object_id version_id catalog_datetime file_count byte_count block_count]
93
91
  end
94
92
 
95
93
  # @attribute
@@ -134,7 +132,7 @@ module Moab
134
132
  end
135
133
  group.files.each do |file|
136
134
  unless file.signature.complete?
137
- if @signature_hash.has_key?(file.signature)
135
+ if @signature_hash.key?(file.signature)
138
136
  file.signature = @signature_hash.find { |k, _v| k == file.signature }[0]
139
137
  elsif group_pathname
140
138
  file_pathname = group_pathname.join(file.instances[0].path)
@@ -153,7 +151,7 @@ module Moab
153
151
  def update(version_inventory, data_pathname)
154
152
  version_inventory.groups.each do |group|
155
153
  group.files.each do |file|
156
- unless @signature_hash.has_key?(file.signature)
154
+ unless @signature_hash.key?(file.signature)
157
155
  entry = SignatureCatalogEntry.new
158
156
  entry.version_id = version_inventory.version_id
159
157
  entry.group_id = group.group_id
@@ -183,11 +181,11 @@ module Moab
183
181
  version_inventory.groups.each do |group|
184
182
  group_addtions = FileGroup.new(:group_id => group.group_id)
185
183
  group.files.each do |file|
186
- unless @signature_hash.has_key?(file.signature)
184
+ unless @signature_hash.key?(file.signature)
187
185
  group_addtions.add_file_instance(file.signature, file.instances[0])
188
186
  end
189
187
  end
190
- version_additions.groups << group_addtions if group_addtions.files.size > 0
188
+ version_additions.groups << group_addtions unless group_addtions.files.empty?
191
189
  end
192
190
  version_additions
193
191
  end
@@ -1,5 +1,3 @@
1
- require 'moab'
2
-
3
1
  module Moab
4
2
  # A file-level entry in a digital object's {SignatureCatalog}.
5
3
  # It has a child {FileSignature} element that identifies the file's contents (the bytestream)
@@ -25,7 +23,7 @@ module Moab
25
23
 
26
24
  # @attribute
27
25
  # @return [Integer] The ordinal version number
28
- attribute :version_id, Integer, :tag => 'originalVersion', :key => true, :on_save => Proc.new { |n| n.to_s }
26
+ attribute :version_id, Integer, :tag => 'originalVersion', :key => true, :on_save => proc { |n| n.to_s }
29
27
 
30
28
  # @attribute
31
29
  # @return [String] The name of the file group
@@ -1,5 +1,3 @@
1
- require 'moab'
2
-
3
1
  module Moab
4
2
  # A class to represent a digital object's repository storage location
5
3
  # and methods for
@@ -122,27 +120,25 @@ module Moab
122
120
  # @param version_id [Integer] The version identifier of an object version
123
121
  # @return [String] The directory name of the version, relative to the digital object home directory (e.g v0002)
124
122
  def self.version_dirname(version_id)
125
- ("v%04d" % version_id)
123
+ format("v%04d", version_id)
126
124
  end
127
125
 
128
126
  # @return [Array<Integer>] The list of all version ids for this object
129
127
  def version_id_list
130
- list = Array.new
128
+ list = []
131
129
  return list unless @object_pathname.exist?
132
130
  @object_pathname.children.each do |dirname|
133
131
  vnum = dirname.basename.to_s
134
- if vnum =~ /^v(\d+)$/
135
- list << vnum[1..-1].to_i
136
- end
132
+ list << vnum[1..-1].to_i if vnum =~ /^v(\d+)$/
137
133
  end
138
134
  list.sort
139
135
  end
140
136
 
141
137
  # @return [Array<StorageObjectVersion>] The list of all versions in this storage object
142
138
  def version_list
143
- version_id_list.collect { |id| self.storage_object_version(id) }
139
+ version_id_list.collect { |id| storage_object_version(id) }
144
140
  end
145
- alias :versions :version_list
141
+ alias versions version_list
146
142
 
147
143
  # @return [Boolean] true if there are no versions yet in this object
148
144
  def empty?
@@ -152,12 +148,12 @@ module Moab
152
148
  # @api external
153
149
  # @return [Integer] The identifier of the latest version of this object, or 0 if no versions exist
154
150
  def current_version_id
155
- @current_version_id ||= self.version_id_list.last || 0
151
+ @current_version_id ||= version_id_list.last || 0
156
152
  end
157
153
 
158
154
  # @return [StorageObjectVersion] The most recent version in the storage object
159
155
  def current_version
160
- self.storage_object_version(current_version_id)
156
+ storage_object_version(current_version_id)
161
157
  end
162
158
 
163
159
  # @api internal
@@ -191,21 +187,18 @@ module Moab
191
187
  # * Version 0 is a special case used to generate empty manifests
192
188
  # * Current version + 1 is used for creation of a new version
193
189
  def storage_object_version(version_id)
194
- if version_id
195
- StorageObjectVersion.new(self, version_id)
196
- else
197
- raise "Version ID not specified"
198
- end
190
+ raise "Version ID not specified" unless version_id
191
+ StorageObjectVersion.new(self, version_id)
199
192
  end
200
193
 
201
194
  # @return [VerificationResult] Return result of storage verification
202
195
  def verify_object_storage
203
196
  result = VerificationResult.new(digital_object_id)
204
- self.version_list.each do |version|
197
+ version_list.each do |version|
205
198
  result.subentities << version.verify_version_storage
206
199
  end
207
200
  result.subentities << current_version.verify_signature_catalog
208
- result.verified = result.subentities.all? { |entity| entity.verified }
201
+ result.verified = result.subentities.all?(&:verified)
209
202
  result
210
203
  end
211
204
 
@@ -216,7 +209,7 @@ module Moab
216
209
  recovery_object = StorageObject.new(@digital_object_id, recovery_path, false)
217
210
  recovery_object.versions.each do |recovery_version|
218
211
  version_id = recovery_version.version_id
219
- storage_version = self.storage_object_version(version_id)
212
+ storage_version = storage_object_version(version_id)
220
213
  # rename/save the original
221
214
  storage_version.deactivate(timestamp)
222
215
  # copy the recovered version into place
@@ -1,4 +1,3 @@
1
- require 'moab'
2
1
  require 'set'
3
2
 
4
3
  module Moab
@@ -59,11 +58,11 @@ module Moab
59
58
  NO_SIGNATURE_CATALOG => "Version %{addl}: Missing signatureCatalog.xml",
60
59
  NO_MANIFEST_INVENTORY => "Version %{addl}: Missing manifestInventory.xml",
61
60
  NO_FILES_IN_MANIFEST_DIR => "Version %{addl}: No files present in manifest dir",
62
- METADATA_SUB_DIRS_DETECTED => "Version %{addl}: metadata directory should only contain files, not directories",
61
+ METADATA_SUB_DIRS_DETECTED => "Version %{version}: metadata directory should only contain files, not directories. Found directory: %{dir}",
63
62
  VERSIONS_NOT_IN_ORDER => "Should contain only sequential version directories. Current directories: %{addl}",
64
63
  NO_FILES_IN_METADATA_DIR => "Version %{addl}: No files present in metadata dir",
65
64
  NO_FILES_IN_CONTENT_DIR => "Version %{addl}: No files present in content dir",
66
- CONTENT_SUB_DIRS_DETECTED => "Version %{addl}: content directory should only contain files, not directories",
65
+ CONTENT_SUB_DIRS_DETECTED => "Version %{version}: content directory should only contain files, not directories. Found directory: %{dir}",
67
66
  BAD_SUB_DIR_IN_CONTENT_DIR => "Version %{addl}: content directory has forbidden sub-directory name: vnnnn or #{FORBIDDEN_CONTENT_SUB_DIRS}"
68
67
  }.freeze
69
68
  end
@@ -139,7 +138,8 @@ module Moab
139
138
  errors = []
140
139
  content_dir_path = File.join(version_path, DATA_DIR, CONTENT_DIR)
141
140
  errors << result_hash(NO_FILES_IN_CONTENT_DIR, basename(version_path)) if directory_entries(content_dir_path).empty?
142
- errors << result_hash(CONTENT_SUB_DIRS_DETECTED, basename(version_path)) if contains_sub_dir?(content_dir_path) && !allow_content_subdirs
141
+ content_sub_dir = contains_sub_dir?(content_dir_path)
142
+ errors << result_hash(CONTENT_SUB_DIRS_DETECTED, version: basename(version_path), dir: content_sub_dir) if content_sub_dir && !allow_content_subdirs
143
143
  if allow_content_subdirs && contains_sub_dir?(content_dir_path) && contains_forbidden_content_sub_dir?(content_dir_path)
144
144
  errors << result_hash(BAD_SUB_DIR_IN_CONTENT_DIR, basename(version_path))
145
145
  end
@@ -158,7 +158,8 @@ module Moab
158
158
  errors = []
159
159
  metadata_dir_path = File.join(version_path, DATA_DIR, METADATA_DIR)
160
160
  errors << result_hash(NO_FILES_IN_METADATA_DIR, basename(version_path)) if directory_entries(metadata_dir_path).empty?
161
- errors << result_hash(METADATA_SUB_DIRS_DETECTED, basename(version_path)) if contains_sub_dir?(metadata_dir_path)
161
+ metadata_sub_dir = contains_sub_dir?(metadata_dir_path)
162
+ errors << result_hash(METADATA_SUB_DIRS_DETECTED, version: basename(version_path), dir: metadata_sub_dir) if metadata_sub_dir
162
163
  errors
163
164
  end
164
165
 
@@ -204,12 +205,25 @@ module Moab
204
205
  path.split(File::SEPARATOR)[-1]
205
206
  end
206
207
 
207
- def result_hash(response_code, addl = nil)
208
- { response_code => error_code_msg(response_code, addl) }
208
+ # @param [Integer] response_code one of the recognized values in error_code_to_messages
209
+ # @param [Hash<Symbol => String>, String] msg_args Value(s) folded into the error message
210
+ # @return [Hash<Integer => String>] single key/value Hash
211
+ # @example Usage
212
+ # sov.result_hash(10, '/some/dir')
213
+ # sov.result_hash(10, addl: '/some/dir') # equivalent
214
+ # sov.result_hash(8, version: '3', dir: '/other/dir')
215
+ def result_hash(response_code, msg_args = nil)
216
+ { response_code => error_code_msg(response_code, msg_args) }
209
217
  end
210
218
 
211
219
  def error_code_msg(response_code, addl = nil)
212
- format(self.class.error_code_to_messages[response_code], addl: addl)
220
+ arg_hash = {}
221
+ if addl.is_a?(Hash)
222
+ arg_hash.merge!(addl)
223
+ else
224
+ arg_hash[:addl] = addl
225
+ end
226
+ self.class.error_code_to_messages[response_code] % arg_hash
213
227
  end
214
228
 
215
229
  def check_required_manifest_files(dir, version)