caruby-core 1.5.5 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/Gemfile +9 -0
  2. data/History.md +5 -1
  3. data/lib/caruby.rb +3 -5
  4. data/lib/caruby/caruby-src.tar.gz +0 -0
  5. data/lib/caruby/database.rb +53 -69
  6. data/lib/caruby/database/application_service.rb +25 -0
  7. data/lib/caruby/database/cache.rb +60 -0
  8. data/lib/caruby/database/fetched_matcher.rb +52 -38
  9. data/lib/caruby/database/lazy_loader.rb +4 -4
  10. data/lib/caruby/database/operation.rb +34 -0
  11. data/lib/caruby/database/persistable.rb +171 -86
  12. data/lib/caruby/database/persistence_service.rb +32 -34
  13. data/lib/caruby/database/persistifier.rb +100 -43
  14. data/lib/caruby/database/reader.rb +107 -85
  15. data/lib/caruby/database/reader_template_builder.rb +60 -0
  16. data/lib/caruby/database/saved_matcher.rb +3 -3
  17. data/lib/caruby/database/sql_executor.rb +88 -17
  18. data/lib/caruby/database/writer.rb +213 -177
  19. data/lib/caruby/database/writer_template_builder.rb +334 -0
  20. data/lib/caruby/{util → helpers}/controlled_value.rb +0 -0
  21. data/lib/caruby/{util → helpers}/coordinate.rb +4 -4
  22. data/lib/caruby/{util → helpers}/person.rb +3 -3
  23. data/lib/caruby/{util → helpers}/properties.rb +7 -9
  24. data/lib/caruby/{util → helpers}/roman.rb +2 -2
  25. data/lib/caruby/{util → helpers}/version.rb +1 -1
  26. data/lib/caruby/json/deserializer.rb +2 -2
  27. data/lib/caruby/json/serializer.rb +49 -7
  28. data/lib/caruby/metadata.rb +30 -0
  29. data/lib/caruby/metadata/java_property.rb +21 -0
  30. data/lib/caruby/metadata/propertied.rb +191 -0
  31. data/lib/caruby/metadata/property.rb +22 -0
  32. data/lib/caruby/metadata/property_characteristics.rb +201 -0
  33. data/lib/caruby/migration/migratable.rb +11 -182
  34. data/lib/caruby/rdbi/driver/jdbc.rb +446 -0
  35. data/lib/caruby/resource.rb +20 -823
  36. data/lib/caruby/version.rb +1 -1
  37. data/test/lib/caruby/database/cache_test.rb +54 -0
  38. data/test/lib/caruby/{util → helpers}/controlled_value_test.rb +3 -5
  39. data/test/lib/caruby/{util → helpers}/person_test.rb +4 -6
  40. data/test/lib/caruby/helpers/properties_test.rb +34 -0
  41. data/test/lib/caruby/{util → helpers}/roman_test.rb +2 -3
  42. data/test/lib/caruby/{util → helpers}/version_test.rb +2 -3
  43. data/test/lib/helper.rb +7 -0
  44. metadata +161 -214
  45. data/lib/caruby/cli/application.rb +0 -36
  46. data/lib/caruby/cli/command.rb +0 -202
  47. data/lib/caruby/csv/csv_mapper.rb +0 -159
  48. data/lib/caruby/csv/csvio.rb +0 -203
  49. data/lib/caruby/database/search_template_builder.rb +0 -56
  50. data/lib/caruby/database/store_template_builder.rb +0 -278
  51. data/lib/caruby/domain.rb +0 -193
  52. data/lib/caruby/domain/attribute.rb +0 -584
  53. data/lib/caruby/domain/attributes.rb +0 -628
  54. data/lib/caruby/domain/dependency.rb +0 -225
  55. data/lib/caruby/domain/id_alias.rb +0 -22
  56. data/lib/caruby/domain/importer.rb +0 -183
  57. data/lib/caruby/domain/introspection.rb +0 -176
  58. data/lib/caruby/domain/inverse.rb +0 -172
  59. data/lib/caruby/domain/inversible.rb +0 -90
  60. data/lib/caruby/domain/java_attribute.rb +0 -173
  61. data/lib/caruby/domain/merge.rb +0 -185
  62. data/lib/caruby/domain/metadata.rb +0 -142
  63. data/lib/caruby/domain/mixin.rb +0 -35
  64. data/lib/caruby/domain/properties.rb +0 -95
  65. data/lib/caruby/domain/reference_visitor.rb +0 -428
  66. data/lib/caruby/domain/uniquify.rb +0 -50
  67. data/lib/caruby/import/java.rb +0 -387
  68. data/lib/caruby/migration/migrator.rb +0 -918
  69. data/lib/caruby/migration/resource_module.rb +0 -9
  70. data/lib/caruby/migration/uniquify.rb +0 -17
  71. data/lib/caruby/util/attribute_path.rb +0 -44
  72. data/lib/caruby/util/cache.rb +0 -56
  73. data/lib/caruby/util/class.rb +0 -149
  74. data/lib/caruby/util/collection.rb +0 -1152
  75. data/lib/caruby/util/domain_extent.rb +0 -46
  76. data/lib/caruby/util/file_separator.rb +0 -65
  77. data/lib/caruby/util/inflector.rb +0 -27
  78. data/lib/caruby/util/log.rb +0 -95
  79. data/lib/caruby/util/math.rb +0 -12
  80. data/lib/caruby/util/merge.rb +0 -59
  81. data/lib/caruby/util/module.rb +0 -18
  82. data/lib/caruby/util/options.rb +0 -97
  83. data/lib/caruby/util/partial_order.rb +0 -35
  84. data/lib/caruby/util/pretty_print.rb +0 -204
  85. data/lib/caruby/util/stopwatch.rb +0 -74
  86. data/lib/caruby/util/topological_sync_enumerator.rb +0 -62
  87. data/lib/caruby/util/transitive_closure.rb +0 -55
  88. data/lib/caruby/util/tree.rb +0 -48
  89. data/lib/caruby/util/trie.rb +0 -37
  90. data/lib/caruby/util/uniquifier.rb +0 -30
  91. data/lib/caruby/util/validation.rb +0 -20
  92. data/lib/caruby/util/visitor.rb +0 -365
  93. data/lib/caruby/util/weak_hash.rb +0 -36
  94. data/test/lib/caruby/csv/csv_mapper_test.rb +0 -40
  95. data/test/lib/caruby/csv/csvio_test.rb +0 -69
  96. data/test/lib/caruby/database/persistable_test.rb +0 -92
  97. data/test/lib/caruby/domain/domain_test.rb +0 -112
  98. data/test/lib/caruby/domain/inversible_test.rb +0 -99
  99. data/test/lib/caruby/domain/reference_visitor_test.rb +0 -130
  100. data/test/lib/caruby/import/java_test.rb +0 -80
  101. data/test/lib/caruby/import/mixed_case_test.rb +0 -14
  102. data/test/lib/caruby/migration/test_case.rb +0 -102
  103. data/test/lib/caruby/test_case.rb +0 -230
  104. data/test/lib/caruby/util/cache_test.rb +0 -23
  105. data/test/lib/caruby/util/class_test.rb +0 -61
  106. data/test/lib/caruby/util/collection_test.rb +0 -398
  107. data/test/lib/caruby/util/command_test.rb +0 -55
  108. data/test/lib/caruby/util/domain_extent_test.rb +0 -60
  109. data/test/lib/caruby/util/file_separator_test.rb +0 -30
  110. data/test/lib/caruby/util/inflector_test.rb +0 -12
  111. data/test/lib/caruby/util/lazy_hash_test.rb +0 -34
  112. data/test/lib/caruby/util/merge_test.rb +0 -83
  113. data/test/lib/caruby/util/module_test.rb +0 -25
  114. data/test/lib/caruby/util/options_test.rb +0 -59
  115. data/test/lib/caruby/util/partial_order_test.rb +0 -42
  116. data/test/lib/caruby/util/pretty_print_test.rb +0 -85
  117. data/test/lib/caruby/util/properties_test.rb +0 -50
  118. data/test/lib/caruby/util/stopwatch_test.rb +0 -18
  119. data/test/lib/caruby/util/topological_sync_enumerator_test.rb +0 -69
  120. data/test/lib/caruby/util/transitive_closure_test.rb +0 -67
  121. data/test/lib/caruby/util/tree_test.rb +0 -23
  122. data/test/lib/caruby/util/trie_test.rb +0 -14
  123. data/test/lib/caruby/util/visitor_test.rb +0 -278
  124. data/test/lib/caruby/util/weak_hash_test.rb +0 -45
  125. data/test/lib/examples/clinical_trials/migration/migration_test.rb +0 -58
  126. data/test/lib/examples/clinical_trials/migration/test_case.rb +0 -38
@@ -1,225 +0,0 @@
1
- require 'caruby/util/validation'
2
-
3
- module CaRuby
4
- module Domain
5
- # Metadata mix-in to capture Resource dependency.
6
- module Dependency
7
-
8
- attr_reader :owners, :owner_attributes
9
-
10
- # Returns the most specific attribute which references the dependent type, or nil if none.
11
- # If the given class can be returned by more than dependent attribute, then the attribute
12
- # is chosen whose return type most closely matches the given class.
13
- #
14
- # @param [Class] klass the dependent type
15
- # @return [Symbol, nil] the dependent reference attribute, or nil if none
16
- def dependent_attribute(klass)
17
- dependent_attributes.inject(nil) do |best, attr|
18
- type = domain_type(attr)
19
- # If the attribute can return the klass then the return type is a candidate.
20
- # In that case, the klass replaces the best candidate if it is more specific than
21
- # the best candidate so far.
22
- klass <= type ? (best && best < type ? best : type) : best
23
- end
24
- end
25
-
26
- # Adds the given attribute as a dependent.
27
- #
28
- # Supported flags include the following:
29
- # * :logical - the dependency relation is not cascaded by the application
30
- # * :autogenerated - a dependent can be created by the application as a side-effect of creating the owner
31
- # * :disjoint - the dependent owner has more than one owner attribute, but only one owner instance
32
- #
33
- # If the attribute inverse is not a collection, then the attribute writer
34
- # is modified to delegate to the dependent owner writer. This enforces
35
- # referential integrity by ensuring that the following post-condition holds:
36
- # * _owner_._attribute_._inverse_ == _owner_
37
- # where:
38
- # * _owner_ is an instance this attribute's declaring class
39
- # * _inverse_ is the owner inverse attribute defined in the dependent class
40
- #
41
- # @param [Symbol] attribute the dependent to add
42
- # @param [<Symbol>] flags the attribute qualifier flags
43
- def add_dependent_attribute(attribute, *flags)
44
- attr_md = attribute_metadata(attribute)
45
- flags << :dependent unless flags.include?(:dependent)
46
- attr_md.qualify(*flags)
47
- inverse = attr_md.inverse
48
- inv_type = attr_md.type
49
- # example: Parent.add_dependent_attribute(:children) with inverse :parent calls the following:
50
- # Child.add_owner(Parent, :children, :parent)
51
- inv_type.add_owner(self, attribute, inverse)
52
- end
53
-
54
- # @return [Boolean] whether this class depends on an owner
55
- def dependent?
56
- not owners.empty?
57
- end
58
-
59
- # @return [Boolean] whether this class has an owner which cascades save operations to this dependent
60
- def cascaded_dependent?
61
- owner_attribute_metadata_enumerator.any? { |attr_md| attr_md.inverse_metadata.cascaded? }
62
- end
63
-
64
- # @return [Boolean] whether this class depends the given other class
65
- def depends_on?(other)
66
- owners.detect { |owner| owner === other }
67
- end
68
-
69
- # @param [Class] klass the dependent type
70
- # @return [Symbol, nil] the attribute which references the dependent type, or nil if none
71
- def dependent_attribute(klass)
72
- type = dependent_attributes.detect_with_metadata { |attr_md| attr_md.type == klass }
73
- return type if type
74
- dependent_attribute(klass.superclass) if klass.superclass < Resource
75
- end
76
-
77
- # @return [<Symbol>] this class's owner attributes
78
- def owner_attributes
79
- @oattrs ||= owner_attribute_metadata_enumerator.transform { |attr_md| attr_md.to_sym }
80
- end
81
-
82
- # @return [Boolean] whether this {Resource} class is dependent and reference its owners
83
- def bidirectional_dependent?
84
- dependent? and not owner_attributes.empty?
85
- end
86
-
87
- # @return [<Class>] this class's dependent types
88
- def dependents
89
- dependent_attributes.wrap { |attr| attr.type }
90
- end
91
-
92
- # @return [<Class>] this class's owner types
93
- def owners
94
- @owners ||= Enumerable::Enumerator.new(owner_attribute_metadata_hash, :each_key)
95
- end
96
-
97
- # @return [Attribute, nil] the sole owner attribute metadata of this class, or nil if there
98
- # is not exactly one owner
99
- def owner_attribute_metadata
100
- attr_mds = owner_attribute_metadata_enumerator
101
- attr_mds.first if attr_mds.size == 1
102
- end
103
-
104
- # @return [Symbol, nil] the sole owner attribute of this class, or nil if there
105
- # is not exactly one owner
106
- def owner_attribute
107
- attr_md = owner_attribute_metadata || return
108
- attr_md.to_sym
109
- end
110
-
111
- # @return [Class, nil] the sole owner type of this class, or nil if there
112
- # is not exactly one owner
113
- def owner_type
114
- attr_md = owner_attribute_metadata || return
115
- attr_md.type
116
- end
117
-
118
- protected
119
-
120
- # Adds the given owner class to this dependent class.
121
- # This method must be called before any dependent attribute is accessed.
122
- # If the attribute is given, then the attribute inverse is set.
123
- # Otherwise, if there is not already an owner attribute, then a new owner attribute is created.
124
- # The name of the new attribute is the lower-case demodulized owner class name.
125
- #
126
- # @param [Class] the owner class
127
- # @param [Symbol] inverse the owner -> dependent attribute
128
- # @param [Symbol, nil] attribute the dependent -> owner attribute, if known
129
- # @raise [ValidationError] if the inverse is nil
130
- def add_owner(klass, inverse, attribute=nil)
131
- if inverse.nil? then raise ValidationError.new("Owner #{klass.qp} missing dependent attribute for dependent #{qp}") end
132
- logger.debug { "Adding #{qp} owner #{klass.qp}#{' attribute ' + attribute.to_s if attribute}#{' inverse ' + inverse.to_s if inverse}..." }
133
- if @owner_attr_hash then
134
- raise MetadataError.new("Can't add #{qp} owner #{klass.qp} after dependencies have been accessed")
135
- end
136
-
137
- # detect the owner attribute, if necessary
138
- attribute ||= detect_owner_attribute(klass, inverse)
139
- attr_md = attribute_metadata(attribute) if attribute
140
- # Add the owner class => attribute entry.
141
- # The attribute is nil if the dependency is unidirectional, i.e. there is an owner class which
142
- # references this class via a dependency attribute but there is no inverse owner attribute.
143
- local_owner_attribute_metadata_hash[klass] = attr_md
144
- # If the dependency is unidirectional, then our job is done.
145
- return if attribute.nil?
146
-
147
- # set the inverse if necessary
148
- unless attr_md.inverse then
149
- set_attribute_inverse(attribute, inverse)
150
- end
151
- # set the owner flag if necessary
152
- unless attr_md.owner? then attr_md.qualify(:owner) end
153
- # Redefine the writer method to warn when changing the owner
154
- rdr, wtr = attr_md.accessors
155
- logger.debug { "Injecting owner change warning into #{qp}.#{attribute} writer method #{wtr}..." }
156
- redefine_method(wtr) do |old_wtr|
157
- lambda do |ref|
158
- prev = send(rdr)
159
- if prev and prev != ref then
160
- if ref.nil? then
161
- logger.warn("Unsetting the #{self} owner #{attribute} #{prev}.")
162
- elsif ref.identifier != prev.identifier then
163
- logger.warn("Resetting the #{self} owner #{attribute} from #{prev} to #{ref}.")
164
- end
165
- end
166
- send(old_wtr, ref)
167
- end
168
- end
169
- end
170
-
171
- # Adds the given attribute as an owner. This method is called when a new attribute is added that
172
- # references an existing owner.
173
- #
174
- # @param [Symbol] attribute the owner attribute
175
- def add_owner_attribute(attribute)
176
- attr_md = attribute_metadata(attribute)
177
- otype = attr_md.type
178
- hash = local_owner_attribute_metadata_hash
179
- if hash.include?(otype) then
180
- oattr = hash[otype]
181
- unless oattr.nil? then
182
- raise MetadataError.new("Cannot set #{qp} owner attribute to #{attribute} since it is already set to #{oattr}")
183
- end
184
- hash[otype] = attr_md
185
- else
186
- add_owner(otype, attr_md.inverse, attribute)
187
- end
188
- end
189
-
190
- # @return [{Class => Attribute}] this class's owner type => attribute hash
191
- def owner_attribute_metadata_hash
192
- @oa_hash ||= create_owner_attribute_metadata_hash
193
- end
194
-
195
- private
196
-
197
- def local_owner_attribute_metadata_hash
198
- @local_oa_hash ||= {}
199
- end
200
-
201
- # @return [{Class => Attribute}] a new owner type => attribute hash
202
- def create_owner_attribute_metadata_hash
203
- local = local_owner_attribute_metadata_hash
204
- superclass < Resource ? local.union(superclass.owner_attribute_metadata_hash) : local
205
- end
206
-
207
- # @return [<Attribute>] the owner attributes
208
- def owner_attribute_metadata_enumerator
209
- # Enumerate each owner Attribute, filtering out nil values.
210
- @oa_enum ||= Enumerable::Enumerator.new(owner_attribute_metadata_hash, :each_value).filter
211
- end
212
-
213
- # Returns the attribute which references the owner. The owner attribute is the inverse
214
- # of the given owner class inverse attribute, if it exists. Otherwise, the owner
215
- # attribute is inferred by #{Inverse#detect_inverse_attribute}.
216
-
217
- # @param klass (see #add_owner)
218
- # @param [Symbol] inverse the owner -> dependent attribute
219
- # @return [Symbol, nil] this class's owner attribute
220
- def detect_owner_attribute(klass, inverse)
221
- klass.attribute_metadata(inverse).inverse or detect_inverse_attribute(klass)
222
- end
223
- end
224
- end
225
- end
@@ -1,22 +0,0 @@
1
- module CaRuby
2
- # Mix-in for Java classes which have an +id+ property.
3
- # Since +id+ is a reserved Ruby method, this mix-in defines an +identifier+ attribute
4
- # which fronts the +id+ property.
5
- module IdAlias
6
- # Returns the identifier.
7
- # This method delegates to the Java +id+ property reader method.
8
- #
9
- # @return [Integer] the identifier value
10
- def identifier
11
- getId
12
- end
13
-
14
- # Sets the identifier to the given value.
15
- # This method delegates to the Java +id+ property writer method.
16
- #
17
- # @param [Integer] value the value to set
18
- def identifier=(value)
19
- setId(value)
20
- end
21
- end
22
- end
@@ -1,183 +0,0 @@
1
- require 'caruby/domain/metadata'
2
- require 'caruby/resource'
3
-
4
- module CaRuby
5
- module Domain
6
- # Importer extends a {Module} with Java class import support.
7
- #
8
- # A Java class is imported into JRuby on demand by referencing the class name.
9
- # Import on demand is induced by a reference to the class, e.g., given the
10
- # following domain resource module definition:
11
- # module ClinicalTrials
12
- # module Resource
13
- # ...
14
- # end
15
- #
16
- # CaRuby::Domain.extend_module(self, Resource, 'org.nci.ctms')
17
- # then the first reference by name to +ClinicalTrials::Subject+
18
- # imports the Java class +org.nci.ctms.Subject+ into the JRuby class wrapper
19
- # +ClinicalTrials::Subject+. The +ClinicalTrials::Resource+ module is included
20
- # in +ClinicalTrials::Subject+ and the Java property meta-data is introspected
21
- # into {Attributes}.
22
- module Importer
23
- # Extends the given module with Java class meta-data import support.
24
- #
25
- # @param [Module] mod the module to extend
26
- # @param [{Symbol => Object}] opts the extension options
27
- # @option opts (see #configure)
28
- def self.extend_module(mod, opts)
29
- mod.extend(self).configure_importer(opts)
30
- end
31
-
32
- # Imports a Java class constant on demand. If the class does not already
33
- # include this module's mixin, then the mixin is included in the class.
34
- #
35
- # @param [Symbol] symbol the missing constant
36
- # @return [Class] the imported class
37
- # @raise [NameError] if the symbol is not an importable Java class
38
- def const_missing(symbol)
39
- logger.debug { "Detecting whether #{symbol} is a #{@pkg} Java class..." }
40
- # Append the symbol to the package to make the Java class name.
41
- begin
42
- klass = java_import "#{@pkg}.#{symbol}"
43
- resource_import(klass)
44
- rescue NameError
45
- logger.debug { "#{symbol} is not recognized as a #{@pkg} Java class - #{$!}\n#{caller.qp}." }
46
- super
47
- end
48
- logger.info(klass.pp_s)
49
- klass
50
- end
51
-
52
- # Imports the given Java class and introspects the {Metadata}.
53
- # The Java class is assumed to be defined in this module's package.
54
- # This module's mixin is added to the class.
55
- #
56
- # @param [Class] klass the source directory
57
- # @raise [NameError] if the symbol does not correspond to a Java class
58
- # in this module's package
59
- def resource_import(klass)
60
- # Add the superclass metadata, if necessary.
61
- sc = klass.superclass
62
- unless sc < @mixin or klass.parent_module != sc.parent_module then
63
- const_get(sc.name.demodulize)
64
- end
65
- java_import(klass)
66
- ensure_metadata_introspected(klass)
67
- klass
68
- end
69
-
70
- # @param [Class, String] class_or_name the class to import into this module
71
- # @return [Class] the imported class
72
- def java_import(class_or_name)
73
- # JRuby 1.4.x does not support a class argument
74
- begin
75
- Class === class_or_name ? super(class_or_name.java_class.name) : super
76
- rescue Exception
77
- raise JavaImportError.new("#{class_or_name} is not a Java class - #{$!}")
78
- end
79
- end
80
-
81
- # Configures this importer with the given options. This method is intended for use by the
82
- # +extend_module+ method.
83
- #
84
- # @param [{Symbol => Object}] opts the extension options
85
- # @option opts [String] :package the required Java package name
86
- # @option opts [Module, Proc] :metadata the optional {Metadata} extension module or proc (default {Metadata})
87
- # @option opts [Module] :mixin the optional mix-in module (default {Resource})
88
- # @option opts [String] :directory the optional directory of source class definitions to load
89
- def configure_importer(opts)
90
- @pkg = opts[:package]
91
- if @pkg.nil? then raise ArgumentError.new("Required domain package option not found") end
92
- @metadata = opts[:metadata] || Metadata
93
- @mixin = opts[:mixin] || Resource
94
- @introspected = Set.new
95
- dir = opts[:directory]
96
- load_dir(dir) if dir
97
- end
98
-
99
- private
100
-
101
- # Enables the given class {Metadata} if necessary.
102
- #
103
- # @param [Class] klass the class to enable
104
- def ensure_metadata_introspected(klass)
105
- add_metadata(klass) unless @introspected.include?(klass)
106
- end
107
-
108
- # Enables the given class meta-data.
109
- #
110
- # @param [Class] klass the class to enable
111
- def add_metadata(klass)
112
- # Mark the class as introspected. Do this first to preclude a recursive loop back
113
- # into this method when the references are introspected in add_metadata.
114
- @introspected << klass
115
- # the package module
116
- mod = klass.parent_module
117
- # Add the superclass metadata, if necessary.
118
- sc = klass.superclass
119
- unless @introspected.include?(sc) or sc.parent_module != mod then
120
- resource_import(sc)
121
- end
122
- # Include the mixin.
123
- unless klass < @mixin then
124
- mixin = @mixin
125
- klass.class_eval { include mixin }
126
- end
127
- # Add the class metadata.
128
- case @metadata
129
- when Module then klass.extend(@metadata)
130
- when Proc then @metadata.call(klass)
131
- else raise MetadataError.new("#{self} metadata is neither a class nor a proc: #{@metadata.qp}")
132
- end
133
- klass.domain_module = self
134
- # Add referenced domain class metadata as necessary.
135
- klass.each_attribute_metadata do |attr_md|
136
- ref = attr_md.type
137
- if ref.nil? then raise MetadataError.new("#{self} #{attr_md} domain type is unknown.") end
138
- unless @introspected.include?(ref) or ref.parent_module != mod then
139
- logger.debug { "Adding #{qp} #{attr_md} reference #{ref.qp} metadata..." }
140
- resource_import(ref)
141
- end
142
- end
143
- end
144
-
145
- # Loads the Ruby source files in the given directory.
146
- #
147
- # @param [String] dir the source directory
148
- def load_dir(dir)
149
- # Auto-load the files on demand.
150
- syms = autoload_dir(dir)
151
- # Load each file on demand.
152
- syms.each do |sym|
153
- klass = const_get(sym)
154
- logger.info(klass.pp_s)
155
- end
156
- end
157
-
158
- # Auto-loads the Ruby source files in the given directory.
159
- #
160
- # @param [String] dir the source directory
161
- # @return [<Symbol>] the class constants that will be loaded
162
- def autoload_dir(dir)
163
- # the domain class definitions
164
- srcs = Dir.glob(File.join(dir, "*.rb"))
165
- # autoload the domain classes to ensure that definitions are picked up on demand in class hierarchy order
166
- srcs.map do |file|
167
- base_name = File.basename(file, ".rb")
168
- sym = base_name.camelize.to_sym
169
- # JRuby autoload of classes defined in a submodule of a Java wrapper class is not supported.
170
- # However, this only occurs with the caTissue Specimen Pathology annotation class definitions,
171
- # not the caTissue Participant or SCG annotations. TODO - confirm, isolate and report.
172
- # Work-around is to require the files instead.
173
- if name[/^Java::/] then
174
- require file
175
- else
176
- autoload(sym, file)
177
- end
178
- sym
179
- end
180
- end
181
- end
182
- end
183
- end
@@ -1,176 +0,0 @@
1
- require 'caruby/util/module'
2
- require 'caruby/import/java'
3
- require 'caruby/domain/java_attribute'
4
-
5
- module CaRuby
6
- module Domain
7
- # Meta-data mix-in to infer attribute meta-data from Java properties.
8
- module Introspection
9
-
10
- protected
11
-
12
- # @return [Boolean] whether this {Resource} class meta-data has been introspected
13
- def introspected?
14
- # initialization sets the attribute => metadata hash
15
- not @attr_md_hash.nil?
16
- end
17
-
18
- # Defines the Java property attribute and standard attribute methods, e.g.
19
- # +study_protocol+ and +studyProtocol+. A boolean attribute is provisioned
20
- # with an additional reader alias, e.g. +available?+ for +is_available+.
21
- #
22
- # Each Java property attribute delegates to the Java property getter and setter.
23
- # Each standard attribute delegates to the Java property attribute.
24
- # Redefining these methods results in a call to the redefined method.
25
- # This contrasts with a Ruby alias, where the alias remains bound to the
26
- # original method body.
27
- def introspect
28
- # the module corresponding to the Java package of this class
29
- mod = parent_module
30
- # Set up the attribute data structures; delegates to Attributes.
31
- init_attributes
32
- logger.debug { "Introspecting #{qp} metadata..." }
33
- # The Java properties defined by this class with both a read and a write method.
34
- pds = java_properties(false)
35
- # Define the standard Java attribute methods.
36
- pds.each { |pd| define_java_attribute(pd) }
37
- logger.debug { "Introspection of #{qp} metadata complete." }
38
- self
39
- end
40
-
41
- private
42
-
43
- # Defines the Java property attribute and standard attribute methods, e.g.
44
- # +study_protocol+ and +studyProtocol+. A boolean attribute is provisioned
45
- # with an additional reader alias, e.g. +available?+ for +is_available+.
46
- #
47
- # A standard attribute which differs from the property attribute delegates
48
- # to the property attribute, e.g. +study_protocol+ delegates to +studyProtocol+
49
- # rather than aliasing +setStudyProtocol+. Redefining these methods results
50
- # in a call to the redefined method. This contrasts with a Ruby alias,
51
- # where each attribute alias is bound to the respective property reader or
52
- # writer.
53
- def define_java_attribute(pd)
54
- if transient?(pd) then
55
- logger.debug { "Ignoring #{name.demodulize} transient property #{pd.name}." }
56
- return
57
- end
58
- # the standard underscore lower-case attributes
59
- attr = create_java_attribute(pd)
60
- # delegate the standard attribute accessors to the property accessors
61
- alias_attribute_property(attr, pd.name)
62
- # add special wrappers
63
- wrap_java_attribute(attr, pd)
64
- # create Ruby alias for boolean, e.g. alias :empty? for :empty
65
- if pd.property_type.name[/\w+$/].downcase == 'boolean' then
66
- # strip leading is_, if any, before appending question mark
67
- aliaz = attr.to_s[/^(is_)?(\w+)/, 2] << '?'
68
- delegate_to_attribute(aliaz, attr)
69
- end
70
- end
71
-
72
- # Adds a filter to the attribute access method for the property descriptor pd if it is a String or Date.
73
- def wrap_java_attribute(attribute, pd)
74
- if pd.property_type == Java::JavaLang::String.java_class then
75
- wrap_java_string_attribute(attribute, pd)
76
- elsif pd.property_type == Java::JavaUtil::Date.java_class then
77
- wrap_java_date_attribute(attribute, pd)
78
- end
79
- end
80
-
81
- # Adds a to_s filter to this Class's String property access methods.
82
- def wrap_java_string_attribute(attribute, pd)
83
- # filter the attribute writer
84
- awtr = "#{attribute}=".to_sym
85
- pwtr = pd.write_method.name.to_sym
86
- define_method(awtr) do |value|
87
- stdval = value.to_s unless value.nil_or_empty?
88
- send(pwtr, stdval)
89
- end
90
- logger.debug { "Filtered #{qp} #{awtr} method with non-String -> String converter." }
91
- end
92
-
93
- # Adds a date parser filter to this Class's Date property access methods.
94
- def wrap_java_date_attribute(attribute, pd)
95
- # filter the attribute reader
96
- prdr = pd.read_method.name.to_sym
97
- define_method(attribute) do
98
- value = send(prdr)
99
- Java::JavaUtil::Date === value ? value.to_ruby_date : value
100
- end
101
-
102
- # filter the attribute writer
103
- awtr = "#{attribute}=".to_sym
104
- pwtr = pd.write_method.name.to_sym
105
- define_method(awtr) do |value|
106
- value = Java::JavaUtil::Date.from_ruby_date(value) if ::Date === value
107
- send(pwtr, value)
108
- end
109
-
110
- logger.debug { "Filtered #{qp} #{attribute} and #{awtr} methods with Java Date <-> Ruby Date converter." }
111
- end
112
-
113
- # Aliases the methods _aliaz_ and _aliaz=_ to _property_ and _property=_, resp.,
114
- # where _property_ is the Java property name for the attribute.
115
- def alias_attribute_property(aliaz, attribute)
116
- # strip the Java reader and writer is/get/set prefix and make a symbol
117
- prdr, pwtr = attribute_metadata(attribute).property_accessors
118
- alias_method(aliaz, prdr)
119
- writer = "#{aliaz}=".to_sym
120
- alias_method(writer, pwtr)
121
- end
122
-
123
- # Makes a standard attribute for the given property descriptor.
124
- # Adds a camelized Java-like alias to the standard attribute.
125
- #
126
- # @quirk caTissue DE annotation collection attributes are often misnamed,
127
- # e.g. +histologic_grade+ for a +HistologicGrade+ collection attribute.
128
- # This is fixed by adding a pluralized alias, e.g. +histologic_grades+.
129
- #
130
- # @return a new attribute symbol created for the given PropertyDescriptor pd
131
- def create_java_attribute(pd)
132
- # make the attribute metadata
133
- attr_md = JavaAttribute.new(pd, self)
134
- add_attribute_metadata(attr_md)
135
- # the property name is an alias for the standard attribute
136
- std_attr = attr_md.to_sym
137
- prop_attr = pd.name.to_sym
138
- delegate_to_attribute(prop_attr, std_attr) unless prop_attr == std_attr
139
-
140
- # alias a misnamed collection attribute, if necessary
141
- if attr_md.collection? then
142
- name = std_attr.to_s
143
- if name.singularize == name then
144
- aliaz = name.pluralize.to_sym
145
- if aliaz != name then
146
- logger.debug { "Adding annotation #{qp} alias #{aliaz} to the misnamed collection attribute #{std_attr}..." }
147
- delegate_to_attribute(aliaz, std_attr)
148
- end
149
- end
150
- end
151
-
152
- std_attr
153
- end
154
-
155
- # Defines methods _aliaz_ and _aliaz=_ which calls the standard _attribute_ and
156
- # _attribute=_ accessor methods, resp.
157
- # Calling rather than aliasing the attribute accessor allows the aliaz accessor to
158
- # reflect a change to the attribute accessor.
159
- def delegate_to_attribute(aliaz, attribute)
160
- if aliaz == attribute then raise MetadataError.new("Cannot delegate #{self} #{aliaz} to itself.") end
161
- rdr, wtr = attribute_metadata(attribute).accessors
162
- define_method(aliaz) { send(rdr) }
163
- define_method("#{aliaz}=".to_sym) { |value| send(wtr, value) }
164
- add_alias(aliaz, attribute)
165
- end
166
-
167
- # Makes a new synthetic attribute for each _method_ => _original_ hash entry.
168
- #
169
- # @param (see Class#offset_attr_accessor)
170
- def offset_attribute(hash, offset=nil)
171
- offset_attr_accessor(hash, offset)
172
- hash.each { |attr, original| add_attribute(attr, attribute_metadata(original).type) }
173
- end
174
- end
175
- end
176
- end