moab-versioning 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|