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.
- checksums.yaml +7 -0
- data/lib/moab.rb +59 -0
- data/lib/moab/bagger.rb +289 -0
- data/lib/moab/config.rb +21 -0
- data/lib/moab/exceptions.rb +18 -0
- data/lib/moab/file_group.rb +244 -0
- data/lib/moab/file_group_difference.rb +336 -0
- data/lib/moab/file_group_difference_subset.rb +45 -0
- data/lib/moab/file_instance.rb +82 -0
- data/lib/moab/file_instance_difference.rb +54 -0
- data/lib/moab/file_inventory.rb +279 -0
- data/lib/moab/file_inventory_difference.rb +132 -0
- data/lib/moab/file_manifestation.rb +85 -0
- data/lib/moab/file_signature.rb +200 -0
- data/lib/moab/signature_catalog.rb +195 -0
- data/lib/moab/signature_catalog_entry.rb +61 -0
- data/lib/moab/storage_object.rb +220 -0
- data/lib/moab/storage_object_version.rb +333 -0
- data/lib/moab/storage_repository.rb +57 -0
- data/lib/moab/storage_services.rb +104 -0
- data/lib/moab/verification_result.rb +83 -0
- data/lib/moab/version_metadata.rb +38 -0
- data/lib/moab/version_metadata_entry.rb +64 -0
- data/lib/moab/version_metadata_event.rb +47 -0
- data/lib/moab_stanford.rb +18 -0
- data/lib/monkey_patches.rb +65 -0
- data/lib/serializer.rb +36 -0
- data/lib/serializer/manifest.rb +76 -0
- data/lib/serializer/serializable.rb +178 -0
- data/lib/stanford/active_fedora_object.rb +34 -0
- data/lib/stanford/content_inventory.rb +236 -0
- data/lib/stanford/dor_metadata.rb +49 -0
- data/lib/stanford/storage_repository.rb +46 -0
- data/lib/stanford/storage_services.rb +66 -0
- data/lib/tasks/yard.rake +34 -0
- data/lib/tools/api_doc_generator.rb +396 -0
- data/lib/tools/spec_generator.rb +410 -0
- data/lib/tools/spec_generator_old.rb +49 -0
- 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
|