moab-versioning 4.3.0 → 4.4.0
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 +2 -1
- data/lib/moab/bagger.rb +6 -1
- data/lib/moab/config.rb +40 -7
- data/lib/moab/deposit_bag_validator.rb +19 -14
- data/lib/moab/exceptions.rb +2 -0
- data/lib/moab/file_group.rb +6 -3
- data/lib/moab/file_group_difference.rb +3 -0
- data/lib/moab/file_group_difference_subset.rb +2 -0
- data/lib/moab/file_instance.rb +3 -0
- data/lib/moab/file_instance_difference.rb +2 -0
- data/lib/moab/file_inventory.rb +4 -0
- data/lib/moab/file_inventory_difference.rb +2 -0
- data/lib/moab/file_manifestation.rb +3 -0
- data/lib/moab/file_signature.rb +7 -2
- data/lib/moab/signature_catalog.rb +3 -3
- data/lib/moab/signature_catalog_entry.rb +2 -0
- data/lib/moab/stanford.rb +2 -0
- data/lib/moab/storage_object.rb +6 -0
- data/lib/moab/storage_object_validator.rb +22 -8
- data/lib/moab/storage_object_version.rb +7 -0
- data/lib/moab/storage_repository.rb +48 -6
- data/lib/moab/storage_services.rb +9 -0
- data/lib/moab/utc_time.rb +2 -0
- data/lib/moab/verification_result.rb +4 -3
- data/lib/moab/version_metadata.rb +2 -0
- data/lib/moab/version_metadata_entry.rb +2 -0
- data/lib/moab/version_metadata_event.rb +2 -0
- data/lib/serializer.rb +2 -0
- data/lib/serializer/manifest.rb +2 -0
- data/lib/serializer/serializable.rb +5 -0
- data/lib/stanford/active_fedora_object.rb +2 -0
- data/lib/stanford/content_inventory.rb +4 -0
- data/lib/stanford/dor_metadata.rb +2 -0
- data/lib/stanford/moab_storage_directory.rb +4 -2
- data/lib/stanford/storage_object_validator.rb +2 -0
- data/lib/stanford/storage_repository.rb +4 -0
- data/lib/stanford/storage_services.rb +2 -0
- metadata +18 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcb99d73bd625e8cbba0201f1dea7f6ae4925806df623e2a1e52198827f5b1c7
|
4
|
+
data.tar.gz: cb53aea7e39eabf83f8f80e0d05b70fe4a73e4376e0a6e054fb912b1225460f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af674dbff0e829b11da52850800fd2b3365e4ebc27d6629cefe7a71de0961bab884e7fb406be515a93a6387e48c5eb90a170144177fef9655331b9517732c298
|
7
|
+
data.tar.gz: d858888aad9e8557b33471f02c600e94217eba5ac213f27469fd7cc745f79752c9699e53c58d007f9c6fd39202d17f5e14da1a5761994309b2e5047ca76e0731
|
data/lib/moab.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Moab is a module that provides a distintive namespace for the collection of classes it contains.
|
2
4
|
#
|
3
5
|
# ====Data Model
|
@@ -33,7 +35,6 @@ module Moab
|
|
33
35
|
end
|
34
36
|
|
35
37
|
require 'serializer'
|
36
|
-
require 'confstruct/configuration'
|
37
38
|
require 'moab/config'
|
38
39
|
require 'moab/utc_time'
|
39
40
|
require 'moab/file_signature'
|
data/lib/moab/bagger.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A class used to create a BagIt package from a version inventory and a set of source files.
|
3
5
|
# The {#fill_bag} method is called with a package_mode parameter that specifies
|
@@ -141,6 +143,7 @@ module Moab
|
|
141
143
|
def deposit_group(group_id, source_dir)
|
142
144
|
group = bag_inventory.group(group_id)
|
143
145
|
return nil? if group.nil? || group.files.empty?
|
146
|
+
|
144
147
|
target_dir = bag_pathname.join('data', group_id)
|
145
148
|
group.path_list.each do |relative_path|
|
146
149
|
source = source_dir.join(relative_path)
|
@@ -158,6 +161,7 @@ module Moab
|
|
158
161
|
def reconstuct_group(group_id, storage_object_dir)
|
159
162
|
group = bag_inventory.group(group_id)
|
160
163
|
return nil? if group.nil? || group.files.empty?
|
164
|
+
|
161
165
|
target_dir = bag_pathname.join('data', group_id)
|
162
166
|
group.files.each do |file|
|
163
167
|
catalog_entry = signature_catalog.signature_hash[file.signature]
|
@@ -260,6 +264,7 @@ module Moab
|
|
260
264
|
shell_execute(tar_cmd.sub('--force-local', ''))
|
261
265
|
end
|
262
266
|
raise(MoabRuntimeError, "Unable to create tarfile #{tar_pathname}") unless tar_pathname.exist?
|
267
|
+
|
263
268
|
true
|
264
269
|
end
|
265
270
|
|
@@ -273,7 +278,7 @@ module Moab
|
|
273
278
|
stdout
|
274
279
|
else
|
275
280
|
msg = "Shell command failed: [#{command}] caused by <STDERR = #{stderr}>"
|
276
|
-
msg << " STDOUT = #{stdout}" if stdout
|
281
|
+
msg << " STDOUT = #{stdout}" if stdout&.length&.positive?
|
277
282
|
raise(MoabStandardError, msg)
|
278
283
|
end
|
279
284
|
rescue SystemCallError => e
|
data/lib/moab/config.rb
CHANGED
@@ -1,10 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
# A place to store configuration for the gem
|
5
|
+
class Configuration
|
6
|
+
def initialize
|
7
|
+
@path_method = :druid_tree
|
8
|
+
@checksum_algos = [:md5, :sha1, :sha256]
|
9
|
+
end
|
10
|
+
|
11
|
+
def configure(&block)
|
12
|
+
instance_eval(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def storage_roots(new_value = nil)
|
16
|
+
@storage_roots = new_value if new_value
|
17
|
+
@storage_roots
|
18
|
+
end
|
19
|
+
|
20
|
+
def storage_trunk(new_value = nil)
|
21
|
+
@storage_trunk = new_value if new_value
|
22
|
+
@storage_trunk
|
23
|
+
end
|
24
|
+
|
25
|
+
def deposit_trunk(new_value = nil)
|
26
|
+
@deposit_trunk = new_value if new_value
|
27
|
+
@deposit_trunk
|
28
|
+
end
|
29
|
+
|
30
|
+
def path_method(new_value = nil)
|
31
|
+
@path_method = new_value if new_value
|
32
|
+
@path_method
|
33
|
+
end
|
34
|
+
|
35
|
+
def checksum_algos(new_value = nil)
|
36
|
+
@checksum_algos = new_value if new_value
|
37
|
+
@checksum_algos
|
38
|
+
end
|
9
39
|
end
|
40
|
+
|
41
|
+
# @return [Moab::Configuration] the configuration data
|
42
|
+
Config = Configuration.new
|
10
43
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# Given a deposit bag, ensures the contents valid for becoming a StorageObjectVersion
|
3
5
|
# this is a Shameless Green implementation, combining code from:
|
@@ -30,23 +32,23 @@ module Moab
|
|
30
32
|
VERSION_MISSING_FROM_FILE => "Version xml file %{version_file} missing data at %{xpath} containing version id"
|
31
33
|
}.freeze
|
32
34
|
|
33
|
-
REQUIRED_MANIFEST_CHECKSUM_TYPE = 'sha256'
|
35
|
+
REQUIRED_MANIFEST_CHECKSUM_TYPE = 'sha256'
|
34
36
|
RECOGNIZED_CHECKSUM_ALGORITHMS = %i[md5 sha1 sha256 sha384 sha512].freeze
|
35
37
|
|
36
|
-
TAGMANIFEST = 'tagmanifest'
|
37
|
-
MANIFEST = 'manifest'
|
38
|
-
DATA_DIR_BASENAME = 'data'
|
39
|
-
BAG_INFO_TXT_BASENAME = 'bag-info.txt'
|
40
|
-
VERSION_ADDITIONS_BASENAME = 'versionAdditions.xml'
|
41
|
-
VERSION_INVENTORY_BASENAME = 'versionInventory.xml'
|
42
|
-
VERSION_METADATA_PATH = "#{DATA_DIR_BASENAME}/metadata/versionMetadata.xml"
|
38
|
+
TAGMANIFEST = 'tagmanifest'
|
39
|
+
MANIFEST = 'manifest'
|
40
|
+
DATA_DIR_BASENAME = 'data'
|
41
|
+
BAG_INFO_TXT_BASENAME = 'bag-info.txt'
|
42
|
+
VERSION_ADDITIONS_BASENAME = 'versionAdditions.xml'
|
43
|
+
VERSION_INVENTORY_BASENAME = 'versionInventory.xml'
|
44
|
+
VERSION_METADATA_PATH = "#{DATA_DIR_BASENAME}/metadata/versionMetadata.xml"
|
43
45
|
|
44
46
|
REQUIRED_BAG_FILES = [
|
45
47
|
DATA_DIR_BASENAME,
|
46
|
-
'bagit.txt'
|
48
|
+
'bagit.txt',
|
47
49
|
BAG_INFO_TXT_BASENAME,
|
48
|
-
"#{MANIFEST}-#{REQUIRED_MANIFEST_CHECKSUM_TYPE}.txt"
|
49
|
-
"#{TAGMANIFEST}-#{REQUIRED_MANIFEST_CHECKSUM_TYPE}.txt"
|
50
|
+
"#{MANIFEST}-#{REQUIRED_MANIFEST_CHECKSUM_TYPE}.txt",
|
51
|
+
"#{TAGMANIFEST}-#{REQUIRED_MANIFEST_CHECKSUM_TYPE}.txt",
|
50
52
|
VERSION_ADDITIONS_BASENAME,
|
51
53
|
VERSION_INVENTORY_BASENAME,
|
52
54
|
VERSION_METADATA_PATH
|
@@ -64,6 +66,7 @@ module Moab
|
|
64
66
|
def validation_errors
|
65
67
|
return [single_error_hash(BAG_DIR_NOT_FOUND, bag_dir: deposit_bag_pathname)] unless deposit_bag_pathname.exist?
|
66
68
|
return result_array unless required_bag_files_exist?
|
69
|
+
|
67
70
|
verify_version
|
68
71
|
verify_tagmanifests
|
69
72
|
verify_payload_size
|
@@ -112,6 +115,7 @@ module Moab
|
|
112
115
|
doc = Nokogiri::XML(File.open(pathname.to_s), &:strict)
|
113
116
|
version_id = doc.xpath(xpath).last.text unless doc.xpath(xpath).empty?
|
114
117
|
return version_id.to_i if version_id
|
118
|
+
|
115
119
|
err_data = {
|
116
120
|
version_file: pathname,
|
117
121
|
xpath: xpath
|
@@ -129,6 +133,7 @@ module Moab
|
|
129
133
|
|
130
134
|
def verify_version_from_xml_file(file_pathname, found)
|
131
135
|
return if found == expected_new_version
|
136
|
+
|
132
137
|
err_data = {
|
133
138
|
file_pathname: file_pathname,
|
134
139
|
new_version: expected_new_version,
|
@@ -251,6 +256,7 @@ module Moab
|
|
251
256
|
end
|
252
257
|
end
|
253
258
|
return if diff_hash.empty?
|
259
|
+
|
254
260
|
err_data = {
|
255
261
|
manifest_type: manifest_type,
|
256
262
|
diffs: diff_hash
|
@@ -266,9 +272,7 @@ module Moab
|
|
266
272
|
checksum_types_to_compare.each do |type|
|
267
273
|
left_checksum = left_checksums[type]
|
268
274
|
right_checksum = right_checksums[type]
|
269
|
-
if left_checksum != right_checksum
|
270
|
-
diff_hash[type] = { left_label => left_checksum, right_label => right_checksum }
|
271
|
-
end
|
275
|
+
diff_hash[type] = { left_label => left_checksum, right_label => right_checksum } if left_checksum != right_checksum
|
272
276
|
end
|
273
277
|
diff_hash.empty? ? nil : diff_hash
|
274
278
|
end
|
@@ -277,6 +281,7 @@ module Moab
|
|
277
281
|
sizes_from_bag_info_file = bag_info_payload_size
|
278
282
|
generated_sizes = generated_payload_size
|
279
283
|
return if sizes_from_bag_info_file == generated_sizes
|
284
|
+
|
280
285
|
err_data = {
|
281
286
|
bag_info_sizes: sizes_from_bag_info_file,
|
282
287
|
generated_sizes: generated_sizes
|
data/lib/moab/exceptions.rb
CHANGED
data/lib/moab/file_group.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A container for a standard subset of a digital objects {FileManifestation} objects
|
3
5
|
# Used to segregate depositor content from repository metadata files
|
@@ -161,9 +163,11 @@ module Moab
|
|
161
163
|
# @return [Boolean] Test whether the given path is contained within the {#base_directory}
|
162
164
|
def is_descendent_of_base?(pathname)
|
163
165
|
raise(MoabRuntimeError, "base_directory has not been set") if @base_directory.nil?
|
166
|
+
|
164
167
|
is_descendent = false
|
165
168
|
pathname.expand_path.ascend { |ancestor| is_descendent ||= (ancestor == @base_directory) }
|
166
169
|
raise(MoabRuntimeError, "#{pathname} is not a descendent of #{@base_directory}") unless is_descendent
|
170
|
+
|
167
171
|
is_descendent
|
168
172
|
end
|
169
173
|
|
@@ -203,6 +207,7 @@ module Moab
|
|
203
207
|
validated ||= is_descendent_of_base?(pathname)
|
204
208
|
pathname.children.sort.each do |child|
|
205
209
|
next if child.basename.to_s == '.DS_Store'
|
210
|
+
|
206
211
|
if child.directory?
|
207
212
|
harvest_directory(child, recursive, validated) if recursive
|
208
213
|
else
|
@@ -222,9 +227,7 @@ module Moab
|
|
222
227
|
instance = FileInstance.new.instance_from_file(pathname, @base_directory)
|
223
228
|
if @signatures_from_bag && @signatures_from_bag[pathname]
|
224
229
|
signature = @signatures_from_bag[pathname]
|
225
|
-
unless signature.complete?
|
226
|
-
signature = signature.normalized_signature(pathname)
|
227
|
-
end
|
230
|
+
signature = signature.normalized_signature(pathname) unless signature.complete?
|
228
231
|
else
|
229
232
|
signature = FileSignature.new.signature_from_file(pathname)
|
230
233
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# Performs analysis and reports the differences between two matching {FileGroup} objects.
|
3
5
|
# The descending elements of the report hold a detailed breakdown of file-level differences, organized by change type.
|
@@ -132,6 +134,7 @@ module Moab
|
|
132
134
|
|
133
135
|
def subsets=(array)
|
134
136
|
return unless array
|
137
|
+
|
135
138
|
array.each { |subset| subset_hash[subset.change.to_sym] = subset }
|
136
139
|
end
|
137
140
|
|
data/lib/moab/file_instance.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# The file path and last modification date properties of a file
|
3
5
|
#
|
@@ -52,6 +54,7 @@ module Moab
|
|
52
54
|
# @return [Boolean] Returns true if self and other have the same path.
|
53
55
|
def eql?(other)
|
54
56
|
return false unless other.respond_to?(:path) # Cannot equal an incomparable type!
|
57
|
+
|
55
58
|
path == other.path
|
56
59
|
end
|
57
60
|
|
data/lib/moab/file_inventory.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A structured container for recording information about a collection of related files.
|
3
5
|
#
|
@@ -132,9 +134,11 @@ module Moab
|
|
132
134
|
file_group = group(group_id)
|
133
135
|
errmsg = "group #{group_id} not found for #{digital_object_id} - #{version_id}"
|
134
136
|
raise FileNotFoundException, errmsg if file_group.nil?
|
137
|
+
|
135
138
|
file_signature = file_group.path_hash[file_id]
|
136
139
|
errmsg = "#{group_id} file #{file_id} not found for #{digital_object_id} - #{version_id}"
|
137
140
|
raise FileNotFoundException, errmsg if file_signature.nil?
|
141
|
+
|
138
142
|
file_signature
|
139
143
|
end
|
140
144
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A container for a file signature and all the physical file instances that have that signature
|
3
5
|
# This element has one child {FileSignature} element, and one or more {FileInstance} elements
|
@@ -75,6 +77,7 @@ module Moab
|
|
75
77
|
# @return [Boolean] True if {FileManifestation} objects have same content
|
76
78
|
def ==(other)
|
77
79
|
return false unless other.respond_to?(:signature) && other.respond_to?(:instances) # Cannot equal an incomparable type!
|
80
|
+
|
78
81
|
(signature == other.signature) && (instances == other.instances)
|
79
82
|
end
|
80
83
|
end
|
data/lib/moab/file_signature.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# The fixity properties of a file, used to determine file content equivalence regardless of filename.
|
3
5
|
# Placing this data in a class by itself facilitates using file size together with the MD5 and SHA1 checksums
|
@@ -128,10 +130,12 @@ module Moab
|
|
128
130
|
def eql?(other)
|
129
131
|
return false unless other.respond_to?(:size) && other.respond_to?(:checksums)
|
130
132
|
return false if size.to_i != other.size.to_i
|
133
|
+
|
131
134
|
self_checksums = checksums
|
132
135
|
other_checksums = other.checksums
|
133
136
|
matching_keys = self_checksums.keys & other_checksums.keys
|
134
137
|
return false if matching_keys.empty?
|
138
|
+
|
135
139
|
matching_keys.each do |key|
|
136
140
|
return false if self_checksums[key] != other_checksums[key]
|
137
141
|
end
|
@@ -176,6 +180,7 @@ module Moab
|
|
176
180
|
def normalized_signature(pathname)
|
177
181
|
sig_from_file = FileSignature.new.signature_from_file(pathname)
|
178
182
|
return sig_from_file if eql?(sig_from_file)
|
183
|
+
|
179
184
|
# The full signature from file is consistent with current values, or...
|
180
185
|
# One or more of the fixity values is inconsistent, so raise an exception
|
181
186
|
raise(MoabRuntimeError, "Signature inconsistent between inventory and file for #{pathname}: #{diff(sig_from_file).inspect}")
|
@@ -185,8 +190,8 @@ module Moab
|
|
185
190
|
def self.checksum_names_for_type
|
186
191
|
{
|
187
192
|
md5: ['MD5'],
|
188
|
-
sha1: [
|
189
|
-
sha256: [
|
193
|
+
sha1: %w[SHA-1 SHA1],
|
194
|
+
sha256: %w[SHA-256 SHA256]
|
190
195
|
}
|
191
196
|
end
|
192
197
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A digital object's Signature Catalog is derived from an filtered aggregation of the file inventories
|
3
5
|
# of a digital object's set of versions. (see {#update})
|
@@ -181,9 +183,7 @@ module Moab
|
|
181
183
|
version_inventory.groups.each do |group|
|
182
184
|
group_addtions = FileGroup.new(:group_id => group.group_id)
|
183
185
|
group.files.each do |file|
|
184
|
-
unless @signature_hash.key?(file.signature)
|
185
|
-
group_addtions.add_file_instance(file.signature, file.instances[0])
|
186
|
-
end
|
186
|
+
group_addtions.add_file_instance(file.signature, file.instances[0]) unless @signature_hash.key?(file.signature)
|
187
187
|
end
|
188
188
|
version_additions.groups << group_addtions unless group_addtions.files.empty?
|
189
189
|
end
|
data/lib/moab/stanford.rb
CHANGED
data/lib/moab/storage_object.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A class to represent a digital object's repository storage location
|
3
5
|
# and methods for
|
@@ -113,6 +115,7 @@ module Moab
|
|
113
115
|
storage_filepath = @object_pathname.join(catalog_filepath)
|
114
116
|
errmsg = "#{catalog_filepath} missing from storage location #{storage_filepath}"
|
115
117
|
raise FileNotFoundException, errmsg unless storage_filepath.exist?
|
118
|
+
|
116
119
|
storage_filepath
|
117
120
|
end
|
118
121
|
|
@@ -127,6 +130,7 @@ module Moab
|
|
127
130
|
def version_id_list
|
128
131
|
list = []
|
129
132
|
return list unless @object_pathname.exist?
|
133
|
+
|
130
134
|
@object_pathname.children.each do |dirname|
|
131
135
|
vnum = dirname.basename.to_s
|
132
136
|
list << vnum[1..-1].to_i if vnum =~ /^v(\d+)$/
|
@@ -163,6 +167,7 @@ module Moab
|
|
163
167
|
if version_inventory.version_id != (current_version_id + 1)
|
164
168
|
raise(MoabRuntimeError, "version mismatch - current: #{current_version_id} new: #{version_inventory.version_id}")
|
165
169
|
end
|
170
|
+
|
166
171
|
true
|
167
172
|
end
|
168
173
|
|
@@ -188,6 +193,7 @@ module Moab
|
|
188
193
|
# * Current version + 1 is used for creation of a new version
|
189
194
|
def storage_object_version(version_id)
|
190
195
|
raise(MoabRuntimeError, "Version ID not specified") unless version_id
|
196
|
+
|
191
197
|
StorageObjectVersion.new(self, version_id)
|
192
198
|
end
|
193
199
|
|
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module Moab
|
4
6
|
# Given a druid path, are the contents actually a well-formed Moab?
|
5
7
|
# Shameless green: repetitious code included.
|
6
8
|
class StorageObjectValidator
|
7
|
-
METADATA_DIR = "metadata"
|
8
|
-
CONTENT_DIR = "content"
|
9
|
+
METADATA_DIR = "metadata"
|
10
|
+
CONTENT_DIR = "content"
|
9
11
|
EXPECTED_DATA_SUB_DIRS = [CONTENT_DIR, METADATA_DIR].freeze
|
10
12
|
IMPLICIT_DIRS = ['.', '..'].freeze # unlike Find.find, Dir.entries returns the current/parent dirs
|
11
|
-
DATA_DIR = "data"
|
12
|
-
MANIFESTS_DIR = 'manifests'
|
13
|
+
DATA_DIR = "data"
|
14
|
+
MANIFESTS_DIR = 'manifests'
|
13
15
|
EXPECTED_VERSION_SUB_DIRS = [DATA_DIR, MANIFESTS_DIR].freeze
|
14
16
|
MANIFEST_INVENTORY_PATH = File.join(MANIFESTS_DIR, "manifestInventory.xml").freeze
|
15
17
|
SIGNATURE_CATALOG_PATH = File.join(MANIFESTS_DIR, "signatureCatalog.xml").freeze
|
@@ -90,6 +92,7 @@ module Moab
|
|
90
92
|
def check_sequential_version_dirs
|
91
93
|
version_directories.each_with_index do |dir_name, index|
|
92
94
|
next if dir_name[1..-1].to_i == index + 1 # version numbering starts at 1, array indexing at 0
|
95
|
+
|
93
96
|
return [result_hash(VERSIONS_NOT_IN_ORDER, version_directories)]
|
94
97
|
end
|
95
98
|
[]
|
@@ -113,6 +116,7 @@ module Moab
|
|
113
116
|
return expected_version_sub_dirs(version_path, version) if count == EXPECTED_VERSION_SUB_DIRS.size
|
114
117
|
return found_unexpected(version_sub_dirs, version, EXPECTED_VERSION_SUB_DIRS) if count > EXPECTED_VERSION_SUB_DIRS.size
|
115
118
|
return missing_dir(version_sub_dirs, version, EXPECTED_VERSION_SUB_DIRS) if count < EXPECTED_VERSION_SUB_DIRS.size
|
119
|
+
|
116
120
|
[]
|
117
121
|
end
|
118
122
|
|
@@ -122,15 +126,20 @@ module Moab
|
|
122
126
|
data_sub_dirs = directory_entries(data_dir_path)
|
123
127
|
errors.concat check_data_sub_dirs(version, data_sub_dirs)
|
124
128
|
errors.concat check_metadata_dir_files_only(version_path) if errors.empty?
|
125
|
-
|
129
|
+
if data_sub_dirs.include?('content') && errors.empty?
|
130
|
+
errors.concat check_optional_content_dir(version_path, allow_content_subdirs)
|
131
|
+
end
|
126
132
|
errors
|
127
133
|
end
|
128
134
|
|
129
135
|
def check_data_sub_dirs(version, data_sub_dirs)
|
130
136
|
return found_unexpected(data_sub_dirs, version, EXPECTED_DATA_SUB_DIRS) if data_sub_dirs.size > EXPECTED_DATA_SUB_DIRS.size
|
137
|
+
|
131
138
|
errors = []
|
132
139
|
errors.concat missing_dir(data_sub_dirs, version, [METADATA_DIR]) unless data_sub_dirs.include?(METADATA_DIR)
|
133
|
-
|
140
|
+
unless data_sub_dirs.to_set.subset?(EXPECTED_DATA_SUB_DIRS.to_set)
|
141
|
+
errors.concat found_unexpected(data_sub_dirs, version, EXPECTED_DATA_SUB_DIRS)
|
142
|
+
end
|
134
143
|
errors
|
135
144
|
end
|
136
145
|
|
@@ -139,7 +148,9 @@ module Moab
|
|
139
148
|
content_dir_path = File.join(version_path, DATA_DIR, CONTENT_DIR)
|
140
149
|
errors << result_hash(NO_FILES_IN_CONTENT_DIR, basename(version_path)) if directory_entries(content_dir_path).empty?
|
141
150
|
content_sub_dir = contains_sub_dir?(content_dir_path)
|
142
|
-
|
151
|
+
if content_sub_dir && !allow_content_subdirs
|
152
|
+
errors << result_hash(CONTENT_SUB_DIRS_DETECTED, version: basename(version_path), dir: content_sub_dir)
|
153
|
+
end
|
143
154
|
if allow_content_subdirs && contains_sub_dir?(content_dir_path) && contains_forbidden_content_sub_dir?(content_dir_path)
|
144
155
|
errors << result_hash(BAD_SUB_DIR_IN_CONTENT_DIR, basename(version_path))
|
145
156
|
end
|
@@ -159,7 +170,9 @@ module Moab
|
|
159
170
|
metadata_dir_path = File.join(version_path, DATA_DIR, METADATA_DIR)
|
160
171
|
errors << result_hash(NO_FILES_IN_METADATA_DIR, basename(version_path)) if directory_entries(metadata_dir_path).empty?
|
161
172
|
metadata_sub_dir = contains_sub_dir?(metadata_dir_path)
|
162
|
-
|
173
|
+
if metadata_sub_dir
|
174
|
+
errors << result_hash(METADATA_SUB_DIRS_DETECTED, version: basename(version_path), dir: metadata_sub_dir)
|
175
|
+
end
|
163
176
|
errors
|
164
177
|
end
|
165
178
|
|
@@ -228,6 +241,7 @@ module Moab
|
|
228
241
|
|
229
242
|
def check_required_manifest_files(dir, version)
|
230
243
|
return [result_hash(NO_FILES_IN_MANIFEST_DIR, version)] unless contains_file?(File.join(dir, MANIFESTS_DIR))
|
244
|
+
|
231
245
|
errors = []
|
232
246
|
errors << result_hash(NO_MANIFEST_INVENTORY, version) unless File.exist?(File.join(dir, MANIFEST_INVENTORY_PATH))
|
233
247
|
errors << result_hash(NO_SIGNATURE_CATALOG, version) unless File.exist?(File.join(dir, SIGNATURE_CATALOG_PATH))
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A class to represent a version subdirectory within an object's home directory in preservation storage
|
3
5
|
# ====Data Model
|
@@ -68,6 +70,7 @@ module Moab
|
|
68
70
|
def find_filepath(file_category, file_id)
|
69
71
|
this_version_filepath = file_pathname(file_category, file_id)
|
70
72
|
return this_version_filepath if this_version_filepath.exist?
|
73
|
+
|
71
74
|
if file_category == 'manifest'
|
72
75
|
msg = "manifest file #{file_id} not found for #{@storage_object.digital_object_id} - #{@version_id}"
|
73
76
|
raise FileNotFoundException, msg
|
@@ -109,6 +112,7 @@ module Moab
|
|
109
112
|
def file_inventory(type)
|
110
113
|
if version_id > 0
|
111
114
|
return @inventory_cache[type] if @inventory_cache.key?(type)
|
115
|
+
|
112
116
|
@inventory_cache[type] = FileInventory.read_xml_file(@version_pathname.join('manifests'), type)
|
113
117
|
else
|
114
118
|
groups = %w[content metadata].collect { |id| FileGroup.new(:group_id => id) }
|
@@ -136,6 +140,7 @@ module Moab
|
|
136
140
|
# @return [void] Create the version subdirectory and move files into it
|
137
141
|
def ingest_bag_data(bag_dir)
|
138
142
|
raise(MoabRuntimeError, "Version already exists: #{@version_pathname}") if @version_pathname.exist?
|
143
|
+
|
139
144
|
@version_pathname.join('manifests').mkpath
|
140
145
|
bag_dir = Pathname(bag_dir)
|
141
146
|
ingest_dir(bag_dir.join('data'), @version_pathname.join('data'))
|
@@ -150,6 +155,7 @@ module Moab
|
|
150
155
|
# @return [void] recursively link or copy the source directory contents to the target directory
|
151
156
|
def ingest_dir(source_dir, target_dir, use_links = true)
|
152
157
|
raise(MoabRuntimeError, "cannot copy - target already exists: #{target_dir.expand_path}") if target_dir.exist?
|
158
|
+
|
153
159
|
target_dir.mkpath
|
154
160
|
source_dir.children.each do |child|
|
155
161
|
if child.directory?
|
@@ -321,6 +327,7 @@ module Moab
|
|
321
327
|
# @return [null] Deactivate this object version by moving it to another directory. (Used by restore operation)
|
322
328
|
def deactivate(timestamp)
|
323
329
|
return unless @version_pathname.exist?
|
330
|
+
|
324
331
|
timestamp_pathname = @version_pathname.parent.join(timestamp.utc.iso8601.gsub(/[-:]/, ''))
|
325
332
|
timestamp_pathname.mkpath
|
326
333
|
demote_pathame = timestamp_pathname.join(@version_pathname.basename)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A class to represent the SDR repository store
|
3
5
|
#
|
@@ -19,8 +21,10 @@ module Moab
|
|
19
21
|
def storage_roots
|
20
22
|
unless defined?(@storage_roots)
|
21
23
|
raise(MoabRuntimeError, "Moab::Config.storage_roots not found in config file") if Moab::Config.storage_roots.nil?
|
24
|
+
|
22
25
|
@storage_roots = [Moab::Config.storage_roots].flatten.collect { |filesystem| Pathname(filesystem) }
|
23
26
|
raise(MoabRuntimeError, "Moab::Config.storage_roots empty") if @storage_roots.empty?
|
27
|
+
|
24
28
|
@storage_roots.each { |root| raise(MoabRuntimeError, "Storage root #{root} not found on system") unless root.exist? }
|
25
29
|
end
|
26
30
|
@storage_roots
|
@@ -30,7 +34,8 @@ module Moab
|
|
30
34
|
def storage_trunk
|
31
35
|
unless defined?(@storage_trunk)
|
32
36
|
raise(MoabRuntimeError, "Moab::Config.storage_trunk not found in config file") if Moab::Config.storage_trunk.nil?
|
33
|
-
|
37
|
+
|
38
|
+
@storage_trunk = Moab::Config.storage_trunk
|
34
39
|
end
|
35
40
|
@storage_trunk
|
36
41
|
end
|
@@ -50,7 +55,7 @@ module Moab
|
|
50
55
|
unless defined?(@deposit_trunk)
|
51
56
|
# do not raise error. this parameter will be ignored if missing
|
52
57
|
# raise "Moab::Config.deposit_trunk not found in config file" if Moab::Config.deposit_trunk.nil?
|
53
|
-
@deposit_trunk
|
58
|
+
@deposit_trunk = Moab::Config.deposit_trunk
|
54
59
|
end
|
55
60
|
@deposit_trunk
|
56
61
|
end
|
@@ -71,6 +76,7 @@ module Moab
|
|
71
76
|
storage_roots.each do |root|
|
72
77
|
root_trunk = root.join(storage_trunk)
|
73
78
|
raise(MoabRuntimeError, "Storage area not found at #{root_trunk}") unless root_trunk.exist?
|
79
|
+
|
74
80
|
root_trunk_branch = root_trunk.join(branch)
|
75
81
|
return root if root_trunk_branch.exist?
|
76
82
|
end
|
@@ -80,6 +86,7 @@ module Moab
|
|
80
86
|
storage_roots.each do |root|
|
81
87
|
root_trunk = root.join(deposit_trunk)
|
82
88
|
raise(MoabRuntimeError, "Deposit area not found at #{root_trunk}") unless root_trunk.exist?
|
89
|
+
|
83
90
|
root_trunk_branch = root_trunk.join(branch)
|
84
91
|
return root if root_trunk_branch.exist?
|
85
92
|
end
|
@@ -93,10 +100,35 @@ module Moab
|
|
93
100
|
# @return [StorageObject] The representation of a digitial object's storage directory, which might not exist yet.
|
94
101
|
def find_storage_object(object_id, include_deposit = false)
|
95
102
|
root = find_storage_root(object_id, include_deposit)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
103
|
+
create_storage_object(object_id, root)
|
104
|
+
end
|
105
|
+
|
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
|
100
132
|
end
|
101
133
|
|
102
134
|
# @param object_id [String] The identifier of the digital object whose size is desired
|
@@ -118,6 +150,7 @@ module Moab
|
|
118
150
|
storage_object = find_storage_object(object_id)
|
119
151
|
unless storage_object.object_pathname.exist?
|
120
152
|
raise ObjectNotFoundException, "No storage object found for #{object_id}" unless create
|
153
|
+
|
121
154
|
storage_object.object_pathname.mkpath
|
122
155
|
end
|
123
156
|
storage_object
|
@@ -129,5 +162,14 @@ module Moab
|
|
129
162
|
def store_new_object_version(druid, bag_pathname)
|
130
163
|
storage_object(druid, create = true).ingest_bag(bag_pathname)
|
131
164
|
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def create_storage_object(object_id, root)
|
169
|
+
storage_pathname = root.join(storage_trunk, storage_branch(object_id))
|
170
|
+
storage_object = StorageObject.new(object_id, storage_pathname)
|
171
|
+
storage_object.storage_root = root
|
172
|
+
storage_object
|
173
|
+
end
|
132
174
|
end
|
133
175
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# An interface class to support access to SDR storage via a RESTful server
|
3
5
|
#
|
@@ -41,6 +43,13 @@ module Moab
|
|
41
43
|
@@repository.find_storage_object(object_id, include_deposit)
|
42
44
|
end
|
43
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)
|
51
|
+
end
|
52
|
+
|
44
53
|
# @param object_id [String] The identifier of the digital object whose size is desired
|
45
54
|
# @param include_deposit [Boolean] specifies whether to look in deposit areas for objects in process of initial ingest
|
46
55
|
# @return [Integer] the size occupied on disk by the storage object, in bytes. this is the entire moab (all versions).
|
data/lib/moab/utc_time.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moab
|
2
4
|
# A recursive "Tree" type object for verifications
|
3
5
|
class VerificationResult
|
@@ -52,10 +54,9 @@ module Moab
|
|
52
54
|
# @return [Hash] The verification result serialized to a hash
|
53
55
|
def to_hash(verbose = false, level = 0)
|
54
56
|
hash = { 'verified' => verified }
|
55
|
-
if verbose || !verified
|
56
|
-
hash['details'] = details || subentities_to_hash(verbose, level)
|
57
|
-
end
|
57
|
+
hash['details'] = details || subentities_to_hash(verbose, level) if verbose || !verified
|
58
58
|
return hash if level > 0
|
59
|
+
|
59
60
|
{ entity => hash }
|
60
61
|
end
|
61
62
|
|
data/lib/serializer.rb
CHANGED
data/lib/serializer/manifest.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Serializer
|
2
4
|
# Some utility methods to faciliate serialization of data fields to Hash, JSON, or YAML shared by all subclasses.
|
3
5
|
# This class assumes that HappyMapper is used for declaration of fields to be serialized.
|
@@ -21,6 +23,7 @@ module Serializer
|
|
21
23
|
opts.each do |key, value|
|
22
24
|
errmsg = "#{key} is not a variable name in #{self.class.name}"
|
23
25
|
raise(Moab::MoabRuntimeError, errmsg) unless variable_names.include?(key.to_s) || key == :test
|
26
|
+
|
24
27
|
instance_variable_set("@#{key}", value)
|
25
28
|
end
|
26
29
|
end
|
@@ -68,6 +71,7 @@ module Serializer
|
|
68
71
|
# @return [String] For the current object instance, return the string to use as a hash key
|
69
72
|
def key
|
70
73
|
return send(key_name) if key_name
|
74
|
+
|
71
75
|
nil
|
72
76
|
end
|
73
77
|
|
@@ -117,6 +121,7 @@ module Serializer
|
|
117
121
|
# @return [Hash] Generate a hash containing the differences between two objects of the same type
|
118
122
|
def diff(other)
|
119
123
|
raise(Moab::MoabRuntimeError, "Cannot compare different classes") if self.class != other.class
|
124
|
+
|
120
125
|
left = other.to_hash
|
121
126
|
right = to_hash
|
122
127
|
if key.nil? || other.key.nil?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Stanford
|
2
4
|
# Stanford-specific utility methods for transforming contentMetadata to versionInventory and doing comparisons
|
3
5
|
#
|
@@ -127,6 +129,7 @@ module Stanford
|
|
127
129
|
def validate_content_metadata(content_metadata)
|
128
130
|
result = validate_content_metadata_details(content_metadata)
|
129
131
|
raise Moab::InvalidMetadataException, result[0] + " ..." unless result.empty?
|
132
|
+
|
130
133
|
true
|
131
134
|
end
|
132
135
|
|
@@ -176,6 +179,7 @@ module Stanford
|
|
176
179
|
def remediate_content_metadata(content_metadata, content_group)
|
177
180
|
return nil if content_metadata.nil?
|
178
181
|
return content_metadata if content_group.nil? || content_group.files.empty?
|
182
|
+
|
179
183
|
signature_for_path = content_group.path_hash
|
180
184
|
@type_for_name = Moab::FileSignature.checksum_type_for_name
|
181
185
|
@names_for_type = Moab::FileSignature.checksum_names_for_type
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'find'
|
2
4
|
|
3
5
|
module Stanford
|
4
6
|
##
|
5
7
|
# methods for dealing with a directory which stores Moab objects
|
6
8
|
class MoabStorageDirectory
|
7
|
-
DRUID_TREE_REGEXP = '[[:lower:]]{2}/\\d{3}/[[:lower:]]{2}/\\d{4}'
|
8
|
-
DRUID_REGEXP = '[[:lower:]]{2}\\d{3}[[:lower:]]{2}\\d{4}'
|
9
|
+
DRUID_TREE_REGEXP = '[[:lower:]]{2}/\\d{3}/[[:lower:]]{2}/\\d{4}'
|
10
|
+
DRUID_REGEXP = '[[:lower:]]{2}\\d{3}[[:lower:]]{2}\\d{4}'
|
9
11
|
|
10
12
|
def self.find_moab_paths(storage_dir)
|
11
13
|
Find.find(storage_dir) do |path|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'druid-tools'
|
2
4
|
|
3
5
|
module Stanford
|
@@ -32,9 +34,11 @@ module Stanford
|
|
32
34
|
# Note: this seems an odd place to do druid validation, but leaving it here for now
|
33
35
|
syntax_msg = "Identifier has invalid suri syntax: #{object_id}"
|
34
36
|
raise(Moab::InvalidSuriSyntaxError, syntax_msg + " nil or empty") if object_id.to_s.empty?
|
37
|
+
|
35
38
|
identifier = object_id.split(':')[-1]
|
36
39
|
raise(Moab::InvalidSuriSyntaxError, syntax_msg) if identifier.to_s.empty?
|
37
40
|
raise(Moab::InvalidSuriSyntaxError, syntax_msg) unless DruidTools::Druid.valid?(identifier, true)
|
41
|
+
|
38
42
|
DruidTools::Druid.new(identifier, true).tree.join(File::SEPARATOR)
|
39
43
|
end
|
40
44
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moab-versioning
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darren Weber
|
@@ -11,24 +11,24 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2020-01-22 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
|
-
name:
|
17
|
+
name: druid-tools
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: 1.0.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 1.0.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: json
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
34
|
- - ">="
|
@@ -42,7 +42,7 @@ dependencies:
|
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '0'
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
|
-
name: nokogiri
|
45
|
+
name: nokogiri
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
48
|
- - ">="
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: '0'
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
59
|
+
name: nokogiri-happymapper
|
60
60
|
requirement: !ruby/object:Gem::Requirement
|
61
61
|
requirements:
|
62
62
|
- - ">="
|
@@ -70,21 +70,7 @@ dependencies:
|
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0'
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
|
-
name:
|
74
|
-
requirement: !ruby/object:Gem::Requirement
|
75
|
-
requirements:
|
76
|
-
- - ">="
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
version: 1.0.0
|
79
|
-
type: :runtime
|
80
|
-
prerelease: false
|
81
|
-
version_requirements: !ruby/object:Gem::Requirement
|
82
|
-
requirements:
|
83
|
-
- - ">="
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: 1.0.0
|
86
|
-
- !ruby/object:Gem::Dependency
|
87
|
-
name: equivalent-xml
|
73
|
+
name: coveralls
|
88
74
|
requirement: !ruby/object:Gem::Requirement
|
89
75
|
requirements:
|
90
76
|
- - ">="
|
@@ -98,7 +84,7 @@ dependencies:
|
|
98
84
|
- !ruby/object:Gem::Version
|
99
85
|
version: '0'
|
100
86
|
- !ruby/object:Gem::Dependency
|
101
|
-
name:
|
87
|
+
name: equivalent-xml
|
102
88
|
requirement: !ruby/object:Gem::Requirement
|
103
89
|
requirements:
|
104
90
|
- - ">="
|
@@ -112,7 +98,7 @@ dependencies:
|
|
112
98
|
- !ruby/object:Gem::Version
|
113
99
|
version: '0'
|
114
100
|
- !ruby/object:Gem::Dependency
|
115
|
-
name:
|
101
|
+
name: pry-byebug
|
116
102
|
requirement: !ruby/object:Gem::Requirement
|
117
103
|
requirements:
|
118
104
|
- - ">="
|
@@ -126,7 +112,7 @@ dependencies:
|
|
126
112
|
- !ruby/object:Gem::Version
|
127
113
|
version: '0'
|
128
114
|
- !ruby/object:Gem::Dependency
|
129
|
-
name:
|
115
|
+
name: rake
|
130
116
|
requirement: !ruby/object:Gem::Requirement
|
131
117
|
requirements:
|
132
118
|
- - ">="
|
@@ -140,7 +126,7 @@ dependencies:
|
|
140
126
|
- !ruby/object:Gem::Version
|
141
127
|
version: '0'
|
142
128
|
- !ruby/object:Gem::Dependency
|
143
|
-
name:
|
129
|
+
name: rspec
|
144
130
|
requirement: !ruby/object:Gem::Requirement
|
145
131
|
requirements:
|
146
132
|
- - ">="
|
@@ -159,28 +145,28 @@ dependencies:
|
|
159
145
|
requirements:
|
160
146
|
- - "~>"
|
161
147
|
- !ruby/object:Gem::Version
|
162
|
-
version: 0.
|
148
|
+
version: 0.79.0
|
163
149
|
type: :development
|
164
150
|
prerelease: false
|
165
151
|
version_requirements: !ruby/object:Gem::Requirement
|
166
152
|
requirements:
|
167
153
|
- - "~>"
|
168
154
|
- !ruby/object:Gem::Version
|
169
|
-
version: 0.
|
155
|
+
version: 0.79.0
|
170
156
|
- !ruby/object:Gem::Dependency
|
171
157
|
name: rubocop-rspec
|
172
158
|
requirement: !ruby/object:Gem::Requirement
|
173
159
|
requirements:
|
174
160
|
- - "~>"
|
175
161
|
- !ruby/object:Gem::Version
|
176
|
-
version: 1.
|
162
|
+
version: 1.37.1
|
177
163
|
type: :development
|
178
164
|
prerelease: false
|
179
165
|
version_requirements: !ruby/object:Gem::Requirement
|
180
166
|
requirements:
|
181
167
|
- - "~>"
|
182
168
|
- !ruby/object:Gem::Version
|
183
|
-
version: 1.
|
169
|
+
version: 1.37.1
|
184
170
|
description: Contains classes to process digital object version content and metadata
|
185
171
|
email:
|
186
172
|
- darren.weber@stanford.edu
|
@@ -237,7 +223,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
237
223
|
requirements:
|
238
224
|
- - "~>"
|
239
225
|
- !ruby/object:Gem::Version
|
240
|
-
version: '2.
|
226
|
+
version: '2.3'
|
241
227
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
242
228
|
requirements:
|
243
229
|
- - ">="
|