caruby-tissue 1.5.6 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (255) hide show
  1. data/Gemfile +17 -0
  2. data/History.md +5 -1
  3. data/README.md +2 -2
  4. data/bin/crtdump +2 -8
  5. data/bin/crtexample +2 -5
  6. data/bin/crtmigrate +3 -6
  7. data/bin/crtsmoke +3 -8
  8. data/conf/wustl/{log4j.properties → linux/log4j.properties} +3 -3
  9. data/conf/wustl/windows/log4j.properties +40 -0
  10. data/examples/galena/Gemfile +16 -0
  11. data/examples/galena/Gemfile.lock +88 -0
  12. data/examples/galena/README.md +16 -16
  13. data/examples/galena/Rakefile +30 -0
  14. data/examples/galena/bin/seed +5 -11
  15. data/examples/galena/conf/annotation/defaults.yaml +2 -0
  16. data/examples/galena/conf/{migration/annotation_fields.yaml → annotation/fields.yaml} +2 -4
  17. data/examples/galena/conf/defaults.yaml +9 -0
  18. data/examples/galena/conf/{migration/filter_fields.yaml → filter/fields.yaml} +0 -1
  19. data/examples/galena/conf/filter/values.yaml +8 -0
  20. data/examples/galena/conf/{migration/frozen_defaults.yaml → frozen/defaults.yaml} +0 -0
  21. data/examples/galena/conf/{migration/frozen_fields.yaml → frozen/fields.yaml} +0 -2
  22. data/examples/galena/conf/{migration/general_fields.yaml → general/fields.yaml} +0 -24
  23. data/examples/galena/conf/registration/fields.yaml +6 -0
  24. data/examples/galena/conf/{migration/simple_fields.yaml → simple/fields.yaml} +1 -6
  25. data/examples/galena/data/annotation.csv +1 -1
  26. data/examples/galena/data/filter.csv +1 -1
  27. data/examples/galena/data/frozen.csv +1 -1
  28. data/examples/galena/data/general.csv +1 -1
  29. data/examples/galena/data/registration.csv +1 -1
  30. data/examples/galena/data/simple.csv +1 -1
  31. data/examples/galena/galena.gemspec +24 -0
  32. data/examples/galena/lib/galena/filter.rb +25 -0
  33. data/examples/galena/lib/galena/{tissue/migration/frozen_shims.rb → frozen.rb} +6 -10
  34. data/examples/galena/lib/galena/seed.rb +126 -0
  35. data/examples/galena/lib/galena/version.rb +3 -0
  36. data/examples/galena/lib/galena.rb +18 -7
  37. data/examples/galena/log/galena.log +37351 -0
  38. data/examples/galena/log/galena.log.0 +147830 -0
  39. data/examples/galena/spec/annotation_spec.rb +46 -0
  40. data/examples/galena/spec/filter_spec.rb +94 -0
  41. data/examples/galena/spec/frozen_spec.rb +39 -0
  42. data/examples/galena/spec/general_spec.rb +62 -0
  43. data/examples/galena/spec/registration_spec.rb +37 -0
  44. data/examples/galena/spec/seed.rb +107 -0
  45. data/examples/galena/spec/simple_spec.rb +58 -0
  46. data/examples/galena/spec/spec_helper.rb +11 -0
  47. data/examples/galena/spec/support/migration.rb +70 -0
  48. data/lib/catissue/annotation/annotatable.rb +10 -8
  49. data/lib/catissue/annotation/annotation.rb +7 -7
  50. data/lib/catissue/annotation/de_integration.rb +9 -20
  51. data/lib/catissue/annotation/importer.rb +148 -0
  52. data/lib/catissue/annotation/introspector.rb +32 -0
  53. data/lib/catissue/annotation/metadata.rb +422 -0
  54. data/lib/catissue/annotation/proxy.rb +2 -2
  55. data/lib/catissue/annotation/proxy_class.rb +45 -30
  56. data/lib/catissue/annotation/record_entry_proxy.rb +2 -2
  57. data/lib/catissue/cli/command.rb +14 -24
  58. data/lib/catissue/cli/example.rb +5 -3
  59. data/lib/catissue/cli/migrate.rb +45 -37
  60. data/lib/catissue/cli/smoke.rb +2 -3
  61. data/lib/catissue/database/annotation/annotation_service.rb +8 -17
  62. data/lib/catissue/database/annotation/entity_facade.rb +33 -30
  63. data/lib/catissue/database/annotation/id_generator.rb +1 -1
  64. data/lib/catissue/database/annotation/integration_service.rb +11 -4
  65. data/lib/catissue/database/annotation/reference_writer.rb +38 -38
  66. data/lib/catissue/database/controlled_value_finder.rb +13 -28
  67. data/lib/catissue/database/controlled_values.rb +73 -45
  68. data/lib/catissue/database.rb +637 -277
  69. data/lib/catissue/domain/abstract_domain_object.rb +5 -0
  70. data/lib/catissue/domain/abstract_position.rb +3 -5
  71. data/lib/catissue/domain/abstract_specimen.rb +79 -65
  72. data/lib/catissue/domain/abstract_specimen_collection_group.rb +3 -6
  73. data/lib/catissue/domain/address.rb +0 -2
  74. data/lib/catissue/domain/cancer_research_group.rb +0 -3
  75. data/lib/catissue/domain/capacity.rb +2 -4
  76. data/lib/catissue/domain/check_in_check_out_event_parameter.rb +0 -3
  77. data/lib/catissue/domain/collection_event_parameters.rb +2 -7
  78. data/lib/catissue/domain/collection_protocol.rb +11 -16
  79. data/lib/catissue/domain/collection_protocol_event.rb +19 -12
  80. data/lib/catissue/domain/collection_protocol_registration.rb +8 -12
  81. data/lib/catissue/domain/consent_tier_response.rb +0 -4
  82. data/lib/catissue/domain/consent_tier_status.rb +1 -4
  83. data/lib/catissue/domain/container.rb +10 -10
  84. data/lib/catissue/domain/container_position.rb +4 -7
  85. data/lib/catissue/domain/container_type.rb +4 -7
  86. data/lib/catissue/domain/department.rb +0 -3
  87. data/lib/catissue/domain/disposal_event_parameters.rb +5 -5
  88. data/lib/catissue/domain/embedded_event_parameters.rb +1 -4
  89. data/lib/catissue/domain/external_identifier.rb +0 -12
  90. data/lib/catissue/domain/frozen_event_parameters.rb +1 -4
  91. data/lib/catissue/domain/institution.rb +0 -3
  92. data/lib/catissue/domain/new_specimen_array_order_item.rb +0 -5
  93. data/lib/catissue/domain/order_details.rb +0 -2
  94. data/lib/catissue/domain/participant/clinical/chemotherapy.rb +1 -3
  95. data/lib/catissue/domain/participant/clinical/duration.rb +2 -4
  96. data/lib/catissue/domain/participant/clinical/radiation_therapy.rb +2 -4
  97. data/lib/catissue/domain/participant.rb +22 -24
  98. data/lib/catissue/domain/participant_medical_identifier.rb +0 -4
  99. data/lib/catissue/domain/password.rb +0 -4
  100. data/lib/catissue/domain/race.rb +0 -3
  101. data/lib/catissue/domain/received_event_parameters.rb +3 -6
  102. data/lib/catissue/domain/site.rb +1 -4
  103. data/lib/catissue/domain/specimen/pathology/additional_finding.rb +12 -0
  104. data/lib/catissue/domain/specimen/pathology/details.rb +12 -0
  105. data/lib/catissue/domain/specimen/pathology/gleason_score.rb +12 -0
  106. data/lib/catissue/domain/specimen/pathology/histologic_grade.rb +12 -0
  107. data/lib/catissue/domain/specimen/pathology/histologic_type.rb +19 -0
  108. data/lib/catissue/domain/specimen/pathology/histologic_variant_type.rb +12 -0
  109. data/lib/catissue/domain/specimen/pathology/invasion.rb +12 -0
  110. data/lib/catissue/domain/specimen/pathology/prostate_specimen_gleason_score.rb +5 -11
  111. data/lib/catissue/domain/specimen/pathology/prostate_specimen_pathology_annotation.rb +12 -12
  112. data/lib/catissue/domain/specimen/pathology/specimen_additional_finding.rb +5 -14
  113. data/lib/catissue/domain/specimen/pathology/specimen_base_solid_tissue_pathology_annotation.rb +6 -21
  114. data/lib/catissue/domain/specimen/pathology/specimen_details.rb +4 -10
  115. data/lib/catissue/domain/specimen/pathology/specimen_histologic_grade.rb +4 -10
  116. data/lib/catissue/domain/specimen/pathology/specimen_histologic_type.rb +6 -14
  117. data/lib/catissue/domain/specimen/pathology/specimen_histologic_variant_type.rb +5 -11
  118. data/lib/catissue/domain/specimen/pathology/specimen_invasion.rb +5 -11
  119. data/lib/catissue/domain/specimen.rb +113 -76
  120. data/lib/catissue/domain/specimen_array.rb +0 -3
  121. data/lib/catissue/domain/specimen_array_content.rb +1 -4
  122. data/lib/catissue/domain/specimen_array_type.rb +1 -4
  123. data/lib/catissue/domain/specimen_characteristics.rb +0 -3
  124. data/lib/catissue/domain/specimen_collection_group/pathology/base_pathology_annotation.rb +2 -4
  125. data/lib/catissue/domain/specimen_collection_group/pathology/base_solid_tissue_pathology_annotation.rb +2 -4
  126. data/lib/catissue/domain/specimen_collection_group.rb +43 -53
  127. data/lib/catissue/domain/specimen_event_parameters.rb +24 -32
  128. data/lib/catissue/domain/specimen_position.rb +8 -5
  129. data/lib/catissue/domain/specimen_protocol.rb +3 -6
  130. data/lib/catissue/domain/specimen_requirement.rb +22 -20
  131. data/lib/catissue/domain/storage_container.rb +9 -12
  132. data/lib/catissue/domain/storage_type.rb +6 -10
  133. data/lib/catissue/domain/transfer_event_parameters.rb +3 -6
  134. data/lib/catissue/domain/user.rb +22 -29
  135. data/lib/catissue/{util → helpers}/collectible.rb +23 -18
  136. data/lib/catissue/helpers/collectible_event_parameters.rb +68 -0
  137. data/lib/catissue/helpers/controlled_value.rb +35 -0
  138. data/lib/catissue/{domain → helpers}/hash_code.rb +0 -0
  139. data/lib/catissue/{util → helpers}/location.rb +6 -5
  140. data/lib/catissue/helpers/log.rb +4 -0
  141. data/lib/catissue/{util → helpers}/person.rb +1 -1
  142. data/lib/catissue/{util → helpers}/position.rb +10 -8
  143. data/lib/catissue/helpers/properties_loader.rb +143 -0
  144. data/lib/catissue/{util → helpers}/storable.rb +2 -1
  145. data/lib/catissue/{util → helpers}/storage_type_holder.rb +9 -3
  146. data/lib/catissue/{annotation/annotatable_class.rb → metadata.rb} +73 -95
  147. data/lib/catissue/migration/migratable.rb +93 -44
  148. data/lib/catissue/migration/migrator.rb +26 -42
  149. data/lib/catissue/migration/shims.rb +1 -1
  150. data/lib/catissue/migration/unique.rb +76 -0
  151. data/lib/catissue/resource.rb +16 -20
  152. data/lib/catissue/version.rb +1 -1
  153. data/lib/catissue/wustl/logger.rb +52 -32
  154. data/lib/catissue.rb +38 -20
  155. data/test/lib/catissue/database/controlled_values_test.rb +22 -27
  156. data/test/lib/catissue/database/database_test.rb +18 -0
  157. data/test/lib/catissue/domain/address_test.rb +9 -11
  158. data/test/lib/catissue/domain/ca_tissue_test_defaults_test.rb +5 -16
  159. data/test/lib/catissue/domain/capacity_test.rb +2 -2
  160. data/test/lib/catissue/domain/collection_event_parameters_test.rb +16 -8
  161. data/test/lib/catissue/domain/collection_protocol_event_test.rb +1 -1
  162. data/test/lib/catissue/domain/collection_protocol_registration_test.rb +26 -16
  163. data/test/lib/catissue/domain/collection_protocol_test.rb +2 -2
  164. data/test/lib/catissue/domain/container_position_test.rb +7 -4
  165. data/test/lib/catissue/domain/department_test.rb +3 -3
  166. data/test/lib/catissue/domain/disposal_event_parameters_test.rb +1 -1
  167. data/test/lib/catissue/domain/external_identifier_test.rb +5 -1
  168. data/test/lib/catissue/domain/location_test.rb +4 -4
  169. data/test/lib/catissue/domain/participant_medical_identifier_test.rb +3 -3
  170. data/test/lib/catissue/domain/participant_test.rb +33 -20
  171. data/test/lib/catissue/domain/received_event_parameters_test.rb +19 -0
  172. data/test/lib/catissue/domain/site_test.rb +2 -2
  173. data/test/lib/catissue/domain/specimen_array_test.rb +3 -3
  174. data/test/lib/catissue/domain/specimen_array_type_test.rb +6 -6
  175. data/test/lib/catissue/domain/specimen_characteristics_test.rb +1 -1
  176. data/test/lib/catissue/domain/specimen_collection_group_test.rb +49 -13
  177. data/test/lib/catissue/domain/specimen_event_parameters_test.rb +4 -4
  178. data/test/lib/catissue/domain/specimen_position_test.rb +1 -1
  179. data/test/lib/catissue/domain/specimen_requirement_test.rb +2 -2
  180. data/test/lib/catissue/domain/specimen_test.rb +58 -24
  181. data/test/lib/catissue/domain/storage_container_test.rb +3 -16
  182. data/test/lib/catissue/domain/storage_type_test.rb +3 -3
  183. data/test/lib/catissue/domain/transfer_event_parameters_test.rb +17 -17
  184. data/test/lib/catissue/domain/user_test.rb +32 -34
  185. data/test/lib/catissue/helpers/properties_loader_test.rb +19 -0
  186. data/test/lib/catissue/migration/{test_case.rb → helpers/test_case.rb} +30 -20
  187. data/test/lib/examples/galena/tissue/domain/examples_test.rb +28 -38
  188. data/test/lib/examples/galena/tissue/helpers/test_case.rb +24 -0
  189. metadata +175 -99
  190. data/bin/crtextract +0 -47
  191. data/examples/galena/conf/extract/simple_fields.yaml +0 -4
  192. data/examples/galena/conf/migration/annotation_defaults.yaml +0 -2
  193. data/examples/galena/conf/migration/filter_defaults.yaml +0 -1
  194. data/examples/galena/conf/migration/filter_values.yaml +0 -13
  195. data/examples/galena/conf/migration/participant_fields.yaml +0 -4
  196. data/examples/galena/conf/migration/registration_fields.yaml +0 -5
  197. data/examples/galena/data/participant.csv +0 -1
  198. data/examples/galena/lib/galena/tissue/migration/filter_shims.rb +0 -41
  199. data/examples/galena/lib/galena/tissue/seed/defaults.rb +0 -127
  200. data/examples/psbin/README.md +0 -45
  201. data/examples/psbin/conf/adjuvant_hormone_defaults.yaml +0 -2
  202. data/examples/psbin/conf/adjuvant_radiation_defaults.yaml +0 -3
  203. data/examples/psbin/conf/biopsy_defaults.yaml +0 -3
  204. data/examples/psbin/conf/biopsy_fields.yaml +0 -9
  205. data/examples/psbin/conf/neoadjuvant_hormone_defaults.yaml +0 -2
  206. data/examples/psbin/conf/neoadjuvant_radiation_defaults.yaml +0 -3
  207. data/examples/psbin/conf/patient_defaults.yaml +0 -3
  208. data/examples/psbin/conf/patient_fields.yaml +0 -5
  209. data/examples/psbin/conf/surgery_defaults.yaml +0 -4
  210. data/examples/psbin/conf/surgery_fields.yaml +0 -15
  211. data/examples/psbin/conf/t_stage_defaults.yaml +0 -1
  212. data/examples/psbin/conf/t_stage_fields.yaml +0 -4
  213. data/examples/psbin/conf/therapy_fields.yaml +0 -5
  214. data/examples/psbin/data/adjuvant_hormone.csv +0 -1
  215. data/examples/psbin/data/adjuvant_radiation.csv +0 -1
  216. data/examples/psbin/data/biopsy.csv +0 -1
  217. data/examples/psbin/data/neoadjuvant_hormone.csv +0 -1
  218. data/examples/psbin/data/neoadjuvant_radiation.csv +0 -1
  219. data/examples/psbin/data/patient.csv +0 -1
  220. data/examples/psbin/data/surgery.csv +0 -1
  221. data/examples/psbin/data/t_stage.csv +0 -1
  222. data/examples/psbin/lib/psbin/biopsy_shims.rb +0 -15
  223. data/examples/psbin/lib/psbin/surgery_shims.rb +0 -15
  224. data/lib/catissue/annotation/annotation_class.rb +0 -406
  225. data/lib/catissue/annotation/annotation_module.rb +0 -106
  226. data/lib/catissue/domain.rb +0 -26
  227. data/lib/catissue/extract/command.rb +0 -31
  228. data/lib/catissue/extract/delta.rb +0 -58
  229. data/lib/catissue/extract/extractor.rb +0 -99
  230. data/lib/catissue/migration/uniquify.rb +0 -2
  231. data/lib/catissue/util/collectible_event_parameters.rb +0 -71
  232. data/lib/catissue/util/controlled_value.rb +0 -29
  233. data/lib/catissue/util/uniquify.rb +0 -86
  234. data/test/fixtures/catissue/domain/conf/catissue_override.yaml +0 -9
  235. data/test/fixtures/catissue/extract/conf/scg_extract.yaml +0 -3
  236. data/test/fixtures/catissue/extract/conf/scg_fields.yaml +0 -3
  237. data/test/fixtures/catissue/extract/conf/spc_extract.yaml +0 -3
  238. data/test/fixtures/catissue/extract/conf/spc_fields.yaml +0 -4
  239. data/test/fixtures/lib/catissue/defaults_test_fixture.rb +0 -206
  240. data/test/fixtures/lib/examples/galena/migration/alt_key_shims.rb +0 -7
  241. data/test/lib/catissue/domain/base_haemotology_pathology_test.rb +0 -24
  242. data/test/lib/catissue/extract/delta_test.rb +0 -25
  243. data/test/lib/catissue/extract/extractor_test.rb +0 -43
  244. data/test/lib/catissue/import/importable_module_test.rb +0 -14
  245. data/test/lib/catissue/test_case.rb +0 -247
  246. data/test/lib/examples/galena/tissue/migration/annotation_test.rb +0 -29
  247. data/test/lib/examples/galena/tissue/migration/filter_test.rb +0 -29
  248. data/test/lib/examples/galena/tissue/migration/frozen_test.rb +0 -36
  249. data/test/lib/examples/galena/tissue/migration/general_test.rb +0 -56
  250. data/test/lib/examples/galena/tissue/migration/participant_test.rb +0 -61
  251. data/test/lib/examples/galena/tissue/migration/registration_test.rb +0 -17
  252. data/test/lib/examples/galena/tissue/migration/seedify.rb +0 -119
  253. data/test/lib/examples/galena/tissue/migration/simple_test.rb +0 -30
  254. data/test/lib/examples/galena/tissue/migration/test_case.rb +0 -72
  255. data/test/lib/examples/psbin/migration_test.rb +0 -153
@@ -0,0 +1,35 @@
1
+ require 'caruby/helpers/controlled_value'
2
+
3
+ module CaTissue
4
+ class ControlledValue < CaRuby::ControlledValue
5
+ # @param [String, Symbol] public_id_or_alias the public id value or the
6
+ # standard alias +:tissue_site+ or +:clinical_diagnosis+
7
+ # @return [String] the standard public id value for the given string or alias
8
+ def self.standard_public_id(public_id_or_alias)
9
+ PID_ALIAS_HASH[public_id_or_alias.to_sym] or public_id_or_alias.to_s
10
+ end
11
+
12
+ attr_accessor :identifier
13
+
14
+ attr_reader :public_id
15
+
16
+ # @return [Integer] the identifier of the parent
17
+ def parent_identifier
18
+ parent.identifier if parent
19
+ end
20
+
21
+ # @param public_id_or_alias (see ControlledValue.standard_public_id)
22
+ def public_id=(public_id_or_alias)
23
+ @public_id = self.class.standard_public_id(public_id_or_alias)
24
+ end
25
+
26
+ def to_s
27
+ "#{value}"
28
+ end
29
+
30
+ private
31
+
32
+ # The public id symbol => name hash.
33
+ PID_ALIAS_HASH = {:tissue_site => 'Tissue_Site_PID', :clinical_diagnosis => 'Clinical_Diagnosis_PID'}
34
+ end
35
+ end
File without changes
@@ -1,5 +1,5 @@
1
- require 'caruby/util/coordinate'
2
- require 'caruby/util/validation'
1
+ require 'caruby/helpers/coordinate'
2
+ require 'jinx/helpers/validation'
3
3
 
4
4
  module CaTissue
5
5
  # A Location is a non-Resource utility class which represents a Container row and column.
@@ -20,7 +20,7 @@ module CaTissue
20
20
  Options.validate(params, INIT_OPTS)
21
21
  @container = Options.get(:in, params)
22
22
  coord = Options.get(:at, params, Coordinate.new)
23
- # turn an :at Array value into a Coordinate
23
+ # turn an +:at+ Array value into a Coordinate
24
24
  if Array === coord and not Coordinate === coord then
25
25
  coord = Coordinate.new(*coord)
26
26
  end
@@ -50,13 +50,13 @@ module CaTissue
50
50
 
51
51
  # @return [Boolean] whether other is a Location and has the same content as this Location
52
52
  def ==(other)
53
- super rescue false
53
+ container == other.container and coordinate == other.coordinate
54
54
  end
55
55
 
56
56
  # @return [Location, nil] a new Location at the next slot in this Location's {#container},
57
57
  # or nil if there are no more locations
58
58
  def succ
59
- self.class.new(:in => container, :at => @coordinate).succ! rescue nil
59
+ self.class.new(:in => container, :at => @coordinate.dup).succ! rescue nil
60
60
  end
61
61
 
62
62
  # Sets this Location to the next slot in this Location's {#container}.
@@ -89,6 +89,7 @@ module CaTissue
89
89
 
90
90
  private
91
91
 
92
+ # @private
92
93
  INIT_OPTS = [:in, :at].to_set
93
94
 
94
95
  # @param [CaRuby::Coordinate] coord the coordinate to validate
@@ -0,0 +1,4 @@
1
+ require 'catissue/wustl/logger'
2
+
3
+ # Set up the caTissue client logger before loading the class definitions.
4
+ Wustl::Logger.configure
@@ -1,4 +1,4 @@
1
- require 'caruby/util/person'
1
+ require 'caruby/helpers/person'
2
2
 
3
3
  module CaTissue
4
4
  # Mix-in for domain classes that define the +first_name+, +last_name+ and optional +middle_name+ attributes.
@@ -1,5 +1,5 @@
1
- require 'catissue/util/location'
2
- require 'caruby/util/validation'
1
+ require 'catissue/helpers/location'
2
+ require 'jinx/helpers/validation'
3
3
 
4
4
  module CaTissue
5
5
  # The Position mix-in encapsulates the location of an occupant in a holder.
@@ -15,7 +15,8 @@ module CaTissue
15
15
  self.class === other and occupant == other.occupant and location == other.location
16
16
  end
17
17
 
18
- # @return [Coordinate] the read-only coordinate with this AbstractPosition's #row and {#column}.
18
+ # @return [Coordinate] the read-only coordinate with this AbstractPosition's {Location#row}
19
+ # and {Location#column}.
19
20
  def coordinate
20
21
  location.coordinate
21
22
  end
@@ -38,17 +39,18 @@ module CaTissue
38
39
  self.column = @location.column
39
40
  end
40
41
 
41
- # @return whether either the column or the row is nil
42
+ # @return [Boolean] whether either the column or the row is nil
42
43
  def unspecified?
43
44
  column.nil? or row.nil?
44
45
  end
45
46
 
46
- # @return [(Integer, Integer)] this Position's zero-based ({#column}, {#row}) tuple.
47
+ # @return [(Integer, Integer)] this Position's zero-based ({Location#column}, {Location#row})
48
+ # tuple
47
49
  def to_a
48
50
  [column, row]
49
51
  end
50
52
 
51
- # @raise [ValidationError] if the holder cannot hold the occupant type
53
+ # @raise [Jinx::ValidationError] if the holder cannot hold the occupant type
52
54
  def validate
53
55
  super
54
56
  logger.debug { "Validating that #{holder} can hold #{occupant}..." }
@@ -56,10 +58,10 @@ module CaTissue
56
58
  if curr_occ.nil? then
57
59
  unless holder.can_hold_child?(occupant) then
58
60
  reason = holder.full? ? "it is full" : "the occupant type is not among the supported types #{holder.container_type.child_types.qp}"
59
- raise ValidationError.new("#{holder} cannot hold #{occupant} since #{reason}")
61
+ raise Jinx::ValidationError.new("#{holder} cannot hold #{occupant} since #{reason}")
60
62
  end
61
63
  elsif curr_occ != occupant
62
- raise ValidationError.new("#{holder} cannot hold #{occupant} since the location #{[colum, row]} is already occupied by #{curr_occ}")
64
+ raise Jinx::ValidationError.new("#{holder} cannot hold #{occupant} since the location #{[colum, row]} is already occupied by #{curr_occ}")
63
65
  end
64
66
  end
65
67
  end
@@ -0,0 +1,143 @@
1
+ module CaTissue
2
+ # The caTissue classpath and connection parameters properties loader mix-in.
3
+ module PropertiesLoader
4
+ class ConfigurationError < StandardError; end
5
+
6
+ # @return [{Symbol => [String, <String>]}] the application properties
7
+ def properties
8
+ @properties ||= load_properties
9
+ end
10
+
11
+ private
12
+
13
+ # Loads the caTissue classpath and connection properties.
14
+ #
15
+ # @quirk caTissue The classpath remoteServices.xml stream is corrupted, e.g. with a line:
16
+ # at top level in <value>http://cabig4 at line 8080
17
+ # instead of the expected:
18
+ # <property name="serviceUrl">http://cabig4:8080<\/property>
19
+ # This might be a classpath order side-effect or an install jar corruption, e.g. as
20
+ # occurs when building the caCORE API with a custom DE jar.
21
+ def load_properties
22
+ # the properties file
23
+ file = default_properties_file
24
+ # the access properties
25
+ props = file && File.exists?(file) ? load_properties_file(file) : {}
26
+ # Load the Java application jar path.
27
+ path = props[:classpath] || props[:path] || infer_classpath
28
+ if path then
29
+ Java.expand_to_class_path(path)
30
+ end
31
+ # TODO - below doesn't work because of caTissue bug described in the method rubydoc.
32
+ # Augment the application login properties with the remoteService.xml url property.
33
+ # path[:url] ||= remote_service_url
34
+ # def remote_service_url
35
+ # is = Java::edu.wustl.catissuecore.domain.Specimen.java_class.class_loader.getResourceAsStream('remoteService.xml')
36
+ # xml = Java::java.util.Scanner.new(is).useDelimiter("\\A").next
37
+ # /<property.+serviceUrl['"]?>(.+)<\/property>/.match(xml).captures.first
38
+ # end
39
+ props
40
+ end
41
+
42
+ def load_properties_file(file)
43
+ props = {}
44
+ logger.info("Loading application properties from #{file}...")
45
+ File.open(file).each do |line|
46
+ # match the tolerant property definition
47
+ match = PROP_DEF_REGEX.match(line.chomp.strip) || next
48
+ # the property [name, value] tokens
49
+ tokens = match.captures
50
+ pname = tokens.first.to_sym
51
+ # :path is deprecated; if there is a :path entry, then
52
+ # set :classpath with the :path value instead.
53
+ name = pname == :path ? :classpath : pname
54
+ value = tokens.last
55
+ # capture the property
56
+ props[name] = value
57
+ end
58
+ props
59
+ end
60
+
61
+ # The property/value matcher, e.g.:
62
+ # host: jacardi
63
+ # host = jacardi
64
+ # host=jacardi
65
+ # name: J. Edgar Hoover
66
+ # but not:
67
+ # # host: jacardi
68
+ # The captures are the trimmed property and value.
69
+ PROP_DEF_REGEX = /^(\w+)(?:\s*[:=]\s*)([^#]+)/
70
+
71
+ # @return [String] the default application properties file, given by +~/.+_name_,
72
+ # where _name_ is the underscore unqualified module name, e.g. +~/.catissue+
73
+ # for module +CaTissue+
74
+ def default_properties_file
75
+ home = ENV['HOME'] || ENV['USERPROFILE'] || '~'
76
+ file = File.expand_path('.catissue', home)
77
+ file if File.exists?(file)
78
+ end
79
+
80
+ # @quirk caTissue 1.2 the caTissue API class path is sensitive to order in subtle ways that
81
+ # caTissue 1.1.2 is not. The caTissue client +build.xml+ defines a +cp+ classpath
82
+ # property which was used to run a caTissue 1.1.1 example.
83
+ #
84
+ # However, the example was removed in caTissue 1.1.2. As of 1.1.2, the +cp+ property is no
85
+ # longer referenced in the client +build.xml+. Rather, the classpath is defined inline
86
+ # in the task with a small but important change: +cp+ placed the config directories
87
+ # before the jar files, whereas the inline definition placed the configs after the jars.
88
+ #
89
+ # This difference does not present a problem in caTissue 1.1.2, but the confusion was
90
+ # carried over to caTissue 1.2 where it does cause a problem. Unlike caTissue 1.1.2,
91
+ # in 1.2 the +DynamicExtension.jar+ is redundantly included in both the client and the
92
+ # declient lib. Furthermore, +DynamicExtension.jar+ contains an invalid and unnecessary
93
+ # +remoteServices.xml+, which references a non-existent +RemoteSDKApplicationService+.
94
+ #
95
+ # If the caTissue path is defined with the jars preceding the configs, then in 1.2 an
96
+ # obscure +PropertyAccessExceptionsException+ exception is raised indicating that
97
+ # +RemoteSDKApplicationService+ is an invalid class name. The cause is the classpath
98
+ # precedence of jars prior to configs.
99
+ #
100
+ # The caRuby FAQ shows an example +~/.catissue+ +path+ property that was originally
101
+ # borrowed from the caTissue 1.1.1 client example. This incorrect precedence was a time
102
+ # bomb that exploded in 1.2. This 1.2 regression is now noted in the FAQ.
103
+ #
104
+ # @quirk caTissue 1.2 per the caTissue API client Ant build file, the +catissuecore.jar+
105
+ # should not be included in the client class path, even though it is in the client lib
106
+ # directory.
107
+ def infer_classpath
108
+ dir = ENV['CATISSUE_CLIENT_HOME'] || '.'
109
+ logger.info("Inferring the class path from directory #{dir}...")
110
+ # Hunt for the client directories
111
+ clt_dir = client_directory(dir, 'caTissueSuite_Client')
112
+ de_dir = client_directory(dir, 'catissue_de_integration_client')
113
+ dirs = [client_subdirectory(clt_dir, 'conf'),
114
+ client_subdirectory(de_dir, 'conf'),
115
+ client_subdirectory(clt_dir, 'lib'),
116
+ client_subdirectory(de_dir, 'lib')
117
+ ]
118
+ # caTissue 1.1.2 has an extra directory.
119
+ clt_lib = File.expand_path('lib', dir)
120
+ dirs << clt_lib if File.directory?(clt_lib)
121
+ # Make a semi-colon separated path string.
122
+ path = dirs.join(';')
123
+ logger.info("Inferred the class path #{path}.")
124
+ path
125
+ end
126
+
127
+ def client_directory(dir, subdir)
128
+ clt_dir = File.expand_path(subdir, dir)
129
+ unless File.directory?(clt_dir) then
130
+ raise ConfigurationError.new("caTissue installation client directory not found: #{clt_dir}")
131
+ end
132
+ clt_dir
133
+ end
134
+
135
+ def client_subdirectory(dir, subdir)
136
+ clt_subdir = File.expand_path(subdir, dir)
137
+ unless File.directory?(clt_subdir) then
138
+ raise ConfigurationError.new("caTissue client directory #{dir} does not include the #{subdir} subdirectory.")
139
+ end
140
+ clt_subdir
141
+ end
142
+ end
143
+ end
@@ -1,4 +1,5 @@
1
- require 'caruby/util/collection'
1
+ require 'jinx/helpers/collections'
2
+
2
3
 
3
4
  module CaTissue
4
5
  # The Storable mix-in adds methods for a domain class which can be stored and implements
@@ -1,9 +1,9 @@
1
- require 'caruby/util/collection'
1
+ require 'jinx/helpers/partial_order'
2
2
 
3
3
  module CaTissue
4
4
  # The StorageTypeHolder mix-in adds common methods for the StorageType or StorageContainer child type accessors.
5
5
  module StorageTypeHolder
6
- include PartialOrder
6
+ include Jinx::PartialOrder
7
7
 
8
8
  # @return [StorageType] the allowable child storage types
9
9
  def child_storage_types
@@ -20,10 +20,16 @@ module CaTissue
20
20
  holds_specimen_array_types
21
21
  end
22
22
 
23
+ # Returns the {CaTissue::SpecimenArrayType} or {CaTissue::StorageType} children which this StorageTypeHolder
24
+ # can hold.
25
+ def child_container_types
26
+ child_storage_types.union(child_specimen_array_types)
27
+ end
28
+
23
29
  # Returns the {CaTissue::SpecimenArrayType}, {CaTissue::AbstractSpecimen#specimen_class} or {CaTissue::StorageType}
24
30
  # children which this StorageTypeHolder can hold.
25
31
  def child_types
26
- child_storage_types.union(child_specimen_classes).union(child_specimen_array_types)
32
+ child_container_types.union(child_specimen_classes)
27
33
  end
28
34
 
29
35
  # Adds the given subtype to the list of subtypes which this StorageType can hold.
@@ -1,41 +1,20 @@
1
- require 'caruby/util/inflector'
2
- require 'caruby/domain/metadata'
3
- require 'catissue/annotation/annotation_module'
1
+ require 'caruby/metadata'
2
+ require 'catissue/annotation/importer'
4
3
  require 'catissue/annotation/de_integration'
5
4
 
6
5
  module CaTissue
7
- # Mix-in for extending a caTissue domain class with annotations.
8
- module AnnotatableClass
6
+ # Mix-in for extending a caTissue domain class with +Jinx::Metadata+ introspection and annotations.
7
+ module Metadata
8
+ include CaRuby::Metadata
9
9
 
10
10
  # @return [Integer, nil] the the hook class designator that is used by caTissue to persist primary
11
11
  # annotation objects, or nil if this is not a primary annotation class
12
12
  attr_reader :entity_id
13
-
14
- # @return [Class] the {DEIntegration} proxy class (nil for 1.1 caTissue)
13
+
14
+ # @return [Class] the {Annotation::DEIntegration} proxy class (nil for 1.1 caTissue)
15
15
  def de_integration_proxy_class
16
16
  @de_integration_proxy_class or (superclass.de_integration_proxy_class if superclass < Annotatable)
17
17
  end
18
-
19
- # Adds {CaRuby::Domain::Metadata} and {AnnotatableClass} functionality to the given class.
20
- #
21
- # @param [Class] the domain class to extend
22
- def self.extend_class(klass)
23
- # Enable the class meta-data.
24
- klass.extend(CaRuby::Domain::Metadata)
25
- klass.extend(self)
26
- end
27
-
28
- def self.extended(klass)
29
- super
30
- # Initialize the class annotation hashes.
31
- klass.class_eval do
32
- # Enable the class meta-data.
33
- # the annotation name => spec hash
34
- @ann_spec_hash = {}
35
- # the annotation module => proxy hash
36
- @ann_mod_pxy_hash = {}
37
- end
38
- end
39
18
 
40
19
  # @return [Integer, nil] this class's entity id, if it exists, otherwise the superclass effective entity id
41
20
  # if the superclass is an annotation class
@@ -61,21 +40,21 @@ module CaTissue
61
40
  annotation_defined?(symbol)
62
41
  end
63
42
 
64
- # Refines the {CaRuby::Domain::Attributes#toxic_attributes} to exclude annotation attributes.
43
+ # Refines the +CaRuby::Propertied.toxic_attributes+ to exclude annotation attributes.
65
44
  #
66
45
  # @return [<Symbol>] the non-annotation unfetched attributes
67
46
  def toxic_attributes
68
- @anntbl_toxic_attrs ||= unfetched_attributes.compose { |attr_md| not attr_md.type < Annotation }
47
+ @anbl_toxic_attrs ||= unfetched_attributes.compose { |prop| not prop.type < Annotation }
69
48
  end
70
49
 
71
50
  # @param [AnnotationModule] mod the annotation module
72
51
  # @return [Symbol] the corresponding annotation proxy reference attribute
73
52
  def annotation_proxy_attribute(mod)
74
- @ann_mod_pxy_hash[mod] or
53
+ (@ann_mod_pxy_hash and @ann_mod_pxy_hash[mod]) or
75
54
  (superclass.annotation_proxy_attribute(mod) if superclass < Annotatable) or
76
55
  raise AnnotationError.new("#{qp} #{mod} proxy attribute not found.")
77
56
  end
78
-
57
+
79
58
  # Loads the annotations, if necessary, and tries to get the constant again.
80
59
  #
81
60
  # @param [Symbol] symbol the missing constant
@@ -90,60 +69,58 @@ module CaTissue
90
69
  end
91
70
  end
92
71
 
93
- # Filters {CaRuby::Domain::Attributes#loadable_attributes} to exclude the {#annotation_attributes}
72
+ # Filters +CaRuby::Propertied#loadable_attributes} to exclude the {.annotation_attributes+
94
73
  # since annotation lazy-loading is not supported.
95
74
  #
96
- # @return (see CaRuby::Domain::Attributes#loadable_attributes)
75
+ # @return (see CaRuby::Propertied#loadable_attributes)
97
76
  def loadable_attributes
98
- @antbl_ld_attrs ||= super.compose { |attr_md| not attr_md.type < Annotation }
77
+ @antbl_ld_attrs ||= unfetched_attributes.compose do |prop|
78
+ # JRuby bug - Copied super body to avoid infinite loop. See const_missing.
79
+ prop.java_property? and not prop.type.abstract? and not prop.type < Annotation
80
+ end
99
81
  end
100
-
82
+
101
83
  def printable_attributes
102
- @prbl_attrs ||= super.union(annotation_attributes)
103
- end
104
-
105
- def attribute_metadata(attribute)
106
- begin
107
- super
108
- rescue
109
- if annotation_attribute?(attribute) then
110
- attribute_metadata(attribute)
111
- else
112
- raise
113
- end
114
- end
84
+ # JRuby bug - Copied super body to avoid infinite loop. See const_missing.
85
+ @prbl_attrs ||= java_attributes.union(annotation_attributes)
115
86
  end
116
-
87
+
117
88
  def annotation_attributes
89
+ @ann_mod_pxy_hash ||= {}
118
90
  @ann_attrs ||= append_ancestor_enum(@ann_mod_pxy_hash.enum_values) do |sc|
119
91
  sc.annotation_attributes if sc < Annotatable
120
92
  end
121
93
  end
122
-
94
+
123
95
  protected
124
-
96
+
125
97
  # @return [<AnnotationModule>] the annotation modules in the class hierarchy
126
98
  def annotation_modules
127
99
  @ann_mods ||= load_annotations
128
100
  end
129
-
101
+
130
102
  private
131
-
103
+
104
+ # @return [Boolean] whenter this class's annotations are loaded
105
+ def annotations_loaded?
106
+ !!@ann_mods
107
+ end
108
+
132
109
  # @param [String] name the proxy record entry class name
133
110
  def annotation_proxy_class_name=(name)
134
111
  @de_integration_proxy_class = Annotation::DEIntegration.proxy(name)
135
112
  if @de_integration_proxy_class then
136
113
  # hide the internal caTissue proxy collection attribute
137
- attr = detect_attribute_with_type(@de_integration_proxy_class)
138
- if attr then
139
- remove_attribute(attr)
140
- logger.debug { "Hid the internal caTissue #{qp} annotation record-keeping attribute #{attr}." }
114
+ pa = detect_attribute_with_type(@de_integration_proxy_class)
115
+ if pa then
116
+ remove_attribute(pa)
117
+ logger.debug { "Hid the internal caTissue #{qp} annotation record-keeping attribute #{pa}." }
141
118
  end
142
119
  else
143
120
  logger.debug { "Ignored the missing caTissue #{qp} proxy class name #{name}, presumably unsupported in this caTissue release." }
144
121
  end
145
122
  end
146
-
123
+
147
124
  # Loads the annotation modules in the class hierarchy.
148
125
  #
149
126
  # @return [<AnnotationModule>] an Enumerable on the loaded annotation modules
@@ -151,15 +128,15 @@ module CaTissue
151
128
  @local_ann_mods = load_local_annotations
152
129
  superclass < Annotatable ? @local_ann_mods.union(superclass.annotation_modules) : @local_ann_mods
153
130
  end
154
-
131
+
155
132
  def parent_entity_id
156
133
  superclass.entity_id if superclass < Annotatable
157
134
  end
158
-
135
+
159
136
  def annotatable_class_hierarchy
160
137
  class_hierarchy.filter { |klass| klass < Annotatable }
161
138
  end
162
-
139
+
163
140
  # Declares an annotation scoped by this class.
164
141
  #
165
142
  # @param [String] name the name of the annotation module
@@ -167,6 +144,7 @@ module CaTissue
167
144
  # @option opts [String] :package the package name (default is the lower-case underscore name)
168
145
  # @option opts [String] :service the service name (default is the lower-case underscore name)
169
146
  # @option opts [String] :group the DE group short name (default is the package)
147
+ # @option opts [String] :proxy_name the DE proxy class name (default is the class name followed by +RecordEntry+)
170
148
  def add_annotation(name, opts={})
171
149
  # the module symbol
172
150
  mod_sym = name.camelize.to_sym
@@ -174,35 +152,39 @@ module CaTissue
174
152
  pkg = opts[:package] ||= name.underscore
175
153
  svc = opts[:service] ||= name.underscore
176
154
  grp = opts[:group] ||= pkg
155
+ pxy_nm = opts[:proxy_name] || "#{self.name.demodulize}RecordEntry"
156
+ self.annotation_proxy_class_name = pxy_nm
177
157
  # add the annotation entry
158
+ @ann_spec_hash ||= {}
178
159
  @ann_spec_hash[mod_sym] = opts
179
160
  logger.info("Added #{qp} annotation #{name} with module #{mod_sym}, package #{pkg}, service #{svc} and group #{grp}.")
180
161
  end
181
-
162
+
182
163
  # @return [Boolean] whether this annotatable class's annotations are loaded
183
164
  def annotations_loaded?
184
- not @ann_mods.nil?
165
+ !!@ann_mods
185
166
  end
186
-
167
+
187
168
  # Loads this class's annotations.
188
169
  #
189
170
  # @return [<AnnotationModule>] the loaded annotation modules
190
171
  def load_local_annotations
172
+ return Array::EMPTY_ARRAY if @ann_spec_hash.nil?
191
173
  # an annotated class has a hook entity id
192
- unless @ann_spec_hash.empty? then initialize_annotation_holder end
174
+ initialize_annotation_holder
193
175
  # build the annotations
194
176
  @ann_spec_hash.map { |name, opts| import_annotation(name, opts) }
195
- end
196
-
177
+ end
178
+
197
179
  # Determines this annotated class's {#entity_id} and {#de_integration_proxy_class}.
198
180
  def initialize_annotation_holder
199
181
  @entity_id = Annotation::EntityFacade.instance.hook_entity_id(self)
200
182
  end
201
-
183
+
202
184
  # @param [Symbol] attribute the annotation accessor
203
185
  # @return [Module] the annotation module which implements the attribute
204
186
  def annotation_attribute_module(attribute)
205
- annotation_modules.detect { |mod| mod.proxy.attribute_defined?(attribute) }
187
+ annotation_modules.detect { |mod| mod.proxy.property_defined?(attribute) }
206
188
  end
207
189
 
208
190
  # Builds a new annotation module for the given module name and options.
@@ -213,19 +195,14 @@ module CaTissue
213
195
  # @raise [AnnotationError] if there is no annotation proxy class
214
196
  def import_annotation(name, opts)
215
197
  logger.debug { "Importing #{qp} annotation #{name}..." }
216
- # Make the annotation module scoped by this Annotatable class.
217
- class_eval("class #{name}; end")
218
- klass = const_get(name)
198
+ # Make the annotation module class scoped by this Annotatable class.
199
+ class_eval("module #{name}; end")
200
+ mod = const_get(name)
219
201
  # Append the AnnotationModule methods.
220
- AnnotationModule.extend_module(klass, self, opts)
221
- # Make the proxy attribute.
222
- attr = create_proxy_attribute(klass)
223
- # The proxy is a logical dependent.
224
- add_dependent_attribute(attr, :logical)
225
- logger.debug { "Created #{qp} annotation proxy logical dependent reference attribute #{attr}." }
226
- # Fill out the dependency hierarchy.
227
- klass.proxy.build_annotation_dependency_hierarchy
228
- klass
202
+ mod.extend(Annotation::Importer)
203
+ # Build the annnotation module.
204
+ mod.initialize_annotation(self, opts) { |pxy| create_proxy_attribute(mod, pxy) }
205
+ mod
229
206
  end
230
207
 
231
208
  # Returns whether this class has an annotation whose proxy accessor is the
@@ -235,28 +212,29 @@ module CaTissue
235
212
  # @return (see #annotation?)
236
213
  # @see #annotation?
237
214
  def annotation_defined?(symbol)
238
- attribute_defined?(symbol) and attribute_metadata(symbol).type < Annotation
215
+ property_defined?(symbol) and property(symbol).type < Annotation
239
216
  end
240
-
217
+
241
218
  # Makes an attribute whose name is the demodulized underscored given module name.
242
219
  # The attribute reader creates an {Annotation::Proxy} instance of the method
243
220
  # receiver {Annotatable} instance on demand.
244
221
  #
245
- # @param [AnnotationModule] klass the subject annotation
246
- # @return [Symbol] the proxy attribute
247
- def create_proxy_attribute(klass)
248
- # the proxy class
249
- pxy = klass.proxy
222
+ # @param [AnnotationModule] mod the subject annotation
223
+ # @return [ProxyClass] proxy the proxy class
224
+ def create_proxy_attribute(mod, proxy)
250
225
  # the proxy attribute symbol
251
- attr = klass.name.demodulize.underscore.to_sym
226
+ pa = mod.name.demodulize.underscore.to_sym
252
227
  # Define the proxy attribute.
253
- attr_create_on_demand_accessor(attr) { Set.new }
228
+ attr_create_on_demand_accessor(pa) { Set.new }
254
229
  # Register the attribute.
255
- add_attribute(attr, pxy, :collection, :saved)
256
- logger.debug { "Added #{qp} #{klass.qp} annotation proxy attribute #{attr} of type #{pxy}." }
230
+ add_attribute(pa, proxy, :collection, :saved, :nosync)
257
231
  # the annotation module => proxy attribute association
258
- @ann_mod_pxy_hash[klass] = attr
259
- attr
232
+ @ann_mod_pxy_hash ||= {}
233
+ @ann_mod_pxy_hash[mod] = pa
234
+ # The proxy is a logical dependent.
235
+ add_dependent_attribute(pa, :logical)
236
+ logger.debug { "Created #{qp} #{mod.qp} annotation proxy logical dependent reference attribute #{pa} to #{proxy}." }
237
+ pa
260
238
  end
261
239
  end
262
240
  end