lyber-utils 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +31 -0
- data/lib/lyber-utils.rb +3 -0
- data/lib/lyber_utils/bagit_bag.rb +108 -0
- data/lib/lyber_utils/checksum_validate.rb +109 -0
- data/lib/lyber_utils/file_utilities.rb +169 -0
- data/lib/tasks/rdoc.rake +32 -0
- metadata +241 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Stanford University Library
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
= lyber_utils
|
2
|
+
|
3
|
+
Require the following:
|
4
|
+
require 'lyber_utils'
|
5
|
+
|
6
|
+
|
7
|
+
This will give you:
|
8
|
+
LyberUtils::BagitBag
|
9
|
+
LyberUtils::ChecksumValidate
|
10
|
+
LyberUtils::FileUtilities
|
11
|
+
|
12
|
+
If you do not want all 3, you can require the individual classes. I.E. if you only want the bagit utils, then require:
|
13
|
+
require 'lyber_utils/bagit_bag'
|
14
|
+
|
15
|
+
The BagitBag class requires the bagit gem
|
16
|
+
http://github.com/flazz/bagit
|
17
|
+
|
18
|
+
== Build and release procedure
|
19
|
+
Modify the version number in lyber-utils.gemspec, then push your commits to AFS. DO NOT TAG!
|
20
|
+
Run: 'rake dlss_release' to tag, build, and publish the lyber-core gem
|
21
|
+
See the Rakefile and the LyberCore::DlssRelease task in lyberteam-devel/lib/dlss/rake/dlss_release.rb
|
22
|
+
for more details
|
23
|
+
|
24
|
+
== Releases
|
25
|
+
- <b>1.3</b> Started to use Dor::Config for workspace configuration
|
26
|
+
- <b>0.1.1</b> Update dependent version of nokogiri for more flexibility
|
27
|
+
- <b>0.1.2</b> Ensure bag begins with an empty directory. Disallow nil values in bag-info.txt properties
|
28
|
+
|
29
|
+
== Copyright
|
30
|
+
|
31
|
+
Copyright (c) 2011 Stanford University Library. See LICENSE for details.
|
data/lib/lyber-utils.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'bagit' # http://github.com/flazz/bagit
|
4
|
+
|
5
|
+
module LyberUtils
|
6
|
+
|
7
|
+
class BagitBag
|
8
|
+
|
9
|
+
attr_reader :bag_dir
|
10
|
+
|
11
|
+
def initialize(bag_dir)
|
12
|
+
@bag_dir = bag_dir
|
13
|
+
create_bag
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_bag()
|
17
|
+
if (File.exist?(@bag_dir))
|
18
|
+
FileUtils.rm_r(@bag_dir)
|
19
|
+
end
|
20
|
+
@bag = BagIt::Bag.new @bag_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_content_files(source_dir, use_links)
|
24
|
+
data_content_dir = File.join(@bag_dir, 'data', 'content')
|
25
|
+
copy_dir(source_dir, data_content_dir, use_links)
|
26
|
+
end
|
27
|
+
|
28
|
+
def copy_dir(source_dir, target_dir, use_links)
|
29
|
+
FileUtils.mkdir_p(target_dir)
|
30
|
+
Dir.foreach(source_dir) do |file|
|
31
|
+
unless (file == '.' or file == '..')
|
32
|
+
source_file = File.join(source_dir, file)
|
33
|
+
target_file = File.join(target_dir, file)
|
34
|
+
if File.directory?(source_file)
|
35
|
+
copy_dir(source_file, target_file, use_links)
|
36
|
+
elsif (use_links)
|
37
|
+
FileUtils.ln(source_file, target_file, :force => true)
|
38
|
+
else
|
39
|
+
FileUtils.cp(source_file, target_file)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_metadata_file_from_string(metadata_string, file_name)
|
46
|
+
if (not metadata_string.nil?)
|
47
|
+
data_file_path = "metadata/#{file_name}"
|
48
|
+
@bag.add_file(data_file_path) do |io|
|
49
|
+
io.puts metadata_string
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def write_metadata_info(md_hash)
|
55
|
+
# make sure none of the md_hash values are nil
|
56
|
+
md_hash.each do |key, value|
|
57
|
+
raise "The bag-info.txt value for #{key} is not allowed to be nil" if value.nil?
|
58
|
+
end
|
59
|
+
payload = bag_payload()
|
60
|
+
bag_info_hash = {
|
61
|
+
'Bag-Size' => bag_size_human(payload[0]),
|
62
|
+
'Payload-Oxum' => "#{payload[0]}.#{payload[1]}",
|
63
|
+
'Bagging-Date' => DateTime.now.to_s[0..9]
|
64
|
+
}
|
65
|
+
@bag.write_bag_info(md_hash.merge(bag_info_hash))
|
66
|
+
end
|
67
|
+
|
68
|
+
def bag_payload()
|
69
|
+
bytes = 0
|
70
|
+
files = 0
|
71
|
+
Find.find(@bag.data_dir) do |filepath|
|
72
|
+
if (not File.directory?(filepath))
|
73
|
+
bytes += File.size(filepath)
|
74
|
+
files += 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
return [bytes, files]
|
78
|
+
end
|
79
|
+
|
80
|
+
def bag_size_human(bytes)
|
81
|
+
count = 0
|
82
|
+
size = bytes
|
83
|
+
while (size >= 1000 and count < 4)
|
84
|
+
size /= 1000.0
|
85
|
+
count += 1
|
86
|
+
end
|
87
|
+
if (count == 0)
|
88
|
+
return sprintf("%d B", size)
|
89
|
+
else
|
90
|
+
return sprintf("%.2f %s", size, %w[B KB MB GB TB][count])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def write_manifests()
|
95
|
+
@bag.manifest!
|
96
|
+
@bag.tagmanifest!
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate()
|
100
|
+
if not @bag.valid?
|
101
|
+
raise "bag not valid: #{@bag_dir}"
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
|
4
|
+
module LyberUtils
|
5
|
+
|
6
|
+
class ChecksumValidate
|
7
|
+
|
8
|
+
# Test the equality of two hashes
|
9
|
+
# @return [Boolean]
|
10
|
+
def self.compare_hashes(hash1, hash2)
|
11
|
+
return (hash1 == hash2)
|
12
|
+
end
|
13
|
+
|
14
|
+
# create a new hash containing:
|
15
|
+
# entries from hash1 where:
|
16
|
+
# * key is in hash1 but missing from hash2
|
17
|
+
# * value is different in the two hashes
|
18
|
+
# entries from hash2 where:
|
19
|
+
# * key is in hash2 but missing from hash1
|
20
|
+
# @return [Hash]
|
21
|
+
def self.get_hash_differences(hash1, hash2)
|
22
|
+
hash1.reject { |k, v| hash2[k] == v }.merge!(hash2.reject { |k, v| hash1.has_key?(k) })
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generate a filename => checksum hash
|
26
|
+
# from an output of the md5sum (or compatible) program
|
27
|
+
# @return [Hash]
|
28
|
+
def self.md5_hash_from_md5sum(md5sum)
|
29
|
+
checksum_hash = {}
|
30
|
+
md5sum.each do |line|
|
31
|
+
line.chomp!
|
32
|
+
digest, filename = line.split(/[ *]{2}/)
|
33
|
+
checksum_hash[filename] = digest.downcase
|
34
|
+
end
|
35
|
+
return checksum_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate a filename => checksum hash
|
39
|
+
# from the contents of a METS file
|
40
|
+
# @return [Hash]
|
41
|
+
def self.md5_hash_from_mets(mets)
|
42
|
+
mets_checksum_hash = {}
|
43
|
+
doc = Nokogiri::XML(mets)
|
44
|
+
doc.xpath('/mets:mets/mets:fileSec//mets:file', {'mets' => 'http://www.loc.gov/METS/'}).each do |filenode|
|
45
|
+
digest = filenode.attribute('CHECKSUM')
|
46
|
+
if (digest)
|
47
|
+
flocat = filenode.xpath('mets:FLocat', {'mets' => 'http://www.loc.gov/METS/'}).first
|
48
|
+
if (flocat)
|
49
|
+
filename = flocat.attribute_with_ns('href', 'http://www.w3.org/1999/xlink')
|
50
|
+
if (filename)
|
51
|
+
mets_checksum_hash[filename.text] = digest.text.downcase
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
return mets_checksum_hash
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generate a filename => checksum hash
|
60
|
+
# from contentMetadata XML
|
61
|
+
# @return [Hash]
|
62
|
+
def self.md5_hash_from_content_metadata(content_md)
|
63
|
+
content_md_checksum_hash = {}
|
64
|
+
doc = Nokogiri::XML(content_md)
|
65
|
+
doc.xpath('/contentMetadata/resource[@type="page"]/file').each do |filenode|
|
66
|
+
filename = filenode.attribute('id')
|
67
|
+
if (filename)
|
68
|
+
md5_element = filenode.xpath('checksum[@type="MD5"]').first
|
69
|
+
if (md5_element)
|
70
|
+
digest = md5_element.text
|
71
|
+
if (digest)
|
72
|
+
content_md_checksum_hash[filename.text] = digest.downcase
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
return content_md_checksum_hash
|
78
|
+
end
|
79
|
+
|
80
|
+
# Verifies MD5 checksums for the files in a directory
|
81
|
+
# against the checksum values in the supplied file
|
82
|
+
# (Uses md5sum command)
|
83
|
+
#
|
84
|
+
# = Inputs:
|
85
|
+
# * directory = dirname containing the file to be checked
|
86
|
+
# * checksum_file = the name of the file containing the expected checksums
|
87
|
+
#
|
88
|
+
# = Return value:
|
89
|
+
# * The method will return true if the verification is successful.
|
90
|
+
# * The method will raise an exception if either the md5sum command fails,
|
91
|
+
# or a test of the md5sum output indicates a checksum mismatch.
|
92
|
+
# The exception's message will contain the explaination of the failure.
|
93
|
+
def self.verify_md5sum_checksums(directory, checksum_file)
|
94
|
+
# LyberCore::Log.debug("verifying checksums in #{directory}")
|
95
|
+
dir_save = Dir.pwd
|
96
|
+
Dir.chdir(directory)
|
97
|
+
checksum_cmd = 'md5sum -c ' + checksum_file + ' | grep -v OK | wc -l'
|
98
|
+
badcount = FileUtilities.execute(checksum_cmd).to_i
|
99
|
+
if not (badcount==0)
|
100
|
+
raise "#{badcount} files had bad checksums"
|
101
|
+
end
|
102
|
+
return true
|
103
|
+
ensure
|
104
|
+
Dir.chdir(dir_save)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'systemu'
|
3
|
+
|
4
|
+
# File Utilities for use in transferring filesystem objects,
|
5
|
+
# decrypting a file, unpacking a targz archive, and validating checksums
|
6
|
+
# @author rnanders@stanford.edu
|
7
|
+
module LyberUtils
|
8
|
+
|
9
|
+
class FileUtilities
|
10
|
+
|
11
|
+
|
12
|
+
# Executes a system command in a subprocess.
|
13
|
+
# The method will return stdout from the command if execution was successful.
|
14
|
+
# The method will raise an exception if if execution fails.
|
15
|
+
# The exception's message will contain the explaination of the failure.
|
16
|
+
# @param [String] command the command to be executed
|
17
|
+
# @return [String] stdout from the command if execution was successful
|
18
|
+
def FileUtilities.execute(command)
|
19
|
+
status, stdout, stderr = systemu(command)
|
20
|
+
if (status.exitstatus != 0)
|
21
|
+
raise stderr
|
22
|
+
end
|
23
|
+
return stdout
|
24
|
+
rescue
|
25
|
+
msg = "Command failed to execute: [#{command}] caused by <STDERR = #{stderr.split($/).join('; ')}>"
|
26
|
+
msg << " STDOUT = #{stdout.split($/).join('; ')}" if (stdout && (stdout.length > 0))
|
27
|
+
raise msg
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# Generates a dirname for storing or retrieving a file in
|
32
|
+
# "pair tree" hierachical structure, where the path is derived
|
33
|
+
# from segments of a barcode string
|
34
|
+
#
|
35
|
+
# = Input:
|
36
|
+
# * barcode = barcode string
|
37
|
+
#
|
38
|
+
# = Return value:
|
39
|
+
# * A string containing a slash-delimited dirname derived from the barcode
|
40
|
+
def FileUtilities.pair_tree_from_barcode(barcode)
|
41
|
+
if (barcode.class != String)
|
42
|
+
raise "Barcode must be a String"
|
43
|
+
end
|
44
|
+
# figure out if this is a SUL barcode or from coordinate library
|
45
|
+
library_prefix=barcode[0..4]
|
46
|
+
if (library_prefix == '36105')
|
47
|
+
pair_tree=barcode[5..10].gsub(/(..)/, '\1/')
|
48
|
+
else
|
49
|
+
library_prefix=barcode[0..2]
|
50
|
+
pair_tree=barcode[3..8].gsub(/(..)/, '\1/')
|
51
|
+
end
|
52
|
+
return "#{library_prefix}/#{pair_tree}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Transfers a filesystem object (file or directory)
|
56
|
+
# from a source to a target location. Uses rsync in "archive" mode
|
57
|
+
# over an ssh connection.
|
58
|
+
#
|
59
|
+
# = Inputs:
|
60
|
+
# * filename = basename of the filesystem object to be transferred
|
61
|
+
# * source_dir = dirname of the source location from which the object is read
|
62
|
+
# * dest_dir = dirname of the target location to which the object is written
|
63
|
+
# If one of the locations is on a remote server, then the dirname should be
|
64
|
+
# prefixed with user@hosthame:
|
65
|
+
#
|
66
|
+
# = Return value:
|
67
|
+
# * The method will return true if the transfer is successful.
|
68
|
+
# * The method will raise an exception if either the rsync command fails,
|
69
|
+
# or a test for the existence of the transferred object fails.
|
70
|
+
# The exception's message will contain the explaination of the failure
|
71
|
+
#
|
72
|
+
# Network transfers will only succeed if the appropriate public key
|
73
|
+
# authentication has been previously set up.
|
74
|
+
def FileUtilities.transfer_object(filename, source_dir, dest_dir)
|
75
|
+
source_path=File.join(source_dir, filename)
|
76
|
+
rsync='rsync -a -e ssh '
|
77
|
+
rsync_cmd = rsync + "'" + source_path + "' " + dest_dir
|
78
|
+
# LyberCore::Log.debug("rsync command is: #{rsync_cmd}")
|
79
|
+
self.execute(rsync_cmd)
|
80
|
+
if not File.exists?(File.join(dest_dir, filename))
|
81
|
+
raise "#{filename} is not found in #{dest_dir}"
|
82
|
+
end
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
|
86
|
+
# Decrypts a GPG encrypted file using the "gpg" command
|
87
|
+
#
|
88
|
+
# = Inputs:
|
89
|
+
# * workspace_dir = dirname containing the file
|
90
|
+
# * targzgpg = the filename of the GPG encrypted file
|
91
|
+
# * targz = the filename of the unencrypted file
|
92
|
+
# * passphrase = the string used to decrypt the file
|
93
|
+
#
|
94
|
+
# = Return value:
|
95
|
+
# * The method will return true if the decryption is successful.
|
96
|
+
# * The method will raise an exception if either the decryption command fails,
|
97
|
+
# or a test for the existence of the decrypted file fails.
|
98
|
+
# The exception's message will contain the explaination of the failure
|
99
|
+
def FileUtilities.gpgdecrypt(workspace_dir, targzgpg, targz, passphrase)
|
100
|
+
# LyberCore::Log.debug("decrypting #{targzgpg}")
|
101
|
+
gpg_cmd="/usr/bin/gpg --passphrase '#{passphrase}' " +
|
102
|
+
"--batch --no-mdc-warning --no-secmem-warning " +
|
103
|
+
" --output " + File.join(workspace_dir, targz) +
|
104
|
+
" --decrypt " + File.join(workspace_dir, targzgpg)
|
105
|
+
self.execute(gpg_cmd)
|
106
|
+
if not File.exists?(File.join(workspace_dir, targz))
|
107
|
+
raise "#{targz} was not created in #{workspace_dir}"
|
108
|
+
end
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
|
112
|
+
# Unpacks a TAR-ed, GZipped archive using a "tar -xzf" command
|
113
|
+
#
|
114
|
+
# = Inputs:
|
115
|
+
# * original_dir = dirname containing the archive file
|
116
|
+
# * targz = the filename of the archive file
|
117
|
+
# * destination_dir = the target directory into which the contents are written
|
118
|
+
#
|
119
|
+
# = Return value:
|
120
|
+
# * The method will return true if the unpacking is successful.
|
121
|
+
# * The method will raise an exception if either the unpack command fails,
|
122
|
+
# or a test for the existence of files in the target directory fails.
|
123
|
+
# The exception's message will contain the explaination of the failure.
|
124
|
+
def FileUtilities.unpack(original_dir, targz, destination_dir)
|
125
|
+
# LyberCore::Log.debug("unpacking #{targz}")
|
126
|
+
FileUtils.mkdir_p(destination_dir)
|
127
|
+
dir_save = Dir.pwd
|
128
|
+
Dir.chdir(destination_dir)
|
129
|
+
unpack_cmd="tar -xzf " + File.join(original_dir, targz)
|
130
|
+
self.execute(unpack_cmd)
|
131
|
+
if not (Dir.entries(destination_dir).length > 0)
|
132
|
+
raise "#{destination_dir} is empty"
|
133
|
+
end
|
134
|
+
return true
|
135
|
+
ensure
|
136
|
+
Dir.chdir(dir_save)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Tars a directory hierarchy
|
140
|
+
#
|
141
|
+
# = Inputs:
|
142
|
+
# * source_path = the filesystem object to be tarred
|
143
|
+
# * dest_path = name of the tar file to be written
|
144
|
+
# (if nil create sourcname.tar in the same dir as the source object)
|
145
|
+
#
|
146
|
+
# = Return value:
|
147
|
+
# * The method will return true if the transfer is successful.
|
148
|
+
# * The method will raise an exception if either the rsync command fails,
|
149
|
+
# or a test for the existence of the transferred object fails.
|
150
|
+
# The exception's message will contain the explaination of the failure
|
151
|
+
#
|
152
|
+
def FileUtilities.tar_object(source_path, dest_path=nil)
|
153
|
+
if (dest_path.nil?)
|
154
|
+
dest_path = source_path + ".tar"
|
155
|
+
end
|
156
|
+
parent_path = File.dirname(source_path)
|
157
|
+
object_name = File.basename(source_path)
|
158
|
+
tar="cd #{parent_path}; tar --force-local -chf "
|
159
|
+
tar_cmd = "#{tar} '#{dest_path}' '#{object_name}'"
|
160
|
+
self.execute(tar_cmd)
|
161
|
+
if not File.exists?(dest_path)
|
162
|
+
raise "#{dest_path} was not created"
|
163
|
+
end
|
164
|
+
return true
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
data/lib/tasks/rdoc.rake
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
desc "Generate RDoc"
|
2
|
+
task :doc => ['doc:generate']
|
3
|
+
|
4
|
+
namespace :doc do
|
5
|
+
project_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
6
|
+
doc_destination = File.join(project_root, 'rdoc')
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'yard'
|
10
|
+
require 'yard/rake/yardoc_task'
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new(:generate) do |yt|
|
13
|
+
yt.files = Dir.glob(File.join(project_root, 'lib', '*.rb')) +
|
14
|
+
Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
|
15
|
+
[ File.join(project_root, 'README.rdoc') ] +
|
16
|
+
[ File.join(project_root, 'LICENSE') ]
|
17
|
+
|
18
|
+
yt.options = ['--output-dir', doc_destination, '--readme', 'README.rdoc']
|
19
|
+
end
|
20
|
+
rescue LoadError
|
21
|
+
desc "Generate YARD Documentation"
|
22
|
+
task :generate do
|
23
|
+
abort "Please install the YARD gem to generate rdoc."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Remove generated documenation"
|
28
|
+
task :clean do
|
29
|
+
rm_r doc_destination if File.exists?(doc_destination)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lyber-utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Richard Anderson
|
14
|
+
- Willy Mene
|
15
|
+
- Michael Klein
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2011-09-27 00:00:00 Z
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 27
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: 0.1.0
|
34
|
+
version_requirements: *id001
|
35
|
+
name: bagit
|
36
|
+
prerelease: false
|
37
|
+
type: :runtime
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 113
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 4
|
48
|
+
- 3
|
49
|
+
- 1
|
50
|
+
version: 1.4.3.1
|
51
|
+
version_requirements: *id002
|
52
|
+
name: nokogiri
|
53
|
+
prerelease: false
|
54
|
+
type: :runtime
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 31
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 2
|
65
|
+
- 0
|
66
|
+
version: 1.2.0
|
67
|
+
version_requirements: *id003
|
68
|
+
name: systemu
|
69
|
+
prerelease: false
|
70
|
+
type: :runtime
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
version_requirements: *id004
|
82
|
+
name: validatable
|
83
|
+
prerelease: false
|
84
|
+
type: :runtime
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 13
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
- 4
|
95
|
+
- 1
|
96
|
+
version: 0.4.1
|
97
|
+
version_requirements: *id005
|
98
|
+
name: lyberteam-devel
|
99
|
+
prerelease: false
|
100
|
+
type: :development
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 49
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
- 8
|
111
|
+
- 7
|
112
|
+
version: 0.8.7
|
113
|
+
version_requirements: *id006
|
114
|
+
name: rake
|
115
|
+
prerelease: false
|
116
|
+
type: :development
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
version_requirements: *id007
|
128
|
+
name: rcov
|
129
|
+
prerelease: false
|
130
|
+
type: :development
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 3
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
version: "0"
|
141
|
+
version_requirements: *id008
|
142
|
+
name: rdoc
|
143
|
+
prerelease: false
|
144
|
+
type: :development
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
148
|
+
requirements:
|
149
|
+
- - <
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
hash: 3
|
152
|
+
segments:
|
153
|
+
- 2
|
154
|
+
- 0
|
155
|
+
version: "2.0"
|
156
|
+
version_requirements: *id009
|
157
|
+
name: rspec
|
158
|
+
prerelease: false
|
159
|
+
type: :development
|
160
|
+
- !ruby/object:Gem::Dependency
|
161
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
170
|
+
version_requirements: *id010
|
171
|
+
name: ruby-debug
|
172
|
+
prerelease: false
|
173
|
+
type: :development
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
hash: 3
|
181
|
+
segments:
|
182
|
+
- 0
|
183
|
+
version: "0"
|
184
|
+
version_requirements: *id011
|
185
|
+
name: yard
|
186
|
+
prerelease: false
|
187
|
+
type: :development
|
188
|
+
description: Contains classes perform generic utilty functions
|
189
|
+
email:
|
190
|
+
- rnanders@stanford.edu
|
191
|
+
executables: []
|
192
|
+
|
193
|
+
extensions: []
|
194
|
+
|
195
|
+
extra_rdoc_files: []
|
196
|
+
|
197
|
+
files:
|
198
|
+
- lib/lyber-utils.rb
|
199
|
+
- lib/lyber_utils/bagit_bag.rb
|
200
|
+
- lib/lyber_utils/checksum_validate.rb
|
201
|
+
- lib/lyber_utils/file_utilities.rb
|
202
|
+
- lib/tasks/rdoc.rake
|
203
|
+
- LICENSE
|
204
|
+
- README.rdoc
|
205
|
+
homepage:
|
206
|
+
licenses: []
|
207
|
+
|
208
|
+
post_install_message:
|
209
|
+
rdoc_options: []
|
210
|
+
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
none: false
|
215
|
+
requirements:
|
216
|
+
- - ">="
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
hash: 3
|
219
|
+
segments:
|
220
|
+
- 0
|
221
|
+
version: "0"
|
222
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
223
|
+
none: false
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
hash: 23
|
228
|
+
segments:
|
229
|
+
- 1
|
230
|
+
- 3
|
231
|
+
- 6
|
232
|
+
version: 1.3.6
|
233
|
+
requirements: []
|
234
|
+
|
235
|
+
rubyforge_project:
|
236
|
+
rubygems_version: 1.8.10
|
237
|
+
signing_key:
|
238
|
+
specification_version: 3
|
239
|
+
summary: Generic utilities used by the SULAIR Digital Library
|
240
|
+
test_files: []
|
241
|
+
|