Class: OcflTools::OcflObject
- Inherits:
-
Object
- Object
- OcflTools::OcflObject
- Defined in:
- lib/ocfl_tools/ocfl_object.rb
Overview
Class that represents the data structures used by an OCFL inventory file.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#contentDirectory ⇒ String
The name of the directory, inside each version directory, that the OCFL object should use as the base directory for files.
-
#digestAlgorithm ⇒ String
Algorithm used by the OCFL object to generate digests for file manifests and versions.
-
#fixity ⇒ Hash
Fixity block of the OCFL object.
-
#head ⇒ String
The most recent version of the OCFL object, expressed as a string that conforms to the format defined in version_format.
-
#id ⇒ String
Id the unique identifer of the OCFL object, as defined by the local repository system.
-
#manifest ⇒ Hash
Manifest block of the OCFL object.
-
#type ⇒ String
The version of the OCFL spec to which this object conforms, expressed as a URL, as required by the OCFL specification.
-
#versions ⇒ Hash
Versions block of the OCFL object.
Instance Method Summary collapse
-
#add_file(file, digest, version) ⇒ Hash
Adds a file to a version.
-
#copy_file(source_file, destination_file, version) ⇒ Hash
Copies a file within the same version.
-
#create_version_hash ⇒ Hash
Returns a version hash with the correct keys created, ready for content to be added.
-
#delete_file(file, version) ⇒ Hash
Given a filepath, deletes that file from the given version.
-
#get_current_files ⇒ Hash
Gets all files for the current (highest) version of the OCFL object.
-
#get_digest(file, version) ⇒ String
When given a file path and version, return the associated digest from version state.
-
#get_files(version) ⇒ Hash
Gets a hash of all logical files and their associated physical filepaths with the given version.
-
#get_state(version) ⇒ Hash
Gets the state block of a given version, comprising of digest keys and an array of filenames associated with those digests.
-
#get_version(version) ⇒ Hash
Gets the existing version hash for the requested version, or else creates and populates a new, empty version hash.
-
#get_version_created(version) ⇒ String
returns the created field for a given version.
-
#get_version_message(version) ⇒ String
returns the message field for a given version.
-
#get_version_user(version) ⇒ Hash
Gets the user Hash for a given version.
-
#initialize ⇒ OcflObject
constructor
A new instance of OcflObject.
-
#move_file(old_file, new_file, version) ⇒ Hash
Moves (renames) a file from one location to another within the same version.
-
#set_head_from_version(version) ⇒ @head
sets @head in current string format, when given integer.
-
#set_state(version, hash) ⇒ Object
Sets the state block for a given version when provided with a hash of digest keys and an array of associated filenames.
-
#set_version(version, hash) ⇒ Object
When given a correctly-constructed hash, create a new OCFL version.
-
#set_version_created(version, created) ⇒ Object
sets the created field for a given version.
-
#set_version_message(version, message) ⇒ Object
sets the message field for a given version.
-
#set_version_user(version, user) ⇒ Object
Sets the user Hash for a given version.
-
#update_file(file, digest, version) ⇒ Object
Updates an existing file with a new bitstream and digest.
-
#update_fixity(digest, fixityAlgorithm, fixityDigest) ⇒ Hash
Given a digest, fixityAlgo and fixityDigest, add to fixity block.
-
#update_manifest(file, digest, version) ⇒ Object
Add a file and digest to the manifest at the given version.
-
#version_id_list ⇒ Array{Integer}
Gets an array of integers comprising all versions of this OCFL object.
Constructor Details
#initialize ⇒ OcflObject
Returns a new instance of OcflObject.
30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 30 def initialize # Parameters that must be serialized into JSON @id = nil @head = nil @type = nil # OcflTools.config.content_type @digestAlgorithm = OcflTools.config.digest_algorithm # sha512 is recommended, Stanford uses sha256. @contentDirectory = OcflTools.config.content_directory # default is 'content', Stanford uses 'data' @manifest = {} @versions = {} # A hash of Version hashes. @fixity = {} # Optional. Same format as Manifest. end |
Instance Attribute Details
#contentDirectory ⇒ String
Returns the name of the directory, inside each version directory, that the OCFL object should use as the base directory for files.
28 29 30 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 28 def contentDirectory @contentDirectory end |
#digestAlgorithm ⇒ String
Returns algorithm used by the OCFL object to generate digests for file manifests and versions.
19 20 21 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 19 def digestAlgorithm @digestAlgorithm end |
#fixity ⇒ Hash
Returns fixity block of the OCFL object.
13 14 15 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 13 def fixity @fixity end |
#head ⇒ String
Returns the most recent version of the OCFL object, expressed as a string that conforms to the format defined in version_format.
22 23 24 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 22 def head @head end |
#id ⇒ String
Returns id the unique identifer of the OCFL object, as defined by the local repository system.
16 17 18 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 16 def id @id end |
#manifest ⇒ Hash
Returns manifest block of the OCFL object.
7 8 9 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 7 def manifest @manifest end |
#type ⇒ String
Returns the version of the OCFL spec to which this object conforms, expressed as a URL, as required by the OCFL specification.
25 26 27 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 25 def type @type end |
#versions ⇒ Hash
Returns versions block of the OCFL object.
10 11 12 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 10 def versions @versions end |
Instance Method Details
#add_file(file, digest, version) ⇒ Hash
will raise an error if an attempt is made to add a file to a prior (non-head) version. Will also raise an error if the requested file already exists in this version with a different digest: use #update_file instead.
Adds a file to a version.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 181 def add_file(file, digest, version) # We use get_state here instead of asking @versions directly # because get_state will create version hash if it doesn't already exist. my_state = get_state(version) unless version == version_id_list.max raise OcflTools::Errors::CannotEditPreviousVersion, "Can't edit prior versions! Only version #{version_id_list.max} can be modified now." end # if the key is not in the manifest, assume that we meant to add it. update_manifest(file, digest, version) unless @manifest.key?(digest) if my_state.key?(digest) # file's already in this version. Add file to existing digest. my_files = my_state[digest] my_files << file unique_files = my_files.uniq # Just in case we're trying to add the same thing multiple times. # Need to actually add this to @versions! @versions[OcflTools::Utils.version_int_to_string(version)]['state'][digest] = unique_files # Prove we actually added to state return get_state(version) end # Check to make sure the file isn't already in this state with a different digest! # If so; fail. We don't do implicit / soft adds. You want that, be explict: do an update_file instead. existing_files = get_files(version) if existing_files.key?(file) raise OcflTools::Errors::FileDigestMismatch, "#{file} already exists with different digest in version #{version}. Consider update instead." end # if it's not in State already, just add it. @versions[OcflTools::Utils.version_int_to_string(version)]['state'][digest] = [file] get_state(version) end |
#copy_file(source_file, destination_file, version) ⇒ Hash
Raises an error if source_file does not exist in this version.
Copies a file within the same version. If the destination file already exists with a different digest, it is overwritten with the digest of the source file.
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 317 def copy_file(source_file, destination_file, version) # add new filename to existing digest in current state. # If destination file already exists, overwrite it. existing_files = get_files(version) if existing_files.key?(destination_file) delete_file(destination_file, version) end # should NOT call add_file, as add_file updates the manifest. # Should instead JUST update current state with new filepath. digest = get_digest(source_file, version) # errors out if source_file not found in current state my_state = get_state(version) my_files = my_state[digest] my_files << destination_file unique_files = my_files.uniq # Just in case we're trying to add the same thing multiple times. # Need to actually add this to @versions! @versions[OcflTools::Utils.version_int_to_string(version)]['state'][digest] = unique_files # Prove we actually added to state get_state(version) # self.add_file(destination_file, self.get_digest(source_file, version), version) end |
#create_version_hash ⇒ Hash
internal API
Returns a version hash with the correct keys created, ready for content to be added.
400 401 402 403 404 405 406 407 408 409 410 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 400 def create_version_hash new_version = {} new_version['created'] = '' new_version['message'] = '' new_version['user'] = {} # user is #name, # address. new_version['user']['name'] = '' new_version['user']['address'] = '' new_version['state'] = {} new_version end |
#delete_file(file, version) ⇒ Hash
Given a filepath, deletes that file from the given version. If multiple copies of the same file (as identified by a common digest) exist in the version, only the requested filepath is removed.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 287 def delete_file(file, version) # remove filename, may remove digest if that was last file associated with that digest. my_state = get_state(version) # Creates version & copies state from prior version if doesn't exist. unless version == version_id_list.max raise OcflTools::Errors::CannotEditPreviousVersion, "Can't edit prior versions! Only version #{version_id_list.max} can be modified now." end my_digest = get_digest(file, version) # we know it's here b/c self.get_digest would have crapped out if not. my_array = my_state[my_digest] # Get [Array] of files that have this digest in this version. my_array.delete(file) # Delete the array value that matches file. if !my_array.empty? # update the array with (fewer) items. my_state[my_digest] = my_array else # delete the key. my_state.delete(my_digest) end # put results back into State. set_state(version, my_state) end |
#get_current_files ⇒ Hash
Gets all files for the current (highest) version of the OCFL object. Represents the state of the object at ‘head’, with the logical files that consist of the most recent version and their physical representations on disk, relative to the object’s root directory.
171 172 173 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 171 def get_current_files get_files(OcflTools::Utils.version_string_to_int(@head)) end |
#get_digest(file, version) ⇒ String
Will raise an exception if requested filepath is not in given version.
When given a file path and version, return the associated digest from version state.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 356 def get_digest(file, version) # Make a hash with each individual file as a key, with the appropriate digest as value. inverted = get_state(version).invert my_files = {} inverted.each do |files, digest| files.each do |i_file| my_files[i_file] = digest end end # Now see if the requested file is actually here. unless my_files.key?(file) raise OcflTools::Errors::FileMissingFromVersionState, "Get_digest can't find requested file #{file} in version #{version}." end my_files[file] end |
#get_files(version) ⇒ Hash
Gets a hash of all logical files and their associated physical filepaths with the given version.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 152 def get_files(version) my_state = get_state(version) my_files = {} my_state.each do |digest, filepaths| # filepaths is [Array] filepaths.each do |logical_filepath| # look up this file via digest in @manifest. physical_filepath = @manifest[digest] # physical_filepath is an [Array] of files, but they're all the same so only need 1. my_files[logical_filepath] = physical_filepath[0] end end my_files end |
#get_state(version) ⇒ Hash
Creates new version and copies previous versions’ state block over if requested version does not yet exist.
Gets the state block of a given version, comprising of digest keys and an array of filenames associated with those digests.
135 136 137 138 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 135 def get_state(version) my_version = get_version(version) my_version['state'] end |
#get_version(version) ⇒ Hash
If a (n-1) version exists in the object, and the requested version does not yet exist, this method will copy that version’s state block into the new version.
Gets the existing version hash for the requested version, or else creates and populates a new, empty version hash.
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 378 def get_version(version) unless version > 0 raise OcflTools::Errors::NonCompliantValue, "Requested value '#{version}' for object version does not comply with specification." end if @versions.key?(OcflTools::Utils.version_int_to_string(version)) @versions[OcflTools::Utils.version_int_to_string(version)] else # Otherwise, construct a new Version [Hash] and return that. @versions[OcflTools::Utils.version_int_to_string(version)] = create_version_hash # If version -1 exists, copy prior version state over. if @versions.key?(OcflTools::Utils.version_int_to_string(version - 1)) @versions[OcflTools::Utils.version_int_to_string(version)]['state'] = OcflTools::Utils.deep_copy(@versions[OcflTools::Utils.version_int_to_string(version - 1)]['state']) end @versions[OcflTools::Utils.version_int_to_string(version)] end end |
#get_version_created(version) ⇒ String
will raise an exception if you attempt to query a non-existent version.
returns the created field for a given version.
89 90 91 92 93 94 95 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 89 def get_version_created(version) unless @versions.key?(OcflTools::Utils.version_int_to_string(version)) raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!" end @versions[OcflTools::Utils.version_int_to_string(version)]['created'] end |
#get_version_message(version) ⇒ String
will raise an exception if you attempt to query a non-existent version.
returns the message field for a given version.
65 66 67 68 69 70 71 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 65 def (version) unless @versions.key?(OcflTools::Utils.version_int_to_string(version)) raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!" end @versions[OcflTools::Utils.version_int_to_string(version)]['message'] end |
#get_version_user(version) ⇒ Hash
will raise an exception if you attempt to query a nonexistent version.
Gets the user Hash for a given version. @ param [Integer] version of OCFL object to retrieve user block for.
113 114 115 116 117 118 119 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 113 def get_version_user(version) unless @versions.key?(OcflTools::Utils.version_int_to_string(version)) raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!" end @versions[OcflTools::Utils.version_int_to_string(version)]['user'] end |
#move_file(old_file, new_file, version) ⇒ Hash
This is functionally a #copy_file followed by a #delete_file. Will raise an error if the source file does not exist in this version.
Moves (renames) a file from one location to another within the same version.
345 346 347 348 349 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 345 def move_file(old_file, new_file, version) # re-name; functionally a copy and delete. copy_file(old_file, new_file, version) delete_file(old_file, version) end |
#set_head_from_version(version) ⇒ @head
sets @head in current string format, when given integer.
45 46 47 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 45 def set_head_from_version(version) @head = OcflTools::Utils.version_int_to_string(version) end |
#set_state(version, hash) ⇒ Object
It is prefered to update version state via add/update/delete/copy/move file operations.
Sets the state block for a given version when provided with a hash of digest keys and an array of associated filenames.
144 145 146 147 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 144 def set_state(version, hash) # SAN Check needed here to make sure passed Hash has all expected keys. @versions[OcflTools::Utils.version_int_to_string(version)]['state'] = hash end |
#set_version(version, hash) ⇒ Object
When given a correctly-constructed hash, create a new OCFL version. See #create_version_hash for more context.
415 416 417 418 419 420 421 422 423 424 425 426 427 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 415 def set_version(version, hash) # SAN Check to make sure passed Hash has all expected keys. e216_errors = [] %w[created message user state].each do |key| if hash.key?(key) == false e216_errors << "version #{version} hash block is missing required #{key} key." end end if e216_errors.size > 0 raise OcflTools::Errors::ValidationError, details: { "E216" => e216_errors } end @versions[OcflTools::Utils.version_int_to_string(version)] = hash end |
#set_version_created(version, created) ⇒ Object
will raise an exception if you attempt to query a non-existent version.
sets the created field for a given version.
77 78 79 80 81 82 83 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 77 def set_version_created(version, created) unless @versions.key?(OcflTools::Utils.version_int_to_string(version)) raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!" end @versions[OcflTools::Utils.version_int_to_string(version)]['created'] = created end |
#set_version_message(version, message) ⇒ Object
will raise an exception if you attempt to query a non-existent version.
sets the message field for a given version.
53 54 55 56 57 58 59 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 53 def (version, ) unless @versions.key?(OcflTools::Utils.version_int_to_string(version)) raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!" end @versions[OcflTools::Utils.version_int_to_string(version)]['message'] = end |
#set_version_user(version, user) ⇒ Object
will raise an exception if you attempt to query a nonexistent version.
Sets the user Hash for a given version. Expects a complete User hash (with sub-keys of name & address).
101 102 103 104 105 106 107 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 101 def set_version_user(version, user) unless @versions.key?(OcflTools::Utils.version_int_to_string(version)) raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!" end @versions[OcflTools::Utils.version_int_to_string(version)]['user'] = user end |
#update_file(file, digest, version) ⇒ Object
this method explicitly deletes the prior file if found, and re-creates it with a new digest via the #add_file method.
Updates an existing file with a new bitstream and digest.
222 223 224 225 226 227 228 229 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 222 def update_file(file, digest, version) # Same filename, different digest, update manifest. # Do a Delete, then an Add. existing_files = get_files(version) delete_file(file, version) if existing_files.key?(file) add_file(file, digest, version) end |
#update_fixity(digest, fixityAlgorithm, fixityDigest) ⇒ Hash
Given a digest, fixityAlgo and fixityDigest, add to fixity block.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 255 def update_fixity(digest, fixityAlgorithm, fixityDigest) # Does Digest exist in @manifest? Fail if not. # Doe fixityAlgorithm exist as a key in @fixity? Add if not. unless @manifest.key?(digest) == true raise OcflTools::Errors::RequestedKeyNotFound, "Unable to find digest #{digest} in manifest!" end filepaths = @manifest[digest] # Construct the nested hash, if necessary. @fixity[fixityAlgorithm] = {} if @fixity.key?(fixityAlgorithm) != true if @fixity[fixityAlgorithm].key?(fixityDigest) != true @fixity[fixityAlgorithm][fixityDigest] = [] end # Append the filepath to the appropriate fixityDigest, if it's not already there. filepaths.each do |filepath| if @fixity[fixityAlgorithm][fixityDigest].include?(filepath) next # don't add it if the filepath is already in the array. end @fixity[fixityAlgorithm][fixityDigest] = (@fixity[fixityAlgorithm][fixityDigest] << filepath) end @fixity end |
#update_manifest(file, digest, version) ⇒ Object
internal API.
Add a file and digest to the manifest at the given version.
236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 236 def update_manifest(file, digest, version) # We only ever add to the manifest. physical_filepath = "#{OcflTools::Utils.version_int_to_string(version)}/#{@contentDirectory}/#{file}" if @manifest.key?(digest) # This bitstream is already in the manifest. # We need to append the new filepath to the existing array. @manifest[digest] = (@manifest[digest] << physical_filepath) return @manifest[digest] end @manifest[digest] = [physical_filepath] # otherwise, add our first entry to the array. @manifest[digest] end |
#version_id_list ⇒ Array{Integer}
Gets an array of integers comprising all versions of this OCFL object. It is not guaranteed to be in numeric order.
123 124 125 126 127 128 129 |
# File 'lib/ocfl_tools/ocfl_object.rb', line 123 def version_id_list my_versions = [] @versions.keys.each do |key| my_versions << OcflTools::Utils.version_string_to_int(key) end my_versions end |