caruby-core 1.4.9 → 1.5.1

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