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.
- checksums.yaml +4 -4
- data/lib/moab.rb +1 -1
- data/lib/moab/bagger.rb +15 -15
- data/lib/moab/config.rb +0 -2
- data/lib/moab/deposit_bag_validator.rb +1 -1
- data/lib/moab/file_group.rb +13 -15
- data/lib/moab/file_group_difference.rb +18 -21
- data/lib/moab/file_group_difference_subset.rb +2 -4
- data/lib/moab/file_instance.rb +1 -3
- data/lib/moab/file_instance_difference.rb +3 -5
- data/lib/moab/file_inventory.rb +17 -27
- data/lib/moab/file_inventory_difference.rb +5 -7
- data/lib/moab/file_manifestation.rb +4 -6
- data/lib/moab/file_signature.rb +29 -40
- data/lib/moab/signature_catalog.rb +11 -13
- data/lib/moab/signature_catalog_entry.rb +1 -3
- data/lib/moab/storage_object.rb +12 -19
- data/lib/moab/storage_object_validator.rb +22 -8
- data/lib/moab/storage_object_version.rb +25 -27
- data/lib/moab/storage_repository.rb +6 -13
- data/lib/moab/storage_services.rb +6 -8
- data/lib/moab/utc_time.rb +0 -2
- data/lib/moab/verification_result.rb +0 -2
- data/lib/moab/version_metadata.rb +1 -3
- data/lib/moab/version_metadata_entry.rb +2 -4
- data/lib/moab/version_metadata_event.rb +0 -2
- data/lib/serializer/manifest.rb +5 -5
- data/lib/serializer/serializable.rb +34 -34
- data/lib/stanford/active_fedora_object.rb +0 -2
- data/lib/stanford/content_inventory.rb +22 -20
- data/lib/stanford/dor_metadata.rb +0 -2
- data/lib/stanford/storage_object_validator.rb +0 -2
- data/lib/stanford/storage_repository.rb +1 -2
- data/lib/stanford/storage_services.rb +5 -7
- metadata +3 -3
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'moab'
|
2
|
-
|
3
1
|
module Moab
|
4
2
|
# A class to represent a version subdirectory within an object's home directory in preservation storage
|
5
3
|
# ====Data Model
|
@@ -32,7 +30,7 @@ module Moab
|
|
32
30
|
def initialize(storage_object, version_id)
|
33
31
|
if version_id.is_a?(Integer)
|
34
32
|
@version_id = version_id
|
35
|
-
elsif version_id.is_a?(String)
|
33
|
+
elsif version_id.is_a?(String) && version_id =~ /^v(\d+)$/
|
36
34
|
@version_id = version_id.sub(/^v/, '').to_i
|
37
35
|
else
|
38
36
|
raise "version_id (#{version_id}) is not in a recognized format"
|
@@ -40,7 +38,7 @@ module Moab
|
|
40
38
|
@version_name = StorageObject.version_dirname(@version_id)
|
41
39
|
@version_pathname = storage_object.object_pathname.join(@version_name)
|
42
40
|
@storage_object = storage_object
|
43
|
-
@inventory_cache =
|
41
|
+
@inventory_cache = {}
|
44
42
|
end
|
45
43
|
|
46
44
|
# @return [String] The unique identifier concatenating digital object id with version id
|
@@ -110,10 +108,10 @@ module Moab
|
|
110
108
|
# @see FileInventory#read_xml_file
|
111
109
|
def file_inventory(type)
|
112
110
|
if version_id > 0
|
113
|
-
return @inventory_cache[type] if @inventory_cache.
|
111
|
+
return @inventory_cache[type] if @inventory_cache.key?(type)
|
114
112
|
@inventory_cache[type] = FileInventory.read_xml_file(@version_pathname.join('manifests'), type)
|
115
113
|
else
|
116
|
-
groups = [
|
114
|
+
groups = %w[content metadata].collect { |id| FileGroup.new(:group_id => id) }
|
117
115
|
FileInventory.new(
|
118
116
|
:type => 'version',
|
119
117
|
:digital_object_id => @storage_object.digital_object_id,
|
@@ -208,12 +206,12 @@ module Moab
|
|
208
206
|
end
|
209
207
|
|
210
208
|
# @return [VerificationResult] return result of testing correctness of version manifests
|
211
|
-
def verify_version_storage
|
212
|
-
result = VerificationResult.new(
|
213
|
-
result.subentities <<
|
214
|
-
result.subentities <<
|
215
|
-
result.subentities <<
|
216
|
-
result.verified = result.subentities.all?
|
209
|
+
def verify_version_storage
|
210
|
+
result = VerificationResult.new(composite_key)
|
211
|
+
result.subentities << verify_manifest_inventory
|
212
|
+
result.subentities << verify_version_inventory
|
213
|
+
result.subentities << verify_version_additions
|
214
|
+
result.verified = result.subentities.all?(&:verified)
|
217
215
|
result
|
218
216
|
end
|
219
217
|
|
@@ -221,8 +219,8 @@ module Moab
|
|
221
219
|
def verify_manifest_inventory
|
222
220
|
# read/parse manifestInventory.xml
|
223
221
|
result = VerificationResult.new("manifest_inventory")
|
224
|
-
manifest_inventory =
|
225
|
-
result.subentities << VerificationResult.verify_value('composite_key',
|
222
|
+
manifest_inventory = file_inventory('manifests')
|
223
|
+
result.subentities << VerificationResult.verify_value('composite_key', composite_key, manifest_inventory.composite_key)
|
226
224
|
result.subentities << VerificationResult.verify_truth('manifests_group', !manifest_inventory.group_empty?('manifests'))
|
227
225
|
# measure the manifest signatures of the files in the directory (excluding manifestInventory.xml)
|
228
226
|
directory_inventory = FileInventory.new.inventory_from_directory(@version_pathname.join('manifests'), 'manifests')
|
@@ -236,7 +234,7 @@ module Moab
|
|
236
234
|
compare_result.verified = (diff.difference_count == 0)
|
237
235
|
compare_result.details = diff.differences_detail
|
238
236
|
result.subentities << compare_result
|
239
|
-
result.verified = result.subentities.all?
|
237
|
+
result.verified = result.subentities.all?(&:verified)
|
240
238
|
result
|
241
239
|
end
|
242
240
|
|
@@ -244,10 +242,10 @@ module Moab
|
|
244
242
|
def verify_signature_catalog
|
245
243
|
result = VerificationResult.new("signature_catalog")
|
246
244
|
signature_catalog = self.signature_catalog
|
247
|
-
result.subentities << VerificationResult.verify_value('signature_key',
|
245
|
+
result.subentities << VerificationResult.verify_value('signature_key', composite_key, signature_catalog.composite_key)
|
248
246
|
found = 0
|
249
|
-
missing =
|
250
|
-
object_pathname =
|
247
|
+
missing = []
|
248
|
+
object_pathname = storage_object.object_pathname
|
251
249
|
signature_catalog.entries.each do |catalog_entry|
|
252
250
|
storage_location = object_pathname.join(catalog_entry.storage_path)
|
253
251
|
if storage_location.exist?
|
@@ -264,19 +262,19 @@ module Moab
|
|
264
262
|
}
|
265
263
|
file_result.details['missing'] = missing unless missing.empty?
|
266
264
|
result.subentities << file_result
|
267
|
-
result.verified = result.subentities.all?
|
265
|
+
result.verified = result.subentities.all?(&:verified)
|
268
266
|
result
|
269
267
|
end
|
270
268
|
|
271
269
|
# @return [Boolean] true if files & signatures listed in version inventory can all be found
|
272
270
|
def verify_version_inventory
|
273
271
|
result = VerificationResult.new("version_inventory")
|
274
|
-
version_inventory =
|
275
|
-
result.subentities << VerificationResult.verify_value('inventory_key',
|
272
|
+
version_inventory = file_inventory('version')
|
273
|
+
result.subentities << VerificationResult.verify_value('inventory_key', composite_key, version_inventory.composite_key)
|
276
274
|
signature_catalog = self.signature_catalog
|
277
|
-
result.subentities << VerificationResult.verify_value('signature_key',
|
275
|
+
result.subentities << VerificationResult.verify_value('signature_key', composite_key, signature_catalog.composite_key)
|
278
276
|
found = 0
|
279
|
-
missing =
|
277
|
+
missing = []
|
280
278
|
version_inventory.groups.each do |group|
|
281
279
|
group.files.each do |file|
|
282
280
|
file.instances.each do |instance|
|
@@ -298,15 +296,15 @@ module Moab
|
|
298
296
|
}
|
299
297
|
file_result.details['missing'] = missing unless missing.empty?
|
300
298
|
result.subentities << file_result
|
301
|
-
result.verified = result.subentities.all?
|
299
|
+
result.verified = result.subentities.all?(&:verified)
|
302
300
|
result
|
303
301
|
end
|
304
302
|
|
305
303
|
# @return [Boolean] returns true if files in data folder match files listed in version addtions inventory
|
306
304
|
def verify_version_additions
|
307
305
|
result = VerificationResult.new("version_additions")
|
308
|
-
version_additions =
|
309
|
-
result.subentities << VerificationResult.verify_value('composite_key',
|
306
|
+
version_additions = file_inventory('additions')
|
307
|
+
result.subentities << VerificationResult.verify_value('composite_key', composite_key, version_additions.composite_key)
|
310
308
|
data_directory = @version_pathname.join('data')
|
311
309
|
directory_inventory = FileInventory.new(:type => 'directory').inventory_from_directory(data_directory)
|
312
310
|
diff = FileInventoryDifference.new
|
@@ -315,7 +313,7 @@ module Moab
|
|
315
313
|
compare_result.verified = (diff.difference_count == 0)
|
316
314
|
compare_result.details = diff.differences_detail
|
317
315
|
result.subentities << compare_result
|
318
|
-
result.verified = result.subentities.all?
|
316
|
+
result.verified = result.subentities.all?(&:verified)
|
319
317
|
result
|
320
318
|
end
|
321
319
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'moab'
|
2
|
-
|
3
1
|
module Moab
|
4
2
|
# A class to represent the SDR repository store
|
5
3
|
#
|
@@ -58,9 +56,9 @@ module Moab
|
|
58
56
|
end
|
59
57
|
|
60
58
|
# @param object_id [String] The identifier of the digital object
|
61
|
-
# @return [
|
59
|
+
# @return [String] The branch segment of the object deposit path
|
60
|
+
# @note Override this method in a subclass
|
62
61
|
def deposit_branch(object_id)
|
63
|
-
#todo This method should be customized, or overridden in a subclass
|
64
62
|
object_id
|
65
63
|
end
|
66
64
|
|
@@ -77,7 +75,7 @@ module Moab
|
|
77
75
|
return root if root_trunk_branch.exist?
|
78
76
|
end
|
79
77
|
# Search for the object's directory in the deposit areas
|
80
|
-
if include_deposit
|
78
|
+
if include_deposit && deposit_trunk
|
81
79
|
branch = deposit_branch(object_id)
|
82
80
|
storage_roots.each do |root|
|
83
81
|
root_trunk = root.join(deposit_trunk)
|
@@ -119,11 +117,8 @@ module Moab
|
|
119
117
|
def storage_object(object_id, create = false)
|
120
118
|
storage_object = find_storage_object(object_id)
|
121
119
|
unless storage_object.object_pathname.exist?
|
122
|
-
|
123
|
-
|
124
|
-
else
|
125
|
-
raise Moab::ObjectNotFoundException, "No storage object found for #{object_id}"
|
126
|
-
end
|
120
|
+
raise Moab::ObjectNotFoundException, "No storage object found for #{object_id}" unless create
|
121
|
+
storage_object.object_pathname.mkpath
|
127
122
|
end
|
128
123
|
storage_object
|
129
124
|
end
|
@@ -132,9 +127,7 @@ module Moab
|
|
132
127
|
# @param druid [String] The object identifier
|
133
128
|
# @return [void] transfer the object to the preservation repository
|
134
129
|
def store_new_object_version(druid, bag_pathname)
|
135
|
-
storage_object
|
136
|
-
new_version = storage_object.ingest_bag(bag_pathname)
|
137
|
-
new_version
|
130
|
+
storage_object(druid, create = true).ingest_bag(bag_pathname)
|
138
131
|
end
|
139
132
|
end
|
140
133
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'moab'
|
2
|
-
|
3
1
|
module Moab
|
4
2
|
# An interface class to support access to SDR storage via a RESTful server
|
5
3
|
#
|
@@ -79,7 +77,7 @@ module Moab
|
|
79
77
|
# @param [String] object_id The digital object identifier of the object
|
80
78
|
# @return [Pathname] Pathname object containing the full path for the specified file
|
81
79
|
def self.version_metadata(object_id)
|
82
|
-
|
80
|
+
retrieve_file('metadata', 'versionMetadata.xml', object_id)
|
83
81
|
end
|
84
82
|
|
85
83
|
# @param [String] object_id The digital object identifier of the object
|
@@ -87,11 +85,11 @@ module Moab
|
|
87
85
|
# @return [FileInventory] the file inventory for the specified object version
|
88
86
|
def self.retrieve_file_group(file_category, object_id, version_id = nil)
|
89
87
|
storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
|
90
|
-
if file_category =~ /manifest/
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
88
|
+
inventory_type = if file_category =~ /manifest/
|
89
|
+
file_category = 'manifests'
|
90
|
+
else
|
91
|
+
'version'
|
92
|
+
end
|
95
93
|
inventory = storage_object_version.file_inventory(inventory_type)
|
96
94
|
inventory.group(file_category)
|
97
95
|
end
|
data/lib/moab/utc_time.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'moab'
|
2
|
-
|
3
1
|
module Moab
|
4
2
|
# The descriptive information about a digital object's collection of versions
|
5
3
|
#
|
@@ -19,7 +17,7 @@ module Moab
|
|
19
17
|
|
20
18
|
# (see Serializable#initialize)
|
21
19
|
def initialize(opts = {})
|
22
|
-
@versions =
|
20
|
+
@versions = []
|
23
21
|
super(opts)
|
24
22
|
end
|
25
23
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'moab'
|
2
|
-
|
3
1
|
module Moab
|
4
2
|
# The descriptive attributes of a digital object version.
|
5
3
|
#
|
@@ -18,13 +16,13 @@ module Moab
|
|
18
16
|
|
19
17
|
# (see Serializable#initialize)
|
20
18
|
def initialize(opts = {})
|
21
|
-
@events =
|
19
|
+
@events = []
|
22
20
|
super(opts)
|
23
21
|
end
|
24
22
|
|
25
23
|
# @attribute
|
26
24
|
# @return [Integer] The object version number (A sequential integer)
|
27
|
-
attribute :version_id, Integer, :tag => 'versionId', :key => true, :on_save =>
|
25
|
+
attribute :version_id, Integer, :tag => 'versionId', :key => true, :on_save => proc { |n| n.to_s }
|
28
26
|
|
29
27
|
# @attribute
|
30
28
|
# @return [String] "an external version label that increments the most significant digit for major revisions,
|
data/lib/serializer/manifest.rb
CHANGED
@@ -19,7 +19,7 @@ module Serializer
|
|
19
19
|
if filename
|
20
20
|
filename
|
21
21
|
else
|
22
|
-
cname =
|
22
|
+
cname = name.split(/::/).last
|
23
23
|
cname[0, 1].downcase + cname[1..-1] + '.xml'
|
24
24
|
end
|
25
25
|
end
|
@@ -29,7 +29,7 @@ module Serializer
|
|
29
29
|
# @param filename [String] Optional filename if one wishes to override the default filename
|
30
30
|
# @return [Pathname] The location of the xml file
|
31
31
|
def self.xml_pathname(parent_dir, filename = nil)
|
32
|
-
Pathname.new(parent_dir).join(
|
32
|
+
Pathname.new(parent_dir).join(xml_filename(filename))
|
33
33
|
end
|
34
34
|
|
35
35
|
# @api external
|
@@ -37,7 +37,7 @@ module Serializer
|
|
37
37
|
# @param filename [String] Optional filename if one wishes to override the default filename
|
38
38
|
# @return [Boolean] Returns true if the xml file exists
|
39
39
|
def self.xml_pathname_exist?(parent_dir, filename = nil)
|
40
|
-
|
40
|
+
xml_pathname(parent_dir, filename).exist?
|
41
41
|
end
|
42
42
|
|
43
43
|
# @api external
|
@@ -46,7 +46,7 @@ module Serializer
|
|
46
46
|
# @return [Serializable] Read the xml file and return the parsed XML
|
47
47
|
# @example {include:file:spec/features/serializer/read_xml_spec.rb}
|
48
48
|
def self.read_xml_file(parent_dir, filename = nil)
|
49
|
-
|
49
|
+
parse(xml_pathname(parent_dir, filename).read)
|
50
50
|
end
|
51
51
|
|
52
52
|
# @api external
|
@@ -56,7 +56,7 @@ module Serializer
|
|
56
56
|
# @return [void] Serializize the in-memory object to a xml file instance
|
57
57
|
def self.write_xml_file(xml_object, parent_dir, filename = nil)
|
58
58
|
parent_dir.mkpath
|
59
|
-
|
59
|
+
xml_pathname(parent_dir, filename).open('w') do |f|
|
60
60
|
xmlBuilder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8')
|
61
61
|
xmlBuilder = xml_object.to_xml(xmlBuilder)
|
62
62
|
f << xmlBuilder.to_xml
|
@@ -19,11 +19,8 @@ module Serializer
|
|
19
19
|
# The symbols should correspond to attributes declared using HappyMapper syntax
|
20
20
|
def initialize(opts = {})
|
21
21
|
opts.each do |key, value|
|
22
|
-
|
23
|
-
|
24
|
-
else
|
25
|
-
raise "#{key} is not a variable name in #{self.class.name}"
|
26
|
-
end
|
22
|
+
raise "#{key} is not a variable name in #{self.class.name}" unless variable_names.include?(key.to_s) || key == :test
|
23
|
+
instance_variable_set("@#{key}", value)
|
27
24
|
end
|
28
25
|
end
|
29
26
|
|
@@ -45,7 +42,7 @@ module Serializer
|
|
45
42
|
# @api internal
|
46
43
|
# @return [Array] Extract the names of the variables
|
47
44
|
def variable_names
|
48
|
-
variables.collect
|
45
|
+
variables.collect(&:name)
|
49
46
|
end
|
50
47
|
|
51
48
|
# @api internal
|
@@ -54,7 +51,7 @@ module Serializer
|
|
54
51
|
# This follows the same convention as used by DataMapper
|
55
52
|
# @see http://datamapper.org/docs/properties.html
|
56
53
|
def key_name
|
57
|
-
|
54
|
+
unless defined?(@key_name)
|
58
55
|
@key_name = nil
|
59
56
|
self.class.attributes.each do |attribute|
|
60
57
|
if attribute.options[:key]
|
@@ -69,7 +66,7 @@ module Serializer
|
|
69
66
|
# @api internal
|
70
67
|
# @return [String] For the current object instance, return the string to use as a hash key
|
71
68
|
def key
|
72
|
-
return
|
69
|
+
return send(key_name) if key_name
|
73
70
|
nil
|
74
71
|
end
|
75
72
|
|
@@ -79,10 +76,10 @@ module Serializer
|
|
79
76
|
# If the array member has a field tagged as a key, that field will be used as the hash.key.
|
80
77
|
# Otherwise the index position of the array member will be used as the key
|
81
78
|
def array_to_hash(array, summary = false)
|
82
|
-
item_hash =
|
79
|
+
item_hash = {}
|
83
80
|
array.each_index do |index|
|
84
81
|
item = array[index]
|
85
|
-
ikey =
|
82
|
+
ikey = item.respond_to?(:key) && item.key ? item.key : index
|
86
83
|
item_hash[ikey] = item.respond_to?(:to_hash) ? item.to_hash(summary) : item
|
87
84
|
end
|
88
85
|
item_hash
|
@@ -92,26 +89,26 @@ module Serializer
|
|
92
89
|
# @return [Hash] Recursively generate an Hash containing the object's properties
|
93
90
|
# @param summary [Boolean] Controls the depth and detail of recursion
|
94
91
|
def to_hash(summary = false)
|
95
|
-
oh =
|
92
|
+
oh = {}
|
96
93
|
vars = summary ? variables.select { |v| summary_fields.include?(v.name) } : variables
|
97
94
|
vars.each do |variable|
|
98
95
|
key = variable.name.to_s
|
99
|
-
value =
|
100
|
-
case value
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
96
|
+
value = send(variable.name)
|
97
|
+
oh[key] = case value
|
98
|
+
when Array
|
99
|
+
array_to_hash(value, summary)
|
100
|
+
when Serializable
|
101
|
+
value.to_hash
|
102
|
+
else
|
103
|
+
value
|
104
|
+
end
|
108
105
|
end
|
109
106
|
oh
|
110
107
|
end
|
111
108
|
|
112
109
|
# @return [Hash] Calls to_hash(summary=true)
|
113
110
|
def summary
|
114
|
-
|
111
|
+
to_hash(summary = true)
|
115
112
|
end
|
116
113
|
|
117
114
|
# @api internal
|
@@ -120,13 +117,13 @@ module Serializer
|
|
120
117
|
def diff(other)
|
121
118
|
raise "Cannot compare different classes" if self.class != other.class
|
122
119
|
left = other.to_hash
|
123
|
-
right =
|
124
|
-
if
|
120
|
+
right = to_hash
|
121
|
+
if key.nil? || other.key.nil?
|
125
122
|
ltag = :old
|
126
123
|
rtag = :new
|
127
124
|
else
|
128
125
|
ltag = other.key
|
129
|
-
rtag =
|
126
|
+
rtag = key
|
130
127
|
end
|
131
128
|
Serializable.deep_diff(ltag, left, rtag, right)
|
132
129
|
end
|
@@ -136,23 +133,26 @@ module Serializer
|
|
136
133
|
# @return [Hash] Generate a hash containing the differences between two hashes
|
137
134
|
# (recursively descend parallel trees of hashes)
|
138
135
|
# @see https://gist.github.com/146844
|
139
|
-
def
|
140
|
-
diff =
|
136
|
+
def self.deep_diff(*hashes)
|
137
|
+
diff = {}
|
141
138
|
case hashes.length
|
142
139
|
when 4
|
143
140
|
ltag, left, rtag, right = hashes
|
144
141
|
when 2
|
145
|
-
ltag
|
142
|
+
ltag = :left
|
143
|
+
left = hashes[0]
|
144
|
+
rtag = :right
|
145
|
+
right = hashes[1]
|
146
146
|
else
|
147
147
|
raise ArgumentError, "wrong number of arguments (#{hashes.length} for 2 or 4)"
|
148
148
|
end
|
149
149
|
(left.keys | right.keys).each do |k|
|
150
150
|
if left[k] != right[k]
|
151
|
-
if left[k].is_a?(Hash) && right[k].is_a?(Hash)
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
151
|
+
diff[k] = if left[k].is_a?(Hash) && right[k].is_a?(Hash)
|
152
|
+
deep_diff(ltag, left[k], rtag, right[k])
|
153
|
+
else
|
154
|
+
Hash.[](ltag, left[k], rtag, right[k])
|
155
|
+
end
|
156
156
|
end
|
157
157
|
end
|
158
158
|
diff
|
@@ -161,14 +161,14 @@ module Serializer
|
|
161
161
|
# @api internal
|
162
162
|
# @return [String] Generate JSON output from a hash of the object's variables
|
163
163
|
def to_json(summary = false)
|
164
|
-
hash =
|
164
|
+
hash = to_hash(summary)
|
165
165
|
JSON.pretty_generate(hash)
|
166
166
|
end
|
167
167
|
|
168
168
|
# @api internal
|
169
169
|
# @return [String] Generate YAML output from a hash of the object's variables
|
170
170
|
def to_yaml(summary = false)
|
171
|
-
|
171
|
+
to_hash(summary).to_yaml
|
172
172
|
end
|
173
173
|
end
|
174
174
|
end
|