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,628 +0,0 @@
1
- require 'enumerator'
2
- require 'caruby/util/collection'
3
- require 'caruby/domain/merge'
4
- require 'caruby/domain/attribute'
5
-
6
- module CaRuby
7
- module Domain
8
- # Meta-data mix-in for attribute accessors.
9
- module Attributes
10
- # @return [<Symbol>] this class's attributes
11
- attr_reader :attributes
12
-
13
- # @return [Hashable] the default attribute => value associations
14
- attr_reader :defaults
15
-
16
- # Returns whether this class has an attribute with the given symbol.
17
- #
18
- # @param [Symbol] symbol the potential attribute
19
- # @return [Boolean] whether there is a corresponding attribute
20
- def attribute_defined?(symbol)
21
- unless Symbol === symbol then
22
- raise ArgumentError.new("Attribute argument #{symbol.qp} of type #{symbol.class.qp} is not a symbol")
23
- end
24
- @attr_md_hash.has_key?(symbol)
25
- end
26
-
27
- # Adds the given attribute to this Class.
28
- #
29
- # @param [Symbol] attribute the attribute to add
30
- # @param [Class] type (see Attribute#initialize)
31
- # @param flags (see Attribute#initialize)
32
- # @return [Attribute] the attribute meta-data
33
- def add_attribute(attribute, type, *flags)
34
- attr_md = Attribute.new(attribute, self, type, *flags)
35
- add_attribute_metadata(attr_md)
36
- attr_md
37
- end
38
-
39
- # @return [(Symbol)] the +[:identifier]+ primary key attribute singleton array
40
- def primary_key_attributes
41
- IDENTIFIER_ATTR_ARRAY
42
- end
43
-
44
- # Returns this class's secondary key attribute array.
45
- # If this class's secondary key is not set, then the secondary key is the Metadata superclass
46
- # secondary key, if any.
47
- #
48
- # @return [<Symbol>] the secondary key attributes
49
- def secondary_key_attributes
50
- @scndy_key_attrs or superclass < Resource ? superclass.secondary_key_attributes : Array::EMPTY_ARRAY
51
- end
52
-
53
- # Returns this class's alternate key attribute array.
54
- # If this class's secondary key is not set, then the alternate key is the {Metadata} superclass
55
- # alternate key, if any.
56
- #
57
- # @return [<Symbol>] the alternate key attributes
58
- def alternate_key_attributes
59
- @alt_key_attrs or superclass < Resource ? superclass.alternate_key_attributes : Array::EMPTY_ARRAY
60
- end
61
-
62
- def each_attribute_metadata(&block)
63
- @attr_md_hash.each_value(&block)
64
- end
65
-
66
- # @return the Attribute for the given attribute symbol or alias
67
- # @raise [NameError] if the attribute is not recognized
68
- def attribute_metadata(attribute)
69
- # Simple and predominant case is that attribute is a standard attribute.
70
- # Otherwise, resolve attribute to the standard symbol.
71
- attr_md = @attr_md_hash[attribute] || @attr_md_hash[standard_attribute(attribute)]
72
- # If not found, then delegate to handler which will either make the new
73
- # attribute or raise a NameError.
74
- attr_md || (attribute_missing(attribute) && @local_attr_md_hash[attribute])
75
- end
76
-
77
- # @param [Symbol, String] name_or_alias the attribute name or alias
78
- # @return [Symbol] the standard attribute symbol for the given name or alias
79
- # @raise [ArgumentError] if the attribute name or alias argument is missing
80
- # @raise [NameError] if the attribute is not found
81
- def standard_attribute(name_or_alias)
82
- if name_or_alias.nil? then
83
- raise ArgumentError.new("#{qp} standard attribute call is missing the attribute name/alias parameter")
84
- end
85
- @alias_std_attr_map[name_or_alias.to_sym] or raise NameError.new("#{self} attribute not found: #{name_or_alias}")
86
- end
87
-
88
- ## Metadata ATTRIBUTE FILTERS ##
89
-
90
- # @return [<Symbol>] the domain attributes which wrap a java property
91
- # @see Attribute#java_property?
92
- def java_attributes
93
- @java_attrs ||= attribute_filter { |attr_md| attr_md.java_property? }
94
- end
95
-
96
- alias :printable_attributes :java_attributes
97
-
98
- # @return [<Symbol>] the domain attributes
99
- def domain_attributes
100
- @dom_attrs ||= attribute_filter { |attr_md| attr_md.domain? }
101
- end
102
-
103
- # @return [<Symbol>] the non-domain Java attributes
104
- def nondomain_attributes
105
- @nondom_attrs ||= attribute_filter { |attr_md| attr_md.java_property? and attr_md.nondomain? }
106
- end
107
-
108
- # @return [<Symbol>] the non-domain Java property wrapper attributes
109
- def nondomain_java_attributes
110
- @nondom_java_attrs ||= nondomain_attributes.compose { |attr_md| attr_md.java_property? }
111
- end
112
-
113
- # @return [<Symbol>] the standard attributes which can be merged into an instance of the subject class.
114
- # The default mergeable attributes consist of the {#nondomain_java_attributes}.
115
- # @see Mergeable#mergeable_attributes
116
- alias :mergeable_attributes :nondomain_java_attributes
117
-
118
- # @param [Boolean, nil] inc_super flag indicating whether to include dependents defined in the superclass
119
- # @return [<Symbol>] the dependent attributes
120
- def dependent_attributes(inc_super=true)
121
- if inc_super then
122
- @dep_attrs ||= attribute_filter { |attr_md| attr_md.dependent? }
123
- else
124
- @local_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.declarer == self }
125
- end
126
- end
127
-
128
- # @return [<Symbol>] the dependent attributes
129
- def autogenerated_dependent_attributes
130
- @ag_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.autogenerated? }
131
- end
132
-
133
- # @return [<Symbol>] the autogenerated logical dependent attributes
134
- # @see #logical_dependent_attributes
135
- # @see Attribute#autogenerated?
136
- def autogenerated_logical_dependent_attributes
137
- @ag_log_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.autogenerated? and attr_md.logical? }
138
- end
139
-
140
- # @return [<Symbol>] the {Attribute#saved_fetch?} attributes
141
- def saved_fetch_attributes
142
- @svd_ftch_attrs ||= domain_attributes.compose { |attr_md| attr_md.saved_fetch? }
143
- end
144
-
145
- # @return [<Symbol>] the logical dependent attributes
146
- # @see Attribute#logical?
147
- def logical_dependent_attributes
148
- @log_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.logical? }
149
- end
150
-
151
- # @return [<Symbol>] the unidirectional dependent attributes
152
- # @see Attribute#unidirectional?
153
- def unidirectional_dependent_attributes
154
- @uni_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.unidirectional? }
155
- end
156
-
157
- # @return [<Symbol>] the auto-generated attributes
158
- # @see Attribute#autogenerated?
159
- def autogenerated_attributes
160
- @ag_attrs ||= attribute_filter { |attr_md| attr_md.autogenerated? }
161
- end
162
-
163
- # @return [<Symbol>] the auto-generated non-domain attributes
164
- # @see Attribute#nondomain?
165
- # @see Attribute#autogenerated?
166
- def autogenerated_nondomain_attributes
167
- @ag_nd_attrs ||= attribute_filter { |attr_md| attr_md.autogenerated? and attr_md.nondomain? }
168
- end
169
-
170
- # @return [<Symbol>] the {Attribute#volatile?} non-domain attributes
171
- def volatile_nondomain_attributes
172
- @unsvd_nd_attrs ||= attribute_filter { |attr_md| attr_md.volatile? and attr_md.nondomain? }
173
- end
174
-
175
- # @return [<Symbol>] the domain attributes which can serve as a query parameter
176
- # @see Attribute#searchable?
177
- def searchable_attributes
178
- @srchbl_attrs ||= attribute_filter { |attr_md| attr_md.searchable? }
179
- end
180
-
181
- # @return [<Symbol>] the create/update cascaded domain attributes
182
- # @see Attribute#cascaded?
183
- def cascaded_attributes
184
- @cscd_attrs ||= domain_attributes.compose { |attr_md| attr_md.cascaded? }
185
- end
186
-
187
- # @return [<Symbol>] the {#cascaded_attributes} which are saved with a proxy
188
- # using the dependent saver_proxy method
189
- def proxied_savable_template_attributes
190
- @px_cscd_attrs ||= savable_template_attributes.compose { |attr_md| attr_md.proxied_save? }
191
- end
192
-
193
- # @return [<Symbol>] the {#cascaded_attributes} which do not have a
194
- # #{Attribute#proxied_save?}
195
- def unproxied_savable_template_attributes
196
- @unpx_sv_tmpl_attrs ||= savable_template_attributes.compose { |attr_md| not attr_md.proxied_save? }
197
- end
198
-
199
- # @return [<Symbol>] the {#domain_attributes} to {Attribute#include_in_save_template?}
200
- def savable_template_attributes
201
- @sv_tmpl_attrs ||= domain_attributes.compose { |attr_md| attr_md.include_in_save_template? }
202
- end
203
-
204
- # Returns the physical or auto-generated logical dependent attributes that can
205
- # be copied from a save result to the given save argument object.
206
- #
207
- # @return [<Symbol>] the attributes that can be copied from a save result to a
208
- # save argument object
209
- # @see Attribute#autogenerated?
210
- def copyable_saved_attributes
211
- @cp_sv_attrs ||= dependent_attributes.compose { |attr_md| attr_md.autogenerated? or not attr_md.logical? }
212
- end
213
-
214
- # Returns the subject class's required attributes, determined as follows:
215
- # * An attribute marked with the :mandatory flag is mandatory.
216
- # * An attribute marked with the :optional or :autogenerated flag is not mandatory.
217
- # * Otherwise, A secondary key or owner attribute is mandatory.
218
- def mandatory_attributes
219
- @mndtry_attrs ||= collect_mandatory_attributes
220
- end
221
-
222
- # @return [<Symbol>] the attributes which are {Attribute#creatable?}
223
- def creatable_attributes
224
- @cr_attrs ||= attribute_filter { |attr_md| attr_md.creatable? }
225
- end
226
-
227
- # @return [<Symbol>] the attributes which are {Attribute#updatable?}
228
- def updatable_attributes
229
- @upd_attrs ||= attribute_filter { |attr_md| attr_md.updatable? }
230
- end
231
-
232
- def fetched_dependent_attributes
233
- @ftchd_dep_attrs ||= (fetched_domain_attributes & dependent_attributes).to_a
234
- end
235
-
236
- def nonowner_attributes
237
- @nownr_atts ||= attribute_filter { |attr_md| not attr_md.owner? }
238
- end
239
-
240
- # @return [<Symbol>] the saved dependent attributes
241
- # @see Attribute#dependent?
242
- # @see Attribute#saved?
243
- def saved_dependent_attributes
244
- @svd_dep_attrs ||= attribute_filter { |attr_md| attr_md.dependent? and attr_md.saved? }
245
- end
246
-
247
- # @return [<Symbol>] the saved independent attributes
248
- # @see Attribute#independent?
249
- # @see Attribute#saved?
250
- def saved_independent_attributes
251
- @svd_ind_attrs ||= attribute_filter { |attr_md| attr_md.independent? and attr_md.saved? }
252
- end
253
-
254
- # @return [<Symbol>] the domain {Attribute#saved?} attributes
255
- def saved_domain_attributes
256
- @svd_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.saved? }
257
- end
258
-
259
- # @return [<Symbol>] the non-domain {Attribute#saved?} attributes
260
- def saved_nondomain_attributes
261
- @svd_nondom_attrs ||= nondomain_attributes.compose { |attr_md| attr_md.saved? }
262
- end
263
-
264
- # @return [<Symbol>] the {Attribute#volatile?} {#nondomain_attributes}
265
- def volatile_nondomain_attributes
266
- @vlt_nondom_attrs ||= nondomain_attributes.compose { |attr_md| attr_md.volatile? }
267
- end
268
-
269
- # @return [<Symbol>] the domain {#creatable_attributes}
270
- def creatable_domain_attributes
271
- @cr_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.creatable? }
272
- end
273
-
274
- # @return [<Symbol>] the domain {#updatable_attributes}
275
- def updatable_domain_attributes
276
- @upd_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.updatable? }
277
- end
278
-
279
- # @return [<Symbol>] the domain attributes whose referents must exist before an instance of this
280
- # metadata's subject class can be created
281
- # @see Attribute#storable_prerequisite?
282
- def storable_prerequisite_attributes
283
- @stbl_prereq_dom_attrs ||= attribute_filter { |attr_md| attr_md.storable_prerequisite? }
284
- end
285
-
286
- # @return [<Symbol>] the attributes which are populated from the database
287
- # @see Attribute#fetched?
288
- def fetched_attributes
289
- @ftchd_attrs ||= attribute_filter { |attr_md| attr_md.fetched? }
290
- end
291
-
292
- # Returns the domain attributes which are populated in a query on the given fetched instance of
293
- # this metadata's subject class. The domain attribute is fetched if it satisfies the following
294
- # conditions:
295
- # * the attribute is a dependent attribute or of abstract domain type
296
- # * the attribute is not specified as unfetched in the configuration
297
- #
298
- # @return [<Symbol>] the attributes which are {Attribute#fetched?}
299
- def fetched_domain_attributes
300
- @ftchd_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.fetched? }
301
- end
302
-
303
- #@return [<Symbol>] the #domain_attributes which are not #fetched_domain_attributes
304
- def unfetched_attributes
305
- @unftchd_attrs ||= domain_attributes.compose { |attr_md| not attr_md.fetched? }
306
- end
307
-
308
- alias :toxic_attributes :unfetched_attributes
309
-
310
- # @return [<Symbol>] the Java property non-abstract {#unfetched_attributes}
311
- def loadable_attributes
312
- @ld_attrs ||= unfetched_attributes.compose { |attr_md| attr_md.java_property? and not attr_md.type.abstract? }
313
- end
314
-
315
- # @param [Symbol] attribute the attribute to check
316
- # @return [Boolean] whether attribute return type is a domain object or collection thereof
317
- def domain_attribute?(attribute)
318
- attribute_metadata(attribute).domain?
319
- end
320
-
321
- # @param [Symbol] attribute the attribute to check
322
- # @return [Boolean] whether attribute is not a domain attribute
323
- def nondomain_attribute?(attribute)
324
- not domain_attribute?(attribute)
325
- end
326
-
327
- # @param [Symbol] attribute the attribute to check
328
- # @return [Boolean] whether attribute is an instance of a Java domain class
329
- def collection_attribute?(attribute)
330
- attribute_metadata(attribute).collection?
331
- end
332
-
333
- protected
334
-
335
- # @return [{Symbol => Attribute}] the attribute => metadata hash
336
- def attribute_metadata_hash
337
- @attr_md_hash
338
- end
339
-
340
- # @return [{Symbol => Symbol}] the attribute alias => standard hash
341
- def alias_standard_attribute_hash
342
- @alias_std_attr_map
343
- end
344
-
345
- private
346
-
347
- IDENTIFIER_ATTR_ARRAY = [:identifier]
348
-
349
- # A filter on the standard attribute symbol => metadata hash that yields
350
- # each attribute which satisfies the attribute metadata condition.
351
- class Filter
352
- include Enumerable
353
-
354
- # @param [Class] the class whose attributes are filtered
355
- # @param [{Symbol => Attribute}] hash the attribute symbol => metadata hash
356
- # @yield [attr_md] condition which determines whether the attribute is selected
357
- # @yieldparam [Attribute] the metadata for the standard attribute
358
- # @raise [ArgumentError] if a parameter is missing
359
- def initialize(klass, hash, &filter)
360
- raise ArgumentError.new("#{klass.qp} attribute filter missing hash argument") if hash.nil?
361
- raise ArgumentError.new("#{klass.qp} attribute filter missing filter block") unless block_given?
362
- @hash = hash
363
- @filter = filter
364
- end
365
-
366
- # @yield [attribute, attr_md] the block to apply to the filtered attribute metadata and attribute
367
- # @yieldparam [Symbol] attribute the attribute
368
- # @yieldparam [Attribute] attr_md the attribute metadata
369
- def each_pair
370
- @hash.each { |attr, attr_md| yield(attr, attr_md) if @filter.call(attr_md) }
371
- end
372
-
373
- # @return [<(Symbol, Attribute)>] the (symbol, attribute) enumerator
374
- def enum_pairs
375
- enum_for(:each_pair)
376
- end
377
-
378
- # @yield [attribute] block to apply to each filtered attribute
379
- # @yieldparam [Symbol] the attribute which satisfies the filter condition
380
- def each_attribute(&block)
381
- each_pair { |attr, attr_md| yield(attr) }
382
- end
383
-
384
- alias :each :each_attribute
385
-
386
- # @yield [attr_md] the block to apply to the filtered attribute metadata
387
- # @yieldparam [Attribute] attr_md the attribute metadata
388
- def each_metadata
389
- each_pair { |attr, attr_md| yield(attr_md) }
390
- end
391
-
392
- # @return [<Attribute>] the attribute metadata enumerator
393
- def enum_metadata
394
- enum_for(:each_metadata)
395
- end
396
-
397
- # @yield [attribute] the block to apply to the attribute
398
- # @yieldparam [Symbol] attribute the attribute
399
- # @return [Attribute] the first attribute metadata satisfies the block
400
- def detect_metadata
401
- each_pair { |attr, attr_md| return attr_md if yield(attr) }
402
- nil
403
- end
404
-
405
- # @yield [attr_md] the block to apply to the attribute metadata
406
- # @yieldparam [Attribute] attr_md the attribute metadata
407
- # @return [Symbol] the first attribute whose metadata satisfies the block
408
- def detect_with_metadata
409
- each_pair { |attr, attr_md| return attr if yield(attr_md) }
410
- nil
411
- end
412
-
413
- # @yield [attr_md] the attribute selection filter
414
- # @yieldparam [Attribute] attr_md the candidate attribute metadata
415
- # @return [Filter] a new Filter which applies the filter block given to this
416
- # method with the Attribute enumerated by this filter
417
- def compose
418
- Filter.new(self, @hash) { |attr_md| @filter.call(attr_md) and yield(attr_md) }
419
- end
420
- end
421
-
422
- # Returns an Enumerable on this Resource class's attributes which iterates on each attribute whose
423
- # corresponding Attribute satisfies the given filter block.
424
- #
425
- # @yield [attr_md] the attribute selector
426
- # @yieldparam [Attribute] attr_md the candidate attribute
427
- def attribute_filter(&filter)
428
- # initialize the attributes on demand
429
- unless introspected? then introspect end
430
- # make the attribute filter
431
- Filter.new(self, @attr_md_hash, &filter)
432
- end
433
-
434
- # Initializes the attribute meta-data structures.
435
- def init_attributes
436
- @local_std_attr_hash = {}
437
- @alias_std_attr_map = append_ancestor_enum(@local_std_attr_hash) { |par| par.alias_standard_attribute_hash }
438
- @local_attr_md_hash = {}
439
- @attr_md_hash = append_ancestor_enum(@local_attr_md_hash) { |par| par.attribute_metadata_hash }
440
- @attributes = Enumerable::Enumerator.new(@attr_md_hash, :each_key)
441
- @local_mndty_attrs = Set.new
442
- @local_defaults = {}
443
- @defaults = append_ancestor_enum(@local_defaults) { |par| par.defaults }
444
- end
445
-
446
- # Detects the first attribute with the given type.
447
- #
448
- # @param [Class] klass the target attribute type
449
- # @return [Symbol, nil] the attribute with the given type
450
- def detect_attribute_with_type(klass)
451
- attribute_metadata_hash.detect_key_with_value { |attr_md| attr_md.type == klass }
452
- end
453
-
454
- # Creates the given attribute alias. If the attribute metadata is registered with this class, then
455
- # this method overrides {Class#alias_attribute} to create a new alias reader (writer) method
456
- # which delegates to the attribute reader (writer, resp.). This aliasing mechanism differs from
457
- # {Class#alias_attribute}, which directly aliases the existing reader or writer method.
458
- # Delegation allows the alias to pick up run-time redefinitions of the aliased reader and writer.
459
- # If the attribute metadata is not registered with this class, then this method delegates to
460
- # {Class#alias_attribute}.
461
- #
462
- # @param [Symbol] aliaz the attribute alias
463
- # @param [Symbol] attribute the attribute to alias
464
- def alias_attribute(aliaz, attribute)
465
- if attribute_defined?(attribute) then
466
- add_attribute_aliases(aliaz => attribute)
467
- else
468
- super
469
- end
470
- end
471
-
472
- # Creates the given aliases to attributes.
473
- #
474
- # @param [{Symbol => Symbol}] hash the alias => attribute hash
475
- # @see #attribute_alias
476
- def add_attribute_aliases(hash)
477
- hash.each { |aliaz, attr| delegate_to_attribute(aliaz, attr) }
478
- end
479
-
480
- # Sets this class's secondary key attributes to the given attributes.
481
- # If attributes is set to nil, then the secondary key is cleared.
482
- def set_secondary_key_attributes(*attributes)
483
- attributes.compact!
484
- @scndy_key_attrs = attributes.map { |attr| standard_attribute(attr) }
485
- end
486
-
487
- # Sets this class's alternate key attributes to the given attributes.
488
- # If attributes is set to nil, then the alternate key is cleared.
489
- def set_alternate_key_attributes(*attributes)
490
- attributes.compact!
491
- @alt_key_attrs = attributes.map { |attr| standard_attribute(attr) }
492
- end
493
-
494
- # Sets the given attribute type to klass. If attribute is defined in a superclass,
495
- # then klass must be a subclass of the superclass attribute type.
496
- #
497
- # Raises ArgumentError if klass is incompatible with the current attribute type.
498
- def set_attribute_type(attribute, klass)
499
- attr_md = attribute_metadata(attribute)
500
- # If this class is the declarer, then simply set the attribute type.
501
- # Otherwise, if the attribute type is unspecified or is a superclass of the given class,
502
- # then make a new attribute metadata for this class.
503
- if attr_md.declarer == self then
504
- logger.debug { "Set #{qp}.#{attribute} type to #{klass.qp}." }
505
- attr_md.type = klass
506
- elsif attr_md.type.nil? or klass < attr_md.type then
507
- new_attr_md = attr_md.restrict_type(self, klass)
508
- logger.debug { "Restricted #{attr_md.declarer.qp}.#{attribute}(#{attr_md.type.qp}) to #{qp} with return type #{klass.qp}." }
509
- add_attribute_metadata(new_attr_md)
510
- elsif klass != attr_md.type then
511
- raise ArgumentError.new("Cannot reset #{qp}.#{attribute} type #{attr_md.type} to incompatible #{klass.qp}")
512
- end
513
- end
514
-
515
- def add_attribute_defaults(hash)
516
- hash.each { |attr, value| @local_defaults[standard_attribute(attr)] = value }
517
- end
518
-
519
- def add_mandatory_attributes(*attributes)
520
- attributes.each { |attr| @local_mndty_attrs << standard_attribute(attr) }
521
- end
522
-
523
- # Marks the given attribute with flags supported by {Attribute#qualify}.
524
- def qualify_attribute(attribute, *flags)
525
- attr_md = attribute_metadata(attribute)
526
- if attr_md.declarer == self then
527
- attr_md.qualify(*flags)
528
- else
529
- logger.debug { "Restricting #{attr_md.declarer.qp}.#{attribute} to #{qp} with additional flags #{flags.to_series}" }
530
- new_attr_md = attr_md.restrict_flags(self, *flags)
531
- add_attribute_metadata(new_attr_md)
532
- end
533
- end
534
-
535
- # Removes the given attribute from this Resource.
536
- # An attribute declared in a superclass Resource is hidden from this Resource but retained in
537
- # the declaring Resource.
538
- def remove_attribute(attribute)
539
- std_attr = standard_attribute(attribute)
540
- # if the attribute is local, then delete it, otherwise filter out the superclass attribute
541
- attr_md = @local_attr_md_hash.delete(std_attr)
542
- if attr_md then
543
- # clear the inverse, if any
544
- attr_md.inverse = nil
545
- # remove from the mandatory attributes, if necessary
546
- @local_mndty_attrs.delete(std_attr)
547
- # remove from the attribute => metadata hash
548
- @local_std_attr_hash.delete_if { |aliaz, attr| attr == std_attr }
549
- else
550
- # Filter the superclass hashes.
551
- anc_md_hash = @attr_md_hash.components[1]
552
- @attr_md_hash.components[1] = anc_md_hash.filter_on_key { |attr| attr != attribute }
553
- anc_alias_hash = @alias_std_attr_map.components[1]
554
- @alias_std_attr_map.components[1] = anc_alias_hash.filter_on_key { |attr| attr != attribute }
555
- end
556
- end
557
-
558
- def add_attribute_metadata(attr_md)
559
- attr = attr_md.to_sym
560
- @local_attr_md_hash[attr] = attr_md
561
- # map the attribute symbol to itself in the alias map
562
- @local_std_attr_hash[attr] = attr
563
- end
564
-
565
- # Records that the given aliaz aliases a standard attribute.
566
- def add_alias(aliaz, attribute)
567
- std_attr = standard_attribute(attribute)
568
- raise ArgumentError.new("#{self} attribute not found: #{attribute}") if std_attr.nil?
569
- @local_std_attr_hash[aliaz.to_sym] = std_attr
570
- end
571
-
572
- # Appends to the given enumerable the result of evaluating the block given to this method
573
- # on the superclass, if the superclass is in the same parent module as this class.
574
- #
575
- # @param [Enumerable] enum the base collection
576
- # @return [Enumerable] the {Enumerable#union} of the base collection with the superclass
577
- # collection, if applicable
578
- def append_ancestor_enum(enum)
579
- return enum unless superclass.parent_module == parent_module
580
- anc_enum = yield superclass
581
- if anc_enum.nil? then raise MetadataError.new("#{qp} superclass #{superclass.qp} does not have required metadata") end
582
- enum.union(anc_enum)
583
- end
584
-
585
- # Collects the {Attribute#fetched_dependent?} and {Attribute#fetched_independent?}
586
- # standard domain attributes.
587
- #
588
- # @return [<Symbol>] the fetched attributes
589
- def collect_default_fetched_domain_attributes
590
- attribute_filter do |attr_md|
591
- if attr_md.domain? then
592
- attr_md.dependent? ? fetched_dependent?(attr_md) : fetched_independent?(attr_md)
593
- end
594
- end
595
- end
596
-
597
- # Merges the secondary key, owner and additional mandatory attributes defined in the properties.
598
- #
599
- # @see #mandatory_attributes
600
- def collect_mandatory_attributes
601
- mandatory = Set.new
602
- # add the secondary key
603
- mandatory.merge(secondary_key_attributes)
604
- # add the owner attribute, if any
605
- oattr = mandatory_owner_attribute
606
- mandatory << oattr if oattr
607
- # remove autogenerated or optional attributes
608
- mandatory.delete_if { |attr| attribute_metadata(attr).autogenerated? or attribute_metadata(attr).optional? }
609
- @local_mndty_attrs.merge!(mandatory)
610
- append_ancestor_enum(@local_mndty_attrs) { |par| par.mandatory_attributes }
611
- end
612
-
613
- # @return [Symbol, nil] the unique non-self-referential owner attribute, if one exists
614
- def mandatory_owner_attribute
615
- attr = owner_attribute || return
616
- attr_md = attribute_metadata(attr)
617
- attr if attr_md.java_property? and attr_md.type != self
618
- end
619
-
620
- # Raises a NameError. Domain classes can override this method to dynamically create a new reference attribute.
621
- #
622
- # @raise [NameError] always
623
- def attribute_missing(attribute)
624
- raise NameError.new("#{name.demodulize} attribute not found: #{attribute}")
625
- end
626
- end
627
- end
628
- end