moab-versioning 1.3.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/lib/moab.rb +59 -0
  3. data/lib/moab/bagger.rb +289 -0
  4. data/lib/moab/config.rb +21 -0
  5. data/lib/moab/exceptions.rb +18 -0
  6. data/lib/moab/file_group.rb +244 -0
  7. data/lib/moab/file_group_difference.rb +336 -0
  8. data/lib/moab/file_group_difference_subset.rb +45 -0
  9. data/lib/moab/file_instance.rb +82 -0
  10. data/lib/moab/file_instance_difference.rb +54 -0
  11. data/lib/moab/file_inventory.rb +279 -0
  12. data/lib/moab/file_inventory_difference.rb +132 -0
  13. data/lib/moab/file_manifestation.rb +85 -0
  14. data/lib/moab/file_signature.rb +200 -0
  15. data/lib/moab/signature_catalog.rb +195 -0
  16. data/lib/moab/signature_catalog_entry.rb +61 -0
  17. data/lib/moab/storage_object.rb +220 -0
  18. data/lib/moab/storage_object_version.rb +333 -0
  19. data/lib/moab/storage_repository.rb +57 -0
  20. data/lib/moab/storage_services.rb +104 -0
  21. data/lib/moab/verification_result.rb +83 -0
  22. data/lib/moab/version_metadata.rb +38 -0
  23. data/lib/moab/version_metadata_entry.rb +64 -0
  24. data/lib/moab/version_metadata_event.rb +47 -0
  25. data/lib/moab_stanford.rb +18 -0
  26. data/lib/monkey_patches.rb +65 -0
  27. data/lib/serializer.rb +36 -0
  28. data/lib/serializer/manifest.rb +76 -0
  29. data/lib/serializer/serializable.rb +178 -0
  30. data/lib/stanford/active_fedora_object.rb +34 -0
  31. data/lib/stanford/content_inventory.rb +236 -0
  32. data/lib/stanford/dor_metadata.rb +49 -0
  33. data/lib/stanford/storage_repository.rb +46 -0
  34. data/lib/stanford/storage_services.rb +66 -0
  35. data/lib/tasks/yard.rake +34 -0
  36. data/lib/tools/api_doc_generator.rb +396 -0
  37. data/lib/tools/spec_generator.rb +410 -0
  38. data/lib/tools/spec_generator_old.rb +49 -0
  39. metadata +252 -0
@@ -0,0 +1,57 @@
1
+ require 'moab'
2
+
3
+ module Moab
4
+
5
+ # A class to represent the SDR repository store
6
+ #
7
+ # ====Data Model
8
+ # * <b>{StorageRepository} = represents a digital object repository storage node</b>
9
+ # * {StorageServices} = supports application layer access to the repository's objects, data, and metadata
10
+ # * {StorageObject} = represents a digital object's repository storage location and ingest/dissemination methods
11
+ # * {StorageObjectVersion} [1..*] = represents a version subdirectory within an object's home directory
12
+ # * {Bagger} [1] = utility for creating bagit packages for ingest or dissemination
13
+ #
14
+ # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
15
+ # All rights reserved. See {file:LICENSE.rdoc} for details.
16
+ class StorageRepository
17
+
18
+ # @return [Pathname] The location of the root directory of the repository storage node
19
+ def repository_home
20
+ Pathname.new(Moab::Config.repository_home)
21
+ end
22
+
23
+ # @param object_id [String] The identifier of the digital object whose version is desired
24
+ # @return [StorageObject] The representation of the desired object storage directory
25
+ def storage_object(object_id, create=false)
26
+ object_pathname = storage_object_pathname(object_id)
27
+ if object_pathname.exist?
28
+ StorageObject.new(object_id, object_pathname)
29
+ elsif create
30
+ object_pathname.mkpath
31
+ StorageObject.new(object_id, object_pathname)
32
+ else
33
+ raise Moab::ObjectNotFoundException, "No object found for #{object_id} at #{object_pathname}"
34
+ end
35
+ end
36
+
37
+ # @param object_id [String] The identifier of the digital object whose version is desired
38
+ # @return [Pathname] The location of the desired object's home directory (default=pairtree)
39
+ def storage_object_pathname(object_id)
40
+ #todo This method should be customized, or overridden in a subclass
41
+ # for a more sophisticated pairtree implementation see https://github.com/microservices/pairtree
42
+ path_segments = object_id.scan(/..?/) << object_id
43
+ object_path = path_segments.join(File::SEPARATOR).gsub(/:/,'_')
44
+ repository_home.join(object_path)
45
+ end
46
+
47
+ # @param druid [String] The object identifier
48
+ # @return [void] transfer the object to the preservation repository
49
+ def store_new_object_version(druid, bag_pathname )
50
+ storage_object = self.storage_object(druid, create=true)
51
+ new_version = storage_object.ingest_bag(bag_pathname)
52
+ new_version
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,104 @@
1
+ require 'moab'
2
+
3
+ module Moab
4
+
5
+ # An interface class to support access to SDR storage via a RESTful server
6
+ #
7
+ # ====Data Model
8
+ # * {StorageRepository} = represents a digital object repository storage node
9
+ # * <b>{StorageServices} = supports application layer access to the repository's objects, data, and metadata</b>
10
+ # * {StorageObject} = represents a digital object's repository storage location and ingest/dissemination methods
11
+ # * {StorageObjectVersion} [1..*] = represents a version subdirectory within an object's home directory
12
+ # * {Bagger} [1] = utility for creating bagit packages for ingest or dissemination
13
+ #
14
+ # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
15
+ # All rights reserved. See {file:LICENSE.rdoc} for details.
16
+ class StorageServices
17
+
18
+ # @return [StorageRepository] an instance of the interface to SDR storage
19
+ @@repository = Moab::StorageRepository.new
20
+
21
+ # @param object_id [String] The digital object identifier of the object
22
+ # @return [String] the location of the storage object
23
+ def self.object_path(object_id)
24
+ @@repository.storage_object(object_id).object_pathname.to_s
25
+ end
26
+
27
+ # @param object_id [String] The digital object identifier of the object
28
+ # @param [Integer] version_id The ID of the version, if nil use latest version
29
+ # @return [String] the location of the storage object version
30
+ def self.object_version_path(object_id,version_id=nil)
31
+ @@repository.storage_object(object_id).find_object_version(version_id).version_pathname.to_s
32
+ end
33
+
34
+ # @param object_id [String] The digital object identifier
35
+ # @return [Integer] The version number of the currently highest version
36
+ def self.current_version(object_id)
37
+ @@repository.storage_object(object_id).current_version_id
38
+ end
39
+
40
+ # @param [String] object_id The digital object identifier of the object
41
+ # @return [Pathname] Pathname object containing the full path for the specified file
42
+ def self.version_metadata(object_id)
43
+ self.retrieve_file('metadata', 'versionMetadata.xml', object_id)
44
+ end
45
+
46
+ # @param [String] object_id The digital object identifier of the object
47
+ # @param [Integer] version_id The ID of the version, if nil use latest version
48
+ # @return [FileInventory] the file inventory for the specified object version
49
+ def self.retrieve_file_group(file_category, object_id, version_id=nil)
50
+ storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
51
+ if file_category =~ /manifest/
52
+ inventory_type = file_category = 'manifests'
53
+ else
54
+ inventory_type = 'version'
55
+ end
56
+ inventory = storage_object_version.file_inventory(inventory_type)
57
+ inventory.group(file_category)
58
+ end
59
+
60
+ # @param [String] file_category The category of file ('content', 'metdata', or 'manifest')
61
+ # @param [String] file_id The name of the file (path relative to base directory)
62
+ # @param [String] object_id The digital object identifier of the object
63
+ # @param [Integer] version_id The ID of the version, if nil use latest version
64
+ # @return [Pathname] Pathname object containing the full path for the specified file
65
+ def self.retrieve_file(file_category, file_id, object_id, version_id=nil)
66
+ storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
67
+ file_pathname = storage_object_version.find_filepath(file_category, file_id)
68
+ end
69
+
70
+ # @param [String] file_category The category of file ('content', 'metdata', or 'manifest')
71
+ # @param [FileSignature] file_signature The signature of the file
72
+ # @param [String] object_id The digital object identifier of the object
73
+ # @param [Integer] version_id The ID of the version, if nil use latest version
74
+ # @return [Pathname] Pathname object containing the full path for the specified file
75
+ def self.retrieve_file_using_signature(file_category, file_signature, object_id, version_id=nil)
76
+ storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
77
+ file_pathname = storage_object_version.find_filepath_using_signature(file_category, file_signature)
78
+ end
79
+
80
+ # @param [String] file_category The category of file ('content', 'metdata', or 'manifest')
81
+ # @param [String] file_id The name of the file (path relative to base directory)
82
+ # @param [String] object_id The digital object identifier of the object
83
+ # @param [Integer] version_id The ID of the version, if nil use latest version
84
+ # @return [FileSignature] The signature of the file
85
+ def self.retrieve_file_signature(file_category, file_id, object_id, version_id=nil)
86
+ storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
87
+ file_pathname = storage_object_version.find_signature(file_category, file_id)
88
+ end
89
+
90
+ # @param [String] object_id The digital object identifier of the object
91
+ # @param [Object] base_version_id The identifier of the base version to be compared
92
+ # @param [Object] compare_version_id The identifier of the version to be compared to the base version
93
+ # @return [FileInventoryDifference] The report of the version differences
94
+ def self.version_differences(object_id, base_version_id,compare_version_id)
95
+ base_version = @@repository.storage_object(object_id).storage_object_version(base_version_id)
96
+ compare_version = @@repository.storage_object(object_id).storage_object_version(compare_version_id)
97
+ base_inventory=base_version.file_inventory('version')
98
+ compare_inventory=compare_version.file_inventory('version')
99
+ FileInventoryDifference.new.compare(base_inventory,compare_inventory)
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,83 @@
1
+ require 'moab'
2
+
3
+ module Moab
4
+
5
+ class VerificationResult
6
+
7
+ # @return [String] The name of the entity that was verified
8
+ attr_accessor :entity
9
+
10
+ # @return [Boolean] The true/false outcome of the verification
11
+ attr_accessor :verified
12
+
13
+ # @return [Hash] The details of the comparisons that were made
14
+ attr_accessor :details
15
+
16
+ # @return [Array<VerificationResult>] The subentities, if any, on which this verification is based
17
+ attr_accessor :subentities
18
+
19
+ # @param entity [Object] The name of the entity being verified
20
+ def initialize(entity)
21
+ @entity = entity
22
+ @verified = false
23
+ @details = nil
24
+ @subentities = Array.new
25
+ end
26
+
27
+ # @param entity [Symbol] The name of the entity being verified
28
+ # @param expected [Object] The expected value
29
+ # @param found [Object] The found value
30
+ # @return [VerificationResult] The result of comparing the expected and found values
31
+ def self.verify_value(entity, expected, found)
32
+ result = VerificationResult.new(entity.to_s)
33
+ result.verified = (expected == found)
34
+ result.details = {'expected' => expected, 'found' => found}
35
+ result
36
+ end
37
+
38
+ # @param entity [Symbol] The name of the entity being verified
39
+ # @param expression [Object] The expression that will be evaluated as true or false
40
+ # @param details [Object] optional details that could be reported
41
+ # @return [VerificationResult] The result of evaluating the expression
42
+ def self.verify_truth(entity,expression,details=nil)
43
+ result = VerificationResult.new(entity.to_s)
44
+ result.verified = !(expression.nil? or (expression == false))
45
+ result.details = details
46
+ result
47
+ end
48
+
49
+ # @param verbose [Boolean] If true, always provide all details of the verification
50
+ # @return [String] The verification result serialized to JSON
51
+ def to_json(verbose=false)
52
+ JSON.pretty_generate(to_hash(verbose))
53
+ end
54
+
55
+ # @param verbose [Boolean] If true, always provide all details of the verification
56
+ # @param level [Integer] Used to test the depth of recursion
57
+ # @return [OrderedHash] The verification result serialized to a hash
58
+ def to_hash(verbose=false,level=0)
59
+ hash = OrderedHash.new
60
+ hash['verified'] = @verified
61
+ if (verbose or @verified == false)
62
+ hash['details'] = @details ? @details : subentities_to_hash(verbose,level)
63
+ end
64
+ if level > 0
65
+ hash
66
+ else
67
+ {@entity => hash}
68
+ end
69
+ end
70
+
71
+ # @param verbose [Boolean] If true, always provide all details of the verification
72
+ # @param level [Integer] Used to increment the depth of recursion
73
+ # @return [OrderedHash] The verification result of subentities serialized to a hash
74
+ def subentities_to_hash(verbose,level)
75
+ hash = OrderedHash.new
76
+ @subentities.each do |s|
77
+ hash[s.entity] = s.to_hash(verbose, level+1)
78
+ end
79
+ hash
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,38 @@
1
+ require 'moab'
2
+
3
+ module Moab
4
+
5
+ # The descriptive information about a digital object's collection of versions
6
+ #
7
+ # ====Data Model
8
+ # * <b>{VersionMetadata} = descriptive information about a digital object's versions</b>
9
+ # * {VersionMetadataEntry} [1..*] = attributes of a digital object version
10
+ # * {VersionMetadataEvent} [1..*] = object version lifecycle events with timestamps
11
+ #
12
+ # @example {include:file:spec/fixtures/data/jq937jp0017/v3/metadata/versionMetadata.xml}
13
+ # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
14
+ # All rights reserved. See {file:LICENSE.rdoc} for details.
15
+ class VersionMetadata < Manifest
16
+
17
+ include HappyMapper
18
+
19
+ # The name of the XML element used to serialize this objects data
20
+ tag 'versionMetadata'
21
+
22
+ # (see Serializable#initialize)
23
+ def initialize(opts={})
24
+ @versions = Array.new
25
+ super(opts)
26
+ end
27
+
28
+ # @attribute
29
+ # @return [String] The digital object identifier
30
+ attribute :digital_object_id, String, :tag => 'objectId'
31
+
32
+ # @attribute
33
+ # @return [Array<VersionMetadataEntry>] An array of version metadata entries, one per version
34
+ has_many :versions, VersionMetadataEntry, :tag => 'version'
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,64 @@
1
+ require 'moab'
2
+
3
+ module Moab
4
+
5
+ # The descriptive attributes of a digital object version.
6
+ #
7
+ # ====Data Model
8
+ # * {VersionMetadata} = descriptive information about a digital object's versions
9
+ # * <b>{VersionMetadataEntry} [1..*] = attributes of a digital object version</b>
10
+ # * {VersionMetadataEvent} [1..*] = object version lifecycle events with timestamps
11
+ #
12
+ # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
13
+ # All rights reserved. See {file:LICENSE.rdoc} for details.
14
+ class VersionMetadataEntry < Serializable
15
+
16
+ include HappyMapper
17
+
18
+ # The name of the XML element used to serialize this objects data
19
+ tag 'version'
20
+
21
+ # (see Serializable#initialize)
22
+ def initialize(opts={})
23
+ @events = Array.new
24
+ super(opts)
25
+ end
26
+
27
+ # @attribute
28
+ # @return [Integer] The object version number (A sequential integer)
29
+ attribute :version_id, Integer, :tag => 'versionId', :key => true, :on_save => Proc.new {|n| n.to_s}
30
+
31
+ # @attribute
32
+ # @return [String] "an external version label that increments the most significant digit for major revisions,
33
+ # second digit for minor revisions, and third for admin? e.g., 1.0, 1.1, 2.0, 2.0.1 etc.
34
+ # This should be dynamically derivable across the set of versions
35
+ attribute :label, String
36
+
37
+ # @attribute
38
+ # @return [String] "major|minor|admin"
39
+ attribute :significance, String
40
+
41
+ # @attribute
42
+ # @return [String] A free text external description of why the version was created
43
+ element :description, String
44
+
45
+ # @attribute
46
+ # @return [String] An internal annotation summarizing the changes (optional)
47
+ element :note, String
48
+
49
+ # @attribute
50
+ # @return [FileGroupDifference] Summary of content file differences since previous version
51
+ element :content_changes, FileGroupDifference, :tag => 'fileGroupDifference'
52
+
53
+ # @attribute
54
+ # @return [FileGroupDifference] Summary of metadata file differences since previous version
55
+ element :metadata_changes, FileGroupDifference, :tag => 'fileGroupDifference'
56
+
57
+ # @attribute
58
+ # @return [Array<VersionMetadataEvent>] Array of events with timestamps that track lifecycle stages
59
+ has_many :events, VersionMetadataEvent, :tag => 'event'
60
+
61
+ end
62
+
63
+ end
64
+
@@ -0,0 +1,47 @@
1
+ require 'moab'
2
+
3
+ module Moab
4
+
5
+ # A container element to record object version lifecycle events with timestamps
6
+ #
7
+ # ====Data Model
8
+ # * {VersionMetadata} = descriptive information about a digital object's versions
9
+ # * {VersionMetadataEntry} [1..*] = attributes of a digital object version
10
+ # * <b>{VersionMetadataEvent} [1..*] = object version lifecycle events with timestamps</b>
11
+ #
12
+ # @see VersionMetadata
13
+ # @see VersionMetadataEntry
14
+ # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
15
+ # All rights reserved. See {file:LICENSE.rdoc} for details.
16
+ class VersionMetadataEvent < Serializable
17
+
18
+ include HappyMapper
19
+
20
+ # The name of the XML element used to serialize this objects data
21
+ tag 'event'
22
+
23
+ # (see Serializable#initialize)
24
+ def initialize(opts={})
25
+ super(opts)
26
+ end
27
+
28
+ # @attribute
29
+ # @return [String] The type of event
30
+ attribute :type, String
31
+
32
+
33
+ # @attribute
34
+ # @return [Time] The date and time of an event
35
+ attribute :datetime, Time, :on_save => Proc.new {|t| t.to_s}
36
+
37
+ def datetime=(event_datetime)
38
+ @datetime=Time.input(event_datetime)
39
+ end
40
+
41
+ def datetime
42
+ Time.output(@datetime)
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,18 @@
1
+ require 'moab'
2
+ include Moab
3
+ require 'stanford/content_inventory'
4
+ require 'stanford/dor_metadata'
5
+ require 'stanford/storage_repository'
6
+ require 'stanford/storage_services'
7
+ require 'stanford/active_fedora_object'
8
+
9
+ # Stanford is a module that isolates classes specific to the Stanford Digital Repository
10
+ #
11
+ # ====Data Model
12
+ # * <b>{DorMetadata} = utility methods for interfacing with Stanford metadata files (esp contentMetadata)</b>
13
+ # * {ActiveFedoraObject} [1..*] = utility for extracting content or other information from a Fedora Instance
14
+ #
15
+ # @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
16
+ # All rights reserved. See {file:LICENSE.rdoc} for details.
17
+ module Stanford
18
+ end
@@ -0,0 +1,65 @@
1
+ require 'time'
2
+
3
+ # monkey-patch to fix JSON_pure
4
+ # @see http://prettystatemachine.blogspot.com/2010/09/typeerrors-in-tojson-make-me-briefly.html
5
+ #noinspection RubyUnusedLocalVariable
6
+ class Fixnum
7
+ # @return [String] The string value of the number
8
+ def to_json(options = nil)
9
+ to_s
10
+ end
11
+ end
12
+
13
+ # Make the default output from a Time variable be formatted in ISO 8601 format
14
+ class Time
15
+ # @return [String] The datetime in ISO 8601 format
16
+ def to_s
17
+ self.utc.iso8601
18
+ end
19
+
20
+ # @param datetime [Time,String,Nil] The input datetime
21
+ # @return [void] Store the input datetime as a Time object
22
+ def self.input(datetime)
23
+ case datetime
24
+ when nil
25
+ nil
26
+ when String
27
+ Time.parse(datetime)
28
+ when Time
29
+ datetime
30
+ else
31
+ raise "unknown time format #{datetime.inspect}"
32
+ end
33
+ end
34
+
35
+ # @param datetime [Time,String,Nil] The datetime value to output
36
+ # @return [String] Format the datetime into a ISO 8601 formatted string
37
+ def self.output(datetime)
38
+ case datetime
39
+ when nil
40
+ nil
41
+ when String
42
+ Time.parse(datetime).utc.iso8601
43
+ when Time
44
+ datetime.utc.iso8601
45
+ else
46
+ raise "unknown time format #{datetime.inspect}"
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ # Add methods to Array so that one attribute of a class can be treated as a unique key
53
+ class Array
54
+ # @return [Array<Object>] An array containing the key fields from a collection of objects
55
+ def keys
56
+ self.collect { |e| e.key }
57
+ end
58
+
59
+ # @param key [Object] The key value to search for
60
+ # @return [Object] The object from the array that has the specified key value
61
+ def keyfind(key)
62
+ self.each { |e| return e if key == e.key }
63
+ nil
64
+ end
65
+ end