caruby-tissue 1.2.1
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.
- data/History.txt +4 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +44 -0
- data/bin/crtdump +31 -0
- data/bin/crtexample +18 -0
- data/bin/crtextract +47 -0
- data/bin/crtmigrate +17 -0
- data/bin/crtsmoke +27 -0
- data/examples/galena/README.md +53 -0
- data/examples/galena/bin/migrate.rb +42 -0
- data/examples/galena/bin/seed.rb +43 -0
- data/examples/galena/conf/extract/simple_fields.yaml +4 -0
- data/examples/galena/conf/migration/filter_fields.yaml +7 -0
- data/examples/galena/conf/migration/filter_migration.yaml +9 -0
- data/examples/galena/conf/migration/frozen_fields.yaml +11 -0
- data/examples/galena/conf/migration/frozen_migration.yaml +9 -0
- data/examples/galena/conf/migration/general_fields.yaml +42 -0
- data/examples/galena/conf/migration/general_migration.yaml +9 -0
- data/examples/galena/conf/migration/simple_fields.yaml +30 -0
- data/examples/galena/conf/migration/simple_migration.yaml +7 -0
- data/examples/galena/conf/migration/small_fields.yaml +24 -0
- data/examples/galena/conf/migration/small_migration.yaml +9 -0
- data/examples/galena/data/filter.csv +1 -0
- data/examples/galena/data/frozen.csv +1 -0
- data/examples/galena/data/general.csv +1 -0
- data/examples/galena/data/minimal.csv +1 -0
- data/examples/galena/data/simple.csv +1 -0
- data/examples/galena/data/small.csv +1 -0
- data/examples/galena/doc/CaTissue.html +93 -0
- data/examples/galena/doc/CaTissue/CollectionProtocolRegistration.html +181 -0
- data/examples/galena/doc/CaTissue/Participant.html +241 -0
- data/examples/galena/doc/CaTissue/SpecimenCollectionGroup.html +190 -0
- data/examples/galena/doc/CaTissue/StorageContainer.html +179 -0
- data/examples/galena/doc/CaTissue/TissueSpecimen.html +320 -0
- data/examples/galena/doc/Galena.html +290 -0
- data/examples/galena/doc/Galena/Seed.html +203 -0
- data/examples/galena/doc/Galena/Seed/Defaults.html +646 -0
- data/examples/galena/doc/_index.html +188 -0
- data/examples/galena/doc/class_list.html +36 -0
- data/examples/galena/doc/css/common.css +1 -0
- data/examples/galena/doc/css/full_list.css +53 -0
- data/examples/galena/doc/css/style.css +307 -0
- data/examples/galena/doc/file.README.html +108 -0
- data/examples/galena/doc/file_list.html +38 -0
- data/examples/galena/doc/frames.html +13 -0
- data/examples/galena/doc/index.html +108 -0
- data/examples/galena/doc/js/app.js +202 -0
- data/examples/galena/doc/js/full_list.js +149 -0
- data/examples/galena/doc/js/jquery.js +154 -0
- data/examples/galena/doc/method_list.html +179 -0
- data/examples/galena/doc/top-level-namespace.html +112 -0
- data/examples/galena/lib/README.html +33 -0
- data/examples/galena/lib/galena.rb +8 -0
- data/examples/galena/lib/galena/cli/seed.rb +43 -0
- data/examples/galena/lib/galena/migration/filter_shims.rb +43 -0
- data/examples/galena/lib/galena/migration/frozen_shims.rb +54 -0
- data/examples/galena/lib/galena/seed/defaults.rb +97 -0
- data/lib/catissue.rb +26 -0
- data/lib/catissue/cli/command.rb +51 -0
- data/lib/catissue/cli/example.rb +31 -0
- data/lib/catissue/cli/migrate.rb +60 -0
- data/lib/catissue/cli/smoke.rb +45 -0
- data/lib/catissue/database.rb +451 -0
- data/lib/catissue/database/annotation/annotatable_service.rb +25 -0
- data/lib/catissue/database/annotation/annotation_service.rb +79 -0
- data/lib/catissue/database/annotation/annotator.rb +84 -0
- data/lib/catissue/database/annotation/entity_manager.rb +10 -0
- data/lib/catissue/database/annotation/integration_service.rb +87 -0
- data/lib/catissue/database/controlled_value_finder.rb +43 -0
- data/lib/catissue/database/controlled_values.rb +162 -0
- data/lib/catissue/domain/abstract_domain_object.rb +8 -0
- data/lib/catissue/domain/abstract_position.rb +22 -0
- data/lib/catissue/domain/abstract_specimen.rb +288 -0
- data/lib/catissue/domain/abstract_specimen_collection_group.rb +25 -0
- data/lib/catissue/domain/address.rb +13 -0
- data/lib/catissue/domain/cancer_research_group.rb +11 -0
- data/lib/catissue/domain/capacity.rb +34 -0
- data/lib/catissue/domain/check_in_check_out_event_parameter.rb +19 -0
- data/lib/catissue/domain/collection_event_parameters.rb +13 -0
- data/lib/catissue/domain/collection_protocol.rb +177 -0
- data/lib/catissue/domain/collection_protocol_event.rb +108 -0
- data/lib/catissue/domain/collection_protocol_registration.rb +108 -0
- data/lib/catissue/domain/consent_tier_response.rb +13 -0
- data/lib/catissue/domain/consent_tier_status.rb +29 -0
- data/lib/catissue/domain/container.rb +234 -0
- data/lib/catissue/domain/container_position.rb +21 -0
- data/lib/catissue/domain/container_type.rb +131 -0
- data/lib/catissue/domain/department.rb +13 -0
- data/lib/catissue/domain/disposal_event_parameters.rb +13 -0
- data/lib/catissue/domain/embedded_event_parameters.rb +10 -0
- data/lib/catissue/domain/external_identifier.rb +22 -0
- data/lib/catissue/domain/frozen_event_parameters.rb +10 -0
- data/lib/catissue/domain/institution.rb +13 -0
- data/lib/catissue/domain/new_specimen_array_order_item.rb +35 -0
- data/lib/catissue/domain/order_details.rb +25 -0
- data/lib/catissue/domain/participant.rb +138 -0
- data/lib/catissue/domain/participant_medical_identifier.rb +38 -0
- data/lib/catissue/domain/password.rb +11 -0
- data/lib/catissue/domain/race.rb +11 -0
- data/lib/catissue/domain/received_event_parameters.rb +25 -0
- data/lib/catissue/domain/scg_event_parameters.rb +11 -0
- data/lib/catissue/domain/site.rb +30 -0
- data/lib/catissue/domain/specimen.rb +456 -0
- data/lib/catissue/domain/specimen_array.rb +47 -0
- data/lib/catissue/domain/specimen_array_content.rb +19 -0
- data/lib/catissue/domain/specimen_array_type.rb +20 -0
- data/lib/catissue/domain/specimen_characteristics.rb +20 -0
- data/lib/catissue/domain/specimen_collection_group.rb +412 -0
- data/lib/catissue/domain/specimen_event_parameters.rb +111 -0
- data/lib/catissue/domain/specimen_position.rb +38 -0
- data/lib/catissue/domain/specimen_protocol.rb +34 -0
- data/lib/catissue/domain/specimen_requirement.rb +143 -0
- data/lib/catissue/domain/storage_container.rb +204 -0
- data/lib/catissue/domain/storage_type.rb +82 -0
- data/lib/catissue/domain/transfer_event_parameters.rb +53 -0
- data/lib/catissue/domain/user.rb +100 -0
- data/lib/catissue/extract/command.rb +31 -0
- data/lib/catissue/extract/delta.rb +62 -0
- data/lib/catissue/extract/extractor.rb +99 -0
- data/lib/catissue/migration/migrator.rb +101 -0
- data/lib/catissue/migration/shims.rb +108 -0
- data/lib/catissue/migration/uniquify.rb +111 -0
- data/lib/catissue/resource.rb +84 -0
- data/lib/catissue/util/controlled_value.rb +29 -0
- data/lib/catissue/util/location.rb +116 -0
- data/lib/catissue/util/log.rb +30 -0
- data/lib/catissue/util/person.rb +31 -0
- data/lib/catissue/util/position.rb +54 -0
- data/lib/catissue/util/storable.rb +34 -0
- data/lib/catissue/util/storage_type_holder.rb +30 -0
- data/lib/catissue/version.rb +7 -0
- metadata +212 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'caruby/migration/migrator'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
shims SpecimenCollectionGroup, CollectionProtocolRegistration, SpecimenCharacteristics,
|
5
|
+
SpecimenEventParameters, CollectionEventParameters, ReceivedEventParameters
|
6
|
+
|
7
|
+
class SpecimenCollectionGroup
|
8
|
+
@@diagnosis_cv_finder = nil
|
9
|
+
|
10
|
+
# Sets this SpecimenCollectionGroup diagnosis ControlledValueFinder.
|
11
|
+
def self.diagnosis_cv_finder=(finder)
|
12
|
+
@@diagnosis_cv_finder = finder
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the diagnosis controlled value as follows:
|
16
|
+
# * If CV lookup is disabled, then this method returns value.
|
17
|
+
# * Otherwise, if the value is remapped via a configuration remap file,
|
18
|
+
# then this method returns the remapped CV.
|
19
|
+
# * Otherwise, if the value is a valid CV, then this method returns value.
|
20
|
+
# * Otherwise, this method returns nil.
|
21
|
+
def self.diagnosis_controlled_value(value)
|
22
|
+
@@diagnosis_cv_finder.nil? ? value : @@diagnosis_cv_finder.controlled_value(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the {#diagnosis_controlled_value}.
|
26
|
+
def migrate_clinical_diagnosis(value, row)
|
27
|
+
SpecimenCollectionGroup.diagnosis_controlled_value(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class SpecimenCharacteristics
|
32
|
+
@@tissue_site_cv_finder = nil
|
33
|
+
|
34
|
+
# Sets this SpecimenCharacteristics tissue site ControlledValueFinder.
|
35
|
+
def self.tissue_site_cv_finder=(finder)
|
36
|
+
@@tissue_site_cv_finder = finder
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the tissue site controlled value as follows:
|
40
|
+
# * If CV lookup is disabled, then this method returns value.
|
41
|
+
# * Otherwise, if the value is remapped via a configuration remap file,
|
42
|
+
# then this method returns the remapped CV.
|
43
|
+
# * Otherwise, if the value is a valid CV, then this method returns value.
|
44
|
+
# * Otherwise, this method returns nil.
|
45
|
+
#
|
46
|
+
# @return the caTissue tissue site permissible value
|
47
|
+
def self.tissue_site_controlled_value(value)
|
48
|
+
@@tissue_site_cv_finder.nil? ? value : @@tissue_site_cv_finder.controlled_value(value)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the {#tissue_site_controlled_value}.
|
52
|
+
def migrate_tissue_site(value, row)
|
53
|
+
standard_cv_tissue_site(value) or variant_cv_tissue_site(value)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Returns the {#tissue_site_controlled_value}.
|
59
|
+
#
|
60
|
+
# @return the caTissue tissue site permissible value
|
61
|
+
def standard_cv_tissue_site(value)
|
62
|
+
SpecimenCharacteristics.tissue_site_controlled_value(value)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the {#tissue_site_controlled_value} which adds the 'NOS' suffix to a value
|
66
|
+
# without one or removes 'NOS' from a value with the suffix.
|
67
|
+
#
|
68
|
+
# @return the caTissue tissue site permissible value
|
69
|
+
def variant_cv_tissue_site(value)
|
70
|
+
# try an NOS suffix variation
|
71
|
+
variation = value =~ /, NOS$/ ? value[0...-', NOS'.length] : value + ', NOS'
|
72
|
+
cv = SpecimenCharacteristics.tissue_site_controlled_value(variation)
|
73
|
+
logger.warn("Migrator substituted tissue site #{cv} for #{value}.") if cv
|
74
|
+
cv
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class SpecimenEventParameters
|
79
|
+
# Returns nil by default, since only SCGEventParameters have a SCG owner.
|
80
|
+
# {SCGEventParameters#migrate_specimen_collection_group} overrides this method.
|
81
|
+
#
|
82
|
+
# @return nil
|
83
|
+
def migrate_specimen_collection_group(scg, row)
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module SCGEventParameters
|
89
|
+
#@param [SpecimenCollectionGroup] scg the migrated owner SCG
|
90
|
+
# @return [SpecimenCollectionGroup] scg
|
91
|
+
# @see SpecimenEventParameters#migrate_specimen_collection_group
|
92
|
+
def migrate_specimen_collection_group(scg, row)
|
93
|
+
# unset the specimen parent if necessary
|
94
|
+
self.specimen = nil if specimen and scg
|
95
|
+
scg
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the given Specimen spc unless this SCGEventParameters already has a SCG owner.
|
99
|
+
# A SCGEventParameters is preferentially set to a migrated SCG rather than a migrated
|
100
|
+
# Specimen.
|
101
|
+
#
|
102
|
+
#@param [Specimen] spc the migrated owner specimen
|
103
|
+
# @return [Specimen, nil] spc unless there is already a SCG owner
|
104
|
+
def migrate_specimen(spc, row)
|
105
|
+
spc unless specimen_collection_group
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'caruby/migration/uniquify'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
shims CollectionProtocol, Site, User, Container, ExternalIdentifier, ParticipantMedicalIdentifier,
|
5
|
+
CollectionProtocolRegistration, SpecimenCollectionGroup, Specimen
|
6
|
+
|
7
|
+
class CollectionProtocol
|
8
|
+
include CaRuby::Migratable::Unique
|
9
|
+
|
10
|
+
# Makes this CP's title unique.
|
11
|
+
def uniquify
|
12
|
+
self.title = self.short_title = uniquify_value(short_title)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Site
|
17
|
+
include CaRuby::Migratable::Unique
|
18
|
+
|
19
|
+
# Makes this Site's name unique.
|
20
|
+
def uniquify
|
21
|
+
self.name = uniquify_value(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class CollectionProtocolRegistration
|
26
|
+
include CaRuby::Migratable::Unique
|
27
|
+
|
28
|
+
# Makes this CPR's PPI unique.
|
29
|
+
def uniquify
|
30
|
+
self.protocol_participant_identifier = uniquify_value(protocol_participant_identifier)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Container
|
35
|
+
include CaRuby::Migratable::Unique
|
36
|
+
|
37
|
+
# Makes this Container's name unique.
|
38
|
+
def uniquify
|
39
|
+
self.name = uniquify_value(name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ContainerType
|
44
|
+
include CaRuby::Migratable::Unique
|
45
|
+
|
46
|
+
# Makes this ContainerType's name unique.
|
47
|
+
def uniquify
|
48
|
+
self.name = uniquify_value(name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class StorageType
|
53
|
+
include CaRuby::Migratable::Unique
|
54
|
+
|
55
|
+
# Makes this StorageType and the ContainerTypes which it holds unique.
|
56
|
+
def uniquify
|
57
|
+
super
|
58
|
+
child_types.each { |ct| ct.uniquify if ContainerType === ct }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class SpecimenCollectionGroup
|
63
|
+
include CaRuby::Migratable::Unique
|
64
|
+
|
65
|
+
# Makes this SCG's SPN unique.
|
66
|
+
def uniquify
|
67
|
+
self.surgical_pathology_number = uniquify_value(surgical_pathology_number)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Specimen
|
72
|
+
include CaRuby::Migratable::Unique
|
73
|
+
|
74
|
+
# Makes this Specimen's label unique.
|
75
|
+
def uniquify
|
76
|
+
self.label = uniquify_value(label.uniquify) if label
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ExternalIdentifier
|
81
|
+
include CaRuby::Migratable::Unique
|
82
|
+
|
83
|
+
# Makes this ExternalIdentifier's value unique.
|
84
|
+
def uniquify
|
85
|
+
self.value = uniquify_value(value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class ParticipantMedicalIdentifier
|
90
|
+
include CaRuby::Migratable::Unique
|
91
|
+
|
92
|
+
# Makes this PMI's MRN unique.
|
93
|
+
def uniquify
|
94
|
+
self.medical_record_number = uniquify_value(medical_record_number)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class User
|
99
|
+
include CaRuby::Migratable::Unique
|
100
|
+
|
101
|
+
# Makes this User's login id and email address unique.
|
102
|
+
# The result is in the form _name___suffix_+@test.com+
|
103
|
+
# where:
|
104
|
+
# * _name_ is the name prefix portion of the original email address
|
105
|
+
# * _suffix_ is a unique number
|
106
|
+
def uniquify
|
107
|
+
email = email_address ||= self.login_name || return
|
108
|
+
self.login_name = self.email_address = uniquify_value(email[/[^@]+/]) + '@test.com'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'caruby/resource'
|
2
|
+
require 'caruby/domain/resource_module'
|
3
|
+
|
4
|
+
module CaTissue
|
5
|
+
extend CaRuby::ResourceModule
|
6
|
+
|
7
|
+
# The module included by all CaTissue domain classes.
|
8
|
+
module Resource
|
9
|
+
include CaRuby::Resource
|
10
|
+
|
11
|
+
# @param [Resource] other the domain object to compare
|
12
|
+
# @param [<Symbol>] attributes the attributes to compare
|
13
|
+
# @return [Boolean} whether each of the given attribute values either equals the
|
14
|
+
# respective other attribute value or one of the values is nil or 'Not Specified'
|
15
|
+
def tolerant_match?(other, attributes)
|
16
|
+
attributes.all? { |attr| Resource.tolerant_value_match?(send(attr), other.send(attr)) }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the CaTissue::Database which stores this object.
|
20
|
+
def database
|
21
|
+
@@database ||= Database.instance
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# Returns the required attributes which are nil for this domain object.
|
27
|
+
# Overrides the CaRuby::Resource method to handle the following bug:
|
28
|
+
#
|
29
|
+
# caTissue alert - Bug #67: AbstractSpecimen.setActivityStatus
|
30
|
+
# is a no-op. The Specimen activityStatus property is incorrectly pulled
|
31
|
+
# up to AbstractSpecimen. AbstractSpecimen.activityStatus is marked as
|
32
|
+
# mandatory, since it is required for Specimen. However, it is not
|
33
|
+
# mandatory, and in fact can't be set, for SpecimenRequirement.
|
34
|
+
# Work-around is to add special code to exclude activityStatus from
|
35
|
+
# the caRuby SpecimenRequirement missing mandatory attributes validation
|
36
|
+
# check.
|
37
|
+
def missing_mandatory_attributes
|
38
|
+
invalid = super
|
39
|
+
# Special case: AbstractSpecimen.setActivityStatus is a no-op.
|
40
|
+
if invalid.include?(:activity_status) and CaTissue::SpecimenRequirement === self then
|
41
|
+
invalid.delete(:activity_status)
|
42
|
+
end
|
43
|
+
invalid
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# The unspecified value.
|
49
|
+
UNSPECIFIED = 'Not Specified'
|
50
|
+
|
51
|
+
# @return whether the given value equals the other value or one of the values is nil or 'Not Specified'
|
52
|
+
def self.tolerant_value_match?(value, other)
|
53
|
+
value == other or unpsecified_value?(value) or unpsecified_value?(other)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return whether the given value equals nil or {Resource.UNSPECIFIED}
|
57
|
+
def self.unpsecified_value?(value)
|
58
|
+
value.nil? or value == UNSPECIFIED
|
59
|
+
end
|
60
|
+
|
61
|
+
# Adds the given domain class to the CaTissue CaRuby::ResourceModule.
|
62
|
+
def self.included(klass)
|
63
|
+
CaTissue.add_class(klass)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# The required include mix-in module.
|
68
|
+
@mixin = Resource
|
69
|
+
|
70
|
+
# The required Java package name.
|
71
|
+
@java_package = 'edu.wustl.catissuecore.domain'
|
72
|
+
|
73
|
+
# Load the domain class definitions.
|
74
|
+
dir = File.join(File.dirname(__FILE__), 'domain')
|
75
|
+
load_dir(dir)
|
76
|
+
end
|
77
|
+
|
78
|
+
module JavaLogger
|
79
|
+
# caTissue alert - the caTissue logger must be initialized before caTissue objects are created.
|
80
|
+
# The logger at issue is the caTissue client logger, not the caTissue server logger nor
|
81
|
+
# the caRuby logger. The caTissue logger facade class is edu.wustl.common.util.logger.Logger,
|
82
|
+
# which is wrapped in Ruby as EduWustlCommonUtilLogger::Logger. TODO - isolate and report.
|
83
|
+
Java::EduWustlCommonUtilLogger::Logger.configure("")
|
84
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'caruby/util/controlled_value'
|
2
|
+
|
3
|
+
module CaTissue
|
4
|
+
class ControlledValue < CaRuby::ControlledValue
|
5
|
+
|
6
|
+
PUBLIC_ID_ALIAS_MAP = {:tissue_site => 'Tissue_Site_PID', :clinical_diagnosis => 'Clinical_Diagnosis_PID'}
|
7
|
+
|
8
|
+
# Returns the standard public id string for the given public_id_or_alias.
|
9
|
+
def self.standard_public_id(public_id_or_alias)
|
10
|
+
PUBLIC_ID_ALIAS_MAP[public_id_or_alias.to_sym] or public_id_or_alias.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :identifier, :public_id
|
14
|
+
|
15
|
+
attr_reader :identifier
|
16
|
+
|
17
|
+
def parent_identifier
|
18
|
+
parent.identifier if parent
|
19
|
+
end
|
20
|
+
|
21
|
+
def public_id=(public_id_or_alias)
|
22
|
+
@public_id = self.class.standard_public_id(public_id_or_alias)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"#{value}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'caruby/util/coordinate'
|
2
|
+
require 'caruby/util/validation'
|
3
|
+
|
4
|
+
module CaTissue
|
5
|
+
# A Location is a non-Resource utility class which represents a Container row and column.
|
6
|
+
#
|
7
|
+
# Location does not capture the occupant; therefore, changing a location coordinate value alone does not
|
8
|
+
# change the storage assignment of an occupant.
|
9
|
+
class Location
|
10
|
+
include Comparable
|
11
|
+
|
12
|
+
attr_accessor :container, :coordinate
|
13
|
+
|
14
|
+
define_attribute_alias(:holder, :container)
|
15
|
+
|
16
|
+
# @param [{Symbol => Object}] params the location fields
|
17
|
+
# @option [Integer] params :in the container holding this location
|
18
|
+
# @option [Coordinate, (Integer, Integer)] params :at the location coordinate, expressed as either
|
19
|
+
# a Coordinate or a (column, row) array
|
20
|
+
# @return the new Location
|
21
|
+
def initialize(params=nil)
|
22
|
+
Options.validate(params, INIT_OPTS)
|
23
|
+
@container = Options.get(:in, params)
|
24
|
+
coord = Options.get(:at, params, Coordinate.new)
|
25
|
+
# turn an :at Array value into a Coordinate
|
26
|
+
if Array === coord and not Coordinate === coord then
|
27
|
+
coord = Coordinate.new(*coord)
|
28
|
+
end
|
29
|
+
validate_coordinate(coord)
|
30
|
+
@coordinate = coord
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return this location's zero-based first dimension value
|
34
|
+
def column
|
35
|
+
@coordinate.x
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets this location's column to the given zero-based value.
|
39
|
+
def column=(value)
|
40
|
+
@coordinate.x = value
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return this location's zero-based second dimension value
|
44
|
+
def row
|
45
|
+
@coordinate.y
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sets this location's row to the given zero-based value.
|
49
|
+
def row=(value)
|
50
|
+
@coordinate.y = value
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Boolean] whether other is a Location and has the same content as this Location
|
54
|
+
def ==(other)
|
55
|
+
super rescue false
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [Location] other the position to compare
|
59
|
+
# @return (see Coordinate#<=>)
|
60
|
+
# @raise [ArgumentError] if other is not an Location
|
61
|
+
def <=>(other)
|
62
|
+
raise ArgumentError.new("Can't compare #{qp} to #{other.qp}") unless @container and Location === other
|
63
|
+
unless container == other.container then
|
64
|
+
raise ArgumentError.new("Can't compare #{qp} in container #{container} to #{other.qp} in container #{other.container}")
|
65
|
+
end
|
66
|
+
@coordinate <=> other.coordinate
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Location, nil] a new Location at the next slot in this Location's {#container},
|
70
|
+
# or nil if there are no more locations
|
71
|
+
def succ
|
72
|
+
self.class.new(:in => container, :at => @coordinate).succ! rescue nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# Sets this Location to the next slot in this Location's {#container}.
|
76
|
+
#
|
77
|
+
# @raise [IndexError] if the next slot exceeds the container capacity
|
78
|
+
# @return self
|
79
|
+
def succ!
|
80
|
+
raise IndexError.new("Location #{qp} container not set") unless @container
|
81
|
+
raise IndexError.new("Location #{qp} coordinate not set") unless @coordinate
|
82
|
+
c = column.succ % @container.capacity.columns
|
83
|
+
r = c.zero? ? row.succ : row
|
84
|
+
unless r < container.capacity.rows then
|
85
|
+
raise IndexError.new("Location #{[c, r].qp} exceeds #{@container} container capacity #{container.capacity.bounds}")
|
86
|
+
end
|
87
|
+
@coordinate.x = c
|
88
|
+
@coordinate.y = r
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
ctr_s = @container.print_class_and_id if @container
|
94
|
+
coord_s = @coordinate.to_s if @coordinate
|
95
|
+
content_s = "{#{ctr_s}#{coord_s}}" if ctr_s or coord_s
|
96
|
+
"#{print_class_and_id}{#{content_s}"
|
97
|
+
end
|
98
|
+
|
99
|
+
alias :inspect :to_s
|
100
|
+
|
101
|
+
alias :qp :to_s
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
INIT_OPTS = [:in, :at].to_set
|
106
|
+
|
107
|
+
# @param [CaRuby::Coordinate] coord the coordinate to validate
|
108
|
+
# @raise [IndexError] if the coordinate exceeds the container bounds
|
109
|
+
def validate_coordinate(coord)
|
110
|
+
bnds = @container && @container.bounds
|
111
|
+
if bnds and coord >= bnds then
|
112
|
+
raise IndexError.new("Location #{coord} exceeds container #{@container} dimensions #{bnds}")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|