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 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.
@@ -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.
@@ -0,0 +1,3 @@
1
+ require 'lyber_utils/file_utilities'
2
+ require 'lyber_utils/checksum_validate'
3
+ require 'lyber_utils/bagit_bag'
@@ -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
@@ -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
+