moab-versioning 4.2.1 → 4.2.2

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