moab-versioning 4.3.0 → 4.4.0
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.
- 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
|
- - ">="
|