Class: Replication::BagitBag
- Inherits:
-
Object
- Object
- Replication::BagitBag
- Defined in:
- lib/replication/bagit_bag.rb
Overview
Copyright (c) 2014 by The Board of Trustees of the Leland Stanford Junior University. All rights reserved. See LICENSE for details.
A BagIt bag contains a structured copy of a digital object for storage, transfer, or replication This class can be used to create, parse, or validate a bag instance
Class Method Summary collapse
-
.create_bag(pathname) ⇒ BagitBag
Initialize a new bag, create home and payload folders, write bagit.txt file.
-
.open_bag(pathname) ⇒ BagitBag
Initialize a new bag, create home and payload folders, write bagit.txt file.
Instance Method Summary collapse
-
#add_data_prefix(file_fixity_hash) ⇒ Hash<String,FileFixity>
A revised hash with file_id paths prefixed with 'data/'.
-
#add_dir_to_payload(link_mode, source_dir) ⇒ Pathname
Generate file_fixity_hash and send it to #add_files_to_payload.
-
#add_files_to_payload(link_mode, source_basepath, file_fixity_hash) ⇒ Pathname
Copy or link the files specified in the file_fixity_hash to the payload directory, then update the payload manifest files.
-
#add_payload_tarfile(tarfile_id, source_fullpath, source_basepath) ⇒ Tarfile
Create a tar archive of a directory into the payload directory, generating checksums in parallel processes and recording those checksums in the payload manifests.
-
#bag_checksum_types ⇒ Array<Symbol>
The list of checksum types to be used when generating fixity data.
-
#bag_checksum_types=(*types) ⇒ Void
Set the list of checksum types to be used when generating fixity data.
-
#bag_pathname ⇒ Pathname
The location of the bag home directory.
-
#bag_pathname=(pathname) ⇒ Void
Set the location of the bag home directory.
-
#bag_payload_size ⇒ Hash<Symbol,Integer>
A hash contining the payload size in bytes, and the number of files, derived from the payload directory contents.
-
#bag_size_human(bytes) ⇒ String
Human-readable rendition of the total payload size.
-
#copy_file(link_mode, source_pathname, target_pathname) ⇒ Pathname
Link or copy the specified file from source location to the target location.
-
#generate_payload_checksums ⇒ Hash<String,FileFixity>
Create hash containing ids and checksums for all files in the bag's payload.
-
#generate_tagfile_checksums ⇒ Hash<String,FileFixity>
Create hash containing ids and checksums for all files in the bag's root directory.
-
#info_payload_size ⇒ Hash<Symbol,Integer>
A hash contining the payload size in bytes, and the number of files, derived from the Payload-Oxum property.
-
#manifest_diff(manifest_fixity_hash, bag_fixity_hash) ⇒ Hash
A report of the differences between the fixity data from the manifest files against the values measured by digesting the files.
-
#payload_pathname ⇒ Pathname
The location of the bag data directory.
-
#read_bag_info_txt ⇒ Hash<String,String] A hash containing the properties documented in the bag-info.txt tagfile
Hash<String,String] A hash containing the properties documented in the bag-info.txt tagfile.
-
#read_bagit_txt ⇒ Hash<String,String] A hash containing the properties documented in the bagit.txt tagfile
Hash<String,String] A hash containing the properties documented in the bagit.txt tagfile.
-
#read_manifest_files(manifest_type) ⇒ Hash<String,FileFixity>
A hash containing file ids and fixity data derived from the manifest files.
-
#verify_bag ⇒ Boolean
Validate the bag containing the digital object.
-
#verify_bag_structure ⇒ Boolean
Test the existence of expected files, return true if files exist, raise exception if not.
-
#verify_manifests(manifest_type, manifest_fixity_hash, bag_fixity_hash) ⇒ Boolean
Compare fixity data from the manifest files against the values measured by digesting the files, returning true if equal or false if not equal.
-
#verify_pathname(pathname) ⇒ Boolean
Test the existence of the specified path.
-
#verify_payload_manifests ⇒ Boolean
Compare fixity data from the payload manifest files against the values measured by digesting the files.
-
#verify_payload_size ⇒ Boolean
Compare the actual measured payload size against the value recorded in bag-info.txt.
-
#verify_tagfile_manifests ⇒ Boolean
Compare fixity data from the tag manifest files against the values measured by digesting the files.
-
#write_bag_info_txt ⇒ Pathname
Generate the bag-info.txt tag file to record the payload size.
-
#write_bagit_txt ⇒ Pathname
Generate the bagit.txt tag file.
-
#write_manifest_checksums(manifest_type, file_fixity_hash, open_mode = 'a') ⇒ Hash<Symbol,Pathname] Update each of the manifests with data from the file_fixity_hash
Hash<Symbol,Pathname] Update each of the manifests with data from the file_fixity_hash.
Class Method Details
.create_bag(pathname) ⇒ BagitBag
Returns Initialize a new bag, create home and payload folders, write bagit.txt file.
16 17 18 19 20 21 22 |
# File 'lib/replication/bagit_bag.rb', line 16 def BagitBag.create_bag(pathname) bag = BagitBag.new bag.bag_pathname = pathname bag.payload_pathname.mkpath bag.write_bagit_txt bag end |
.open_bag(pathname) ⇒ BagitBag
Returns Initialize a new bag, create home and payload folders, write bagit.txt file.
26 27 28 29 30 31 32 33 |
# File 'lib/replication/bagit_bag.rb', line 26 def BagitBag.open_bag(pathname) bag = BagitBag.new bag.bag_pathname = pathname raise "No bag found at #{bag.bag_pathname}" unless bag.bag_pathname.exist? bagit_txt = bag.bag_pathname.join("bagit.txt") raise "No bagit.txt file found at #{bagit_txt}" unless bagit_txt.exist? bag end |
Instance Method Details
#add_data_prefix(file_fixity_hash) ⇒ Hash<String,FileFixity>
Returns A revised hash with file_id paths prefixed with 'data/'.
110 111 112 113 114 115 116 117 |
# File 'lib/replication/bagit_bag.rb', line 110 def add_data_prefix(file_fixity_hash) new_hash = Hash.new file_fixity_hash.values.each do |fixity| fixity.file_id = "data/#{fixity.file_id}" new_hash[fixity.file_id] = fixity end new_hash end |
#add_dir_to_payload(link_mode, source_dir) ⇒ Pathname
Returns Generate file_fixity_hash and send it to #add_files_to_payload.
87 88 89 90 91 |
# File 'lib/replication/bagit_bag.rb', line 87 def add_dir_to_payload (link_mode, source_dir) file_fixity_hash = Fixity.generate_checksums(source_dir, source_dir.find ,bag_checksum_types) add_files_to_payload(link_mode, source_dir, file_fixity_hash) payload_pathname end |
#add_files_to_payload(link_mode, source_basepath, file_fixity_hash) ⇒ Pathname
Returns Copy or link the files specified in the file_fixity_hash to the payload directory, then update the payload manifest files.
98 99 100 101 102 103 104 105 106 |
# File 'lib/replication/bagit_bag.rb', line 98 def add_files_to_payload(link_mode, source_basepath, file_fixity_hash) file_fixity_hash.keys.each do |file_id| source_pathname = source_basepath.join(file_id) target_pathname = payload_pathname.join(file_id) copy_file(link_mode, source_pathname, target_pathname) end write_manifest_checksums('manifest', add_data_prefix(file_fixity_hash)) payload_pathname end |
#add_payload_tarfile(tarfile_id, source_fullpath, source_basepath) ⇒ Tarfile
Returns Create a tar archive of a directory into the payload directory, generating checksums in parallel processes and recording those checksums in the payload manifests.
142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/replication/bagit_bag.rb', line 142 def add_payload_tarfile(tarfile_id,source_fullpath, source_basepath) tarfile = Tarfile.new tarfile.source_basepath = Pathname(source_basepath) tarfile.source_fullpath = Pathname(source_fullpath) tarfile.tarfile_basepath = payload_pathname tarfile.tarfile_fullpath = payload_pathname.join("#{tarfile_id}") tarfile.create_tarfile file_fixity_hash = Fixity.generate_checksums(bag_pathname,[tarfile.tarfile_fullpath],bag_checksum_types) write_manifest_checksums('manifest', file_fixity_hash) tarfile end |
#bag_checksum_types ⇒ Array<Symbol>
Returns The list of checksum types to be used when generating fixity data.
74 75 76 |
# File 'lib/replication/bagit_bag.rb', line 74 def bag_checksum_types @bag_checksum_types ||= Fixity.default_checksum_types end |
#bag_checksum_types=(*types) ⇒ Void
Returns Set the list of checksum types to be used when generating fixity data.
80 81 82 |
# File 'lib/replication/bagit_bag.rb', line 80 def bag_checksum_types=(*types) @bag_checksum_types = Fixity.validate_checksum_types(*types) end |
#bag_pathname ⇒ Pathname
Returns The location of the bag home directory.
36 37 38 |
# File 'lib/replication/bagit_bag.rb', line 36 def bag_pathname @bag_pathname end |
#bag_pathname=(pathname) ⇒ Void
Returns Set the location of the bag home directory.
42 43 44 |
# File 'lib/replication/bagit_bag.rb', line 42 def bag_pathname=(pathname) @bag_pathname = Pathname(pathname) end |
#bag_payload_size ⇒ Hash<Symbol,Integer>
Returns A hash contining the payload size in bytes, and the number of files, derived from the payload directory contents.
168 169 170 171 172 173 174 |
# File 'lib/replication/bagit_bag.rb', line 168 def bag_payload_size payload_pathname.find.select{|f| f.file?}.inject({bytes: 0, files: 0}) do |hash,file| hash[:bytes] += file.size hash[:files] += 1 hash end end |
#bag_size_human(bytes) ⇒ String
Returns Human-readable rendition of the total payload size.
178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/replication/bagit_bag.rb', line 178 def bag_size_human(bytes) count = 0 size = bytes while ( size >= 1024 and count < 4 ) size /= 1024.0 count += 1 end if (count == 0) return sprintf("%d B", size) else return sprintf("%.2f %s", size, %w[B KB MB GB TB][count] ) end end |
#copy_file(link_mode, source_pathname, target_pathname) ⇒ Pathname
Returns link or copy the specified file from source location to the target location.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/replication/bagit_bag.rb', line 123 def copy_file(link_mode, source_pathname, target_pathname) target_pathname.parent.mkpath case link_mode when :copy, nil FileUtils.copy(source_pathname.to_s, target_pathname.to_s) # automatically dereferences symlinks when :link FileUtils.link(source_pathname.to_s, target_pathname.to_s) #, :force => true (false is default) when :symlink FileUtils.symlink(source_pathname.to_s, target_pathname.to_s) #, :force => true (false is default) else raise "Invalid link_mode: #{link_mode}, expected one of [:copy,:link,:symlink]" end target_pathname end |
#generate_payload_checksums ⇒ Hash<String,FileFixity>
Returns create hash containing ids and checksums for all files in the bag's payload.
232 233 234 235 236 237 |
# File 'lib/replication/bagit_bag.rb', line 232 def generate_payload_checksums # get list of all files in the data directory path_list = payload_pathname.find # generate checksums, but use bag home dir as the base directory for file ids (per bagit spec) Fixity.generate_checksums(bag_pathname, path_list, bag_checksum_types) end |
#generate_tagfile_checksums ⇒ Hash<String,FileFixity>
Returns create hash containing ids and checksums for all files in the bag's root directory.
224 225 226 227 228 229 |
# File 'lib/replication/bagit_bag.rb', line 224 def generate_tagfile_checksums # get list of all files in the bag home dir, except those starting with 'tagmanifest' tagfiles = bag_pathname.children.reject{|file| file.basename.to_s.start_with?('tagmanifest')} # generate checksums, using bag home dir as the base directory for file ids (per bagit spec) Fixity.generate_checksums(bag_pathname, tagfiles, bag_checksum_types ) end |
#info_payload_size ⇒ Hash<Symbol,Integer>
Returns A hash contining the payload size in bytes, and the number of files, derived from the Payload-Oxum property.
206 207 208 209 210 211 |
# File 'lib/replication/bagit_bag.rb', line 206 def info_payload_size info = read_bag_info_txt size_array = info['Payload-Oxum'].split('.') size_hash = {:bytes => size_array[0].to_i, :files => size_array[1].to_i} size_hash end |
#manifest_diff(manifest_fixity_hash, bag_fixity_hash) ⇒ Hash
Returns A report of the differences between the fixity data from the manifest files against the values measured by digesting the files.
313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/replication/bagit_bag.rb', line 313 def manifest_diff(manifest_fixity_hash, bag_fixity_hash) diff = Hash.new (manifest_fixity_hash.keys | bag_fixity_hash.keys).each do |file_id| manifest_fixity = manifest_fixity_hash[file_id] || FileFixity.new(file_id: file_id) bag_fixity = bag_fixity_hash[file_id] || FileFixity.new(file_id: file_id) if manifest_fixity != bag_fixity diff[file_id] = manifest_fixity.diff(bag_fixity,'manifest','bag') end end diff end |
#payload_pathname ⇒ Pathname
Returns The location of the bag data directory.
47 48 49 |
# File 'lib/replication/bagit_bag.rb', line 47 def payload_pathname bag_pathname.join('data') end |
#read_bag_info_txt ⇒ Hash<String,String] A hash containing the properties documented in the bag-info.txt tagfile
Returns Hash<String,String] A hash containing the properties documented in the bag-info.txt tagfile.
193 194 195 196 197 198 199 200 201 202 |
# File 'lib/replication/bagit_bag.rb', line 193 def read_bag_info_txt properties = Hash.new bag_info = bag_pathname.join("bag-info.txt") bag_info.readlines.each do |line| line.chomp!.strip! key,value = line.split(':',2) properties[key.strip] = value.strip if value end properties end |
#read_bagit_txt ⇒ Hash<String,String] A hash containing the properties documented in the bagit.txt tagfile
Returns Hash<String,String] A hash containing the properties documented in the bagit.txt tagfile.
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/replication/bagit_bag.rb', line 62 def read_bagit_txt properties = Hash.new bagit_txt = bag_pathname.join("bagit.txt") bagit_txt.readlines.each do |line| line.chomp!.strip! key,value = line.split(':',2) properties[key.strip] = value.strip if value end properties end |
#read_manifest_files(manifest_type) ⇒ Hash<String,FileFixity>
Returns A hash containing file ids and fixity data derived from the manifest files.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/replication/bagit_bag.rb', line 260 def read_manifest_files(manifest_type) file_fixity_hash = Hash.new checksum_type_list = Array.new Fixity.valid_checksum_ids.each do |checksum_type| manifest_pathname = bag_pathname.join("#{manifest_type}-#{checksum_type}.txt") if manifest_pathname.file? checksum_type_list << checksum_type manifest_pathname.readlines.each do |line| line.chomp!.strip! checksum,file_id = line.split(/[\s*]+/,2) file_fixity = file_fixity_hash[file_id] || FileFixity.new(file_id: file_id) file_fixity.set_checksum(checksum_type,checksum) file_fixity_hash[file_id] = file_fixity end end end self.bag_checksum_types = self.bag_checksum_types | checksum_type_list file_fixity_hash end |
#verify_bag ⇒ Boolean
Returns Validate the bag containing the digital object.
326 327 328 329 330 331 332 |
# File 'lib/replication/bagit_bag.rb', line 326 def verify_bag verify_bag_structure verify_tagfile_manifests verify_payload_size verify_payload_manifests true end |
#verify_bag_structure ⇒ Boolean
Returns Test the existence of expected files, return true if files exist, raise exception if not.
335 336 337 338 339 340 |
# File 'lib/replication/bagit_bag.rb', line 335 def verify_bag_structure required_files = ['data','bagit.txt','bag-info.txt','manifest-sha256.txt','tagmanifest-sha256.txt'] required_files.each{|filename| verify_pathname(bag_pathname.join(filename))} optional_files = [] true end |
#verify_manifests(manifest_type, manifest_fixity_hash, bag_fixity_hash) ⇒ Boolean
Compare fixity data from the manifest files against the values measured by digesting the files, returning true if equal or false if not equal
301 302 303 304 305 306 307 |
# File 'lib/replication/bagit_bag.rb', line 301 def verify_manifests(manifest_type, manifest_fixity_hash, bag_fixity_hash) diff = manifest_diff(manifest_fixity_hash, bag_fixity_hash) if diff.size > 0 raise "Failed #{manifest_type} verification! Differences: \n#{diff.inspect}" end true end |
#verify_pathname(pathname) ⇒ Boolean
Returns Test the existence of the specified path. Return true if file exists, raise exception if not.
344 345 346 347 |
# File 'lib/replication/bagit_bag.rb', line 344 def verify_pathname(pathname) raise "#{pathname.basename} not found at #{pathname}" unless pathname.exist? true end |
#verify_payload_manifests ⇒ Boolean
Returns Compare fixity data from the payload manifest files against the values measured by digesting the files.
289 290 291 292 293 294 |
# File 'lib/replication/bagit_bag.rb', line 289 def verify_payload_manifests manifest_type = 'manifest' manifest_fixity_hash = read_manifest_files(manifest_type) bag_fixity_hash = generate_payload_checksums verify_manifests(manifest_type, manifest_fixity_hash, bag_fixity_hash) end |
#verify_payload_size ⇒ Boolean
Returns Compare the actual measured payload size against the value recorded in bag-info.txt.
214 215 216 217 218 219 220 221 |
# File 'lib/replication/bagit_bag.rb', line 214 def verify_payload_size info_size = info_payload_size bag_size = bag_payload_size if info_size != bag_size raise "Failed payload size verification! Expected: #{info_size}, Found: #{bag_size}" end true end |
#verify_tagfile_manifests ⇒ Boolean
Returns Compare fixity data from the tag manifest files against the values measured by digesting the files.
281 282 283 284 285 286 |
# File 'lib/replication/bagit_bag.rb', line 281 def verify_tagfile_manifests manifest_type = 'tagmanifest' manifest_fixity_hash = read_manifest_files(manifest_type) bag_fixity_hash = generate_tagfile_checksums verify_manifests(manifest_type, manifest_fixity_hash, bag_fixity_hash) end |
#write_bag_info_txt ⇒ Pathname
Returns Generate the bag-info.txt tag file to record the payload size.
155 156 157 158 159 160 161 162 163 164 |
# File 'lib/replication/bagit_bag.rb', line 155 def write_bag_info_txt payload_size = bag_payload_size bag_info_txt = bag_pathname.join("bag-info.txt") bag_info_txt.open('w') do |f| f.puts "External-Identifier: #{bag_pathname.basename}" f.puts "Payload-Oxum: #{payload_size[:bytes]}.#{payload_size[:files]}" f.puts "Bag-Size: #{bag_size_human(payload_size[:bytes])}" end bag_info_txt end |
#write_bagit_txt ⇒ Pathname
Returns Generate the bagit.txt tag file.
52 53 54 55 56 57 58 59 |
# File 'lib/replication/bagit_bag.rb', line 52 def write_bagit_txt bagit_txt = bag_pathname.join("bagit.txt") bagit_txt.open('w') do |f| f.puts "Tag-File-Character-Encoding: UTF-8" f.puts "BagIt-Version: 0.97" end bagit_txt end |
#write_manifest_checksums(manifest_type, file_fixity_hash, open_mode = 'a') ⇒ Hash<Symbol,Pathname] Update each of the manifests with data from the file_fixity_hash
Returns Hash<Symbol,Pathname] Update each of the manifests with data from the file_fixity_hash.
243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/replication/bagit_bag.rb', line 243 def write_manifest_checksums(manifest_type, file_fixity_hash, open_mode='a') manifests = Hash.new self.bag_checksum_types.each do |checksum_type| manifest_pathname = bag_pathname.join("#{manifest_type}-#{checksum_type}.txt") manifest_file = manifest_pathname.open(open_mode) file_fixity_hash.values.each do |fixity| checksum = fixity.get_checksum(checksum_type) manifest_file.puts("#{checksum} #{fixity.file_id}") if checksum end manifest_file.close manifests[checksum_type] = manifest_pathname end manifests end |