caruby-core 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. data/History.txt +4 -0
  2. data/LEGAL +5 -0
  3. data/LICENSE +22 -0
  4. data/README.md +51 -0
  5. data/doc/website/css/site.css +1 -5
  6. data/doc/website/images/avatar.png +0 -0
  7. data/doc/website/images/favicon.ico +0 -0
  8. data/doc/website/images/logo.png +0 -0
  9. data/doc/website/index.html +82 -0
  10. data/doc/website/install.html +87 -0
  11. data/doc/website/quick_start.html +87 -0
  12. data/doc/website/tissue.html +85 -0
  13. data/doc/website/uom.html +10 -0
  14. data/lib/caruby.rb +3 -0
  15. data/lib/caruby/active_support/README.txt +2 -0
  16. data/lib/caruby/active_support/core_ext/string.rb +7 -0
  17. data/lib/caruby/active_support/core_ext/string/inflections.rb +167 -0
  18. data/lib/caruby/active_support/inflections.rb +55 -0
  19. data/lib/caruby/active_support/inflector.rb +398 -0
  20. data/lib/caruby/cli/application.rb +36 -0
  21. data/lib/caruby/cli/command.rb +169 -0
  22. data/lib/caruby/csv/csv_mapper.rb +157 -0
  23. data/lib/caruby/csv/csvio.rb +185 -0
  24. data/lib/caruby/database.rb +252 -0
  25. data/lib/caruby/database/fetched_matcher.rb +66 -0
  26. data/lib/caruby/database/persistable.rb +432 -0
  27. data/lib/caruby/database/persistence_service.rb +162 -0
  28. data/lib/caruby/database/reader.rb +599 -0
  29. data/lib/caruby/database/saved_merger.rb +131 -0
  30. data/lib/caruby/database/search_template_builder.rb +59 -0
  31. data/lib/caruby/database/sql_executor.rb +75 -0
  32. data/lib/caruby/database/store_template_builder.rb +200 -0
  33. data/lib/caruby/database/writer.rb +469 -0
  34. data/lib/caruby/domain/annotatable.rb +25 -0
  35. data/lib/caruby/domain/annotation.rb +23 -0
  36. data/lib/caruby/domain/attribute_metadata.rb +447 -0
  37. data/lib/caruby/domain/java_attribute_metadata.rb +160 -0
  38. data/lib/caruby/domain/merge.rb +91 -0
  39. data/lib/caruby/domain/properties.rb +95 -0
  40. data/lib/caruby/domain/reference_visitor.rb +289 -0
  41. data/lib/caruby/domain/resource_attributes.rb +528 -0
  42. data/lib/caruby/domain/resource_dependency.rb +205 -0
  43. data/lib/caruby/domain/resource_introspection.rb +159 -0
  44. data/lib/caruby/domain/resource_metadata.rb +117 -0
  45. data/lib/caruby/domain/resource_module.rb +285 -0
  46. data/lib/caruby/domain/uniquify.rb +38 -0
  47. data/lib/caruby/import/annotatable_class.rb +28 -0
  48. data/lib/caruby/import/annotation_class.rb +27 -0
  49. data/lib/caruby/import/annotation_module.rb +67 -0
  50. data/lib/caruby/import/java.rb +338 -0
  51. data/lib/caruby/migration/migratable.rb +167 -0
  52. data/lib/caruby/migration/migrator.rb +533 -0
  53. data/lib/caruby/migration/resource.rb +8 -0
  54. data/lib/caruby/migration/resource_module.rb +11 -0
  55. data/lib/caruby/migration/uniquify.rb +20 -0
  56. data/lib/caruby/resource.rb +969 -0
  57. data/lib/caruby/util/attribute_path.rb +46 -0
  58. data/lib/caruby/util/cache.rb +53 -0
  59. data/lib/caruby/util/class.rb +99 -0
  60. data/lib/caruby/util/collection.rb +1053 -0
  61. data/lib/caruby/util/controlled_value.rb +35 -0
  62. data/lib/caruby/util/coordinate.rb +75 -0
  63. data/lib/caruby/util/domain_extent.rb +49 -0
  64. data/lib/caruby/util/file_separator.rb +65 -0
  65. data/lib/caruby/util/inflector.rb +20 -0
  66. data/lib/caruby/util/log.rb +95 -0
  67. data/lib/caruby/util/math.rb +12 -0
  68. data/lib/caruby/util/merge.rb +59 -0
  69. data/lib/caruby/util/module.rb +34 -0
  70. data/lib/caruby/util/options.rb +92 -0
  71. data/lib/caruby/util/partial_order.rb +36 -0
  72. data/lib/caruby/util/person.rb +119 -0
  73. data/lib/caruby/util/pretty_print.rb +184 -0
  74. data/lib/caruby/util/properties.rb +112 -0
  75. data/lib/caruby/util/stopwatch.rb +66 -0
  76. data/lib/caruby/util/topological_sync_enumerator.rb +53 -0
  77. data/lib/caruby/util/transitive_closure.rb +45 -0
  78. data/lib/caruby/util/tree.rb +48 -0
  79. data/lib/caruby/util/trie.rb +37 -0
  80. data/lib/caruby/util/uniquifier.rb +30 -0
  81. data/lib/caruby/util/validation.rb +48 -0
  82. data/lib/caruby/util/version.rb +56 -0
  83. data/lib/caruby/util/visitor.rb +351 -0
  84. data/lib/caruby/util/weak_hash.rb +36 -0
  85. data/lib/caruby/version.rb +3 -0
  86. metadata +186 -0
@@ -0,0 +1,528 @@
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, :defaults
10
+
11
+ # Returns the subject class's required attributes, determined as follows:
12
+ # * An attribute marked with the :mandatory flag is mandatory.
13
+ # * An attribute marked with the :optional or :autogenerated flag is not mandatory.
14
+ # * Otherwise, A secondary key or owner attribute is mandatory.
15
+ attr_reader :mandatory_attributes
16
+
17
+ # Adds the given attribute to this Class.
18
+ # If attribute refers to a domain type, then the type argument is the referenced domain type.
19
+ # Supported flags are listed in CaRuby::AttributeMetadata.
20
+ def add_attribute(attribute, type=nil, *flags)
21
+ add_attribute_metadata(AttributeMetadata.new(attribute, self, type, *flags))
22
+ attribute
23
+ end
24
+
25
+ # Returns the +[:identifier]+ primary key attribute array.
26
+ def primary_key_attributes
27
+ IDENTIFIER_ATTR_ARRAY
28
+ end
29
+
30
+ # Returns this class's secondary key attribute array.
31
+ # If this class's secondary key is not set, then the secondary key is the ResourceMetadata superclass
32
+ # secondary key, if any.
33
+ def secondary_key_attributes
34
+ @scndy_key_attrs or superclass < Resource ? superclass.secondary_key_attributes : Array::EMPTY_ARRAY
35
+ end
36
+
37
+ # Returns this class's alternate key attribute array.
38
+ # If this class's secondary key is not set, then the alternate key is the ResourceMetadata superclass
39
+ # alternate key, if any.
40
+ def alternate_key_attributes
41
+ @alt_key_attrs or superclass < Resource ? superclass.alternate_key_attributes : Array::EMPTY_ARRAY
42
+ end
43
+
44
+ # @return the AttributeMetadata for the given attribute symbol or alias
45
+ # @raise [NameError] if the attribute is not recognized
46
+ def attribute_metadata(attribute)
47
+ # simple and predominant case is that attribute is a standard attribute.
48
+ # otherwise, resolve attribute to the standard symbol.
49
+ attr_md = attribute_metadata_hash[attribute] || attribute_metadata_hash[standard_attribute(attribute)]
50
+ # if not found, then delegate to handler which will either make the new attribute or raise a NameError
51
+ attr_md || (attribute_missing(attribute) && @local_attr_md_hash[attribute])
52
+ end
53
+
54
+ # Returns the standard attribute symbol for the given name_or_alias.
55
+ #
56
+ # Raises NameError if the attribute is not found
57
+ def standard_attribute(name_or_alias)
58
+ alias_standard_attribute_hash[name_or_alias.to_sym] or raise NameError.new("#{qp} attribute not found: #{name_or_alias}")
59
+ end
60
+
61
+ # Returns an Enumerable on this Metadata's attributes which iterates on each attribute whose
62
+ # corresponding AttributeMetadata satisfies the given filter block.
63
+ def attribute_filter(&filter) # :yields: attribute_metadata
64
+ Filter.new(attribute_metadata_hash, &filter)
65
+ end
66
+
67
+ ## the built-in Metadata attribute filters ##
68
+
69
+ # @return [<Symbol>] the domain attributes which wrap a java property
70
+ # @see AttributeMetadata#java_property?
71
+ def java_attributes
72
+ @java_attrs ||= attribute_filter { |attr_md| attr_md.java_property? }
73
+ end
74
+
75
+ # @return [<Symbol>] the domain attributes
76
+ def domain_attributes
77
+ @dom_attrs ||= attribute_filter { |attr_md| attr_md.domain? }
78
+ end
79
+
80
+ # @return [<Symbol>] the non-domain Java attributes
81
+ def nondomain_attributes
82
+ @nondom_attrs ||= attribute_filter { |attr_md| attr_md.java_property? and attr_md.nondomain? }
83
+ end
84
+
85
+ # @return [<Symbol>] the non-domain Java property wrapper attributes
86
+ def nondomain_java_attributes
87
+ @nondom_java_attrs ||= nondomain_attributes.compose { |attr_md| attr_md.java_property? }
88
+ end
89
+
90
+ # @return [<Symbol>] the standard attributes which can be merged into an instance of the subject class.
91
+ # The default mergeable attributes consist of the {#nondomain_java_attributes}.
92
+ # @see Mergeable#mergeable_attributes
93
+ alias :mergeable_attributes :nondomain_java_attributes
94
+
95
+ # @return [<Symbol>] the dependent attributes
96
+ def dependent_attributes
97
+ @dep_attrs ||= attribute_filter { |attr_md| attr_md.dependent? }
98
+ end
99
+
100
+ # @return [<Symbol>] the dependent attributes
101
+ def autogenerated_dependent_attributes
102
+ @ag_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.autogenerated? }
103
+ end
104
+
105
+ # @return [<Symbol>] the dependent attributes which are created but not fetched
106
+ # @see AttributeMetadata#unfetched_created?
107
+ def unfetched_created_attributes
108
+ @uc_attrs ||= attribute_filter { |attr_md| attr_md.unfetched_created? }
109
+ end
110
+
111
+ # @return [<Symbol>] the autogenerated logical dependent attributes
112
+ # @see #logical_dependent_attributes
113
+ # @see AttributeMetadata#autogenerated?
114
+ def autogenerated_logical_dependent_attributes
115
+ @ag_log_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.autogenerated? and attr_md.logical? }
116
+ end
117
+
118
+ # @return [<Symbol>] the autogenerated {AttributeMetadata#saved_mergeable?} dependent attributes
119
+ def mergeable_saved_autogenerated_attributes
120
+ @mgbl_sv_unftchd_ag_attrs ||= autogenerated_dependent_attributes.compose { |attr_md| attr_md.saved_mergeable? }
121
+ end
122
+
123
+ # @return [<Symbol>] the logical dependent attributes
124
+ # @see AttributeMetadata#logical?
125
+ def logical_dependent_attributes
126
+ @log_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.logical? }
127
+ end
128
+
129
+ # @return [<Symbol>] the auto-generated attributes
130
+ # @see AttributeMetadata#autogenerated?
131
+ def autogenerated_attributes
132
+ @ag_attrs ||= attribute_filter { |attr_md| attr_md.autogenerated? }
133
+ end
134
+
135
+ # @return [<Symbol>] the auto-generated non-domain attributes
136
+ # @see AttributeMetadata#nondomain?
137
+ # @see AttributeMetadata#autogenerated?
138
+ def autogenerated_nondomain_attributes
139
+ @ag_nd_attrs ||= attribute_filter { |attr_md| attr_md.autogenerated? and attr_md.nondomain? }
140
+ end
141
+
142
+ # @return [<Symbol>] the {AttributeMetadata#volatile?} non-domain attributes
143
+ def volatile_nondomain_attributes
144
+ @unsvd_nd_attrs ||= attribute_filter { |attr_md| attr_md.volatile? and attr_md.nondomain? }
145
+ end
146
+
147
+ # @return [<Symbol>] the domain attributes which can serve as a query parameter
148
+ # @see AttributeMetadata#searchable?
149
+ def searchable_attributes
150
+ @srchbl_attrs ||= attribute_filter { |attr_md| attr_md.searchable? }
151
+ end
152
+
153
+ # @return [<Symbol>] the create/update cascaded domain attributes
154
+ # @see AttributeMetadata#cascaded?
155
+ def cascaded_attributes
156
+ @cscd_attrs ||= domain_attributes.compose { |attr_md| attr_md.cascaded? }
157
+ end
158
+
159
+ # @return [<Symbol>] the {#cascaded_attributes} which are saved with a proxy
160
+ # using the dependent saver_proxy method
161
+ def proxied_cascaded_attributes
162
+ @px_cscd_attrs ||= cascaded_attributes.compose { |attr_md| attr_md.proxied_save? }
163
+ end
164
+
165
+ # @return [<Symbol>] the {#cascaded_attributes} which are not saved with a proxy
166
+ # using the dependent saver_proxy method
167
+ def unproxied_cascaded_attributes
168
+ @unpx_cscd_attrs ||= cascaded_attributes.compose { |attr_md| not attr_md.proxied_save? }
169
+ end
170
+
171
+ # Returns the physical or auto-generated logical dependent attributes that can
172
+ # be copied from a save result to the given save argument object.
173
+ #
174
+ # @return [<Symbol>] the attributes that can be copied from a save result to a
175
+ # save argument object
176
+ # @see AttributeMetadata#autogenerated?
177
+ def copyable_saved_attributes
178
+ @cp_sv_attrs ||= dependent_attributes.compose { |attr_md| attr_md.autogenerated? or not attr_md.logical? }
179
+ end
180
+
181
+ def mandatory_attributes
182
+ @mndtry_attrs ||= collect_mandatory_attributes
183
+ end
184
+
185
+ # @return [<Symbol>] the attributes which are {AttributeMetadata#creatable?}
186
+ def creatable_attributes
187
+ @cr_attrs ||= attribute_filter { |attr_md| attr_md.creatable? }
188
+ end
189
+
190
+ # @return [<Symbol>] the attributes which are {AttributeMetadata#updatable?}
191
+ def updatable_attributes
192
+ @upd_attrs ||= attribute_filter { |attr_md| attr_md.updatable? }
193
+ end
194
+
195
+ def fetched_dependent_attributes
196
+ @ftchd_dep_attrs ||= (fetched_domain_attributes & dependent_attributes).to_a
197
+ end
198
+
199
+ # @return [<Symbol>] the independent saved attributes
200
+ # @see AttributeMetadata#independent?
201
+ # @see AttributeMetadata#saved?
202
+ def saved_independent_attributes
203
+ @svd_ind_attrs ||= attribute_filter { |attr_md| attr_md.independent? and attr_md.saved? }
204
+ end
205
+
206
+ # @return [<Symbol>] the domain {AttributeMetadata#saved?} attributes
207
+ def saved_domain_attributes
208
+ @svd_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.saved? }
209
+ end
210
+
211
+ # @return [<Symbol>] the non-domain {AttributeMetadata#saved?} attributes
212
+ def saved_nondomain_attributes
213
+ @svd_nondom_attrs ||= nondomain_attributes.compose { |attr_md| attr_md.saved? }
214
+ end
215
+
216
+ # @return [<Symbol>] the {AttributeMetadata#volatile?} {#nondomain_attributes}
217
+ def volatile_nondomain_attributes
218
+ @vlt_nondom_attrs ||= nondomain_attributes.compose { |attr_md| attr_md.volatile? }
219
+ end
220
+
221
+ # @return [<Symbol>] the domain {#creatable_attributes}
222
+ def creatable_domain_attributes
223
+ @cr_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.creatable? }
224
+ end
225
+
226
+ # @return [<Symbol>] the domain {#updatable_attributes}
227
+ def updatable_domain_attributes
228
+ @upd_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.updatable? }
229
+ end
230
+
231
+ # @return [<Symbol>] the domain attributes whose referents must exist before an instance of this
232
+ # metadata's subject classcan be created
233
+ # @see AttributeMetadata#storable_prerequisite?
234
+ def storable_prerequisite_attributes
235
+ @stbl_prereq_dom_attrs ||= attribute_filter { |attr_md| attr_md.storable_prerequisite? }
236
+ end
237
+
238
+ # Returns the domain attributes which are copied to create a storable template.
239
+ # The default is the {#nondomain_attributes}. An individual Resource class can
240
+ # override this to provide special reference attributes which must be set in
241
+ # a save operation.
242
+ #
243
+ # @param [Symbol] attribute the refernce attribute of the domain object being copied
244
+ def storable_copy_attributes(attribute)
245
+ attrs = @stbl_cp_attrs[attribute] if @stbl_cp_attrs
246
+ attrs || Array::EMPTY_ARRAY
247
+ end
248
+
249
+ # @return [<Symbol>] the attributes which are populated from the database
250
+ # @see AttributeMetadata#fetched?
251
+ def fetched_attributes
252
+ @ftchd_attrs ||= attribute_filter { |attr_md| attr_md.fetched? }
253
+ end
254
+
255
+ # Returns the domain attributes which are populated in a query on the given fetched instance of
256
+ # this metadata's subject class. The domain attribute is fetched if it satisfies the following
257
+ # conditions:
258
+ # * the attribute is a dependent attribute or of abstract domain type
259
+ # * the attribute is not specified as unfetched in the configuration
260
+ #
261
+ # @return [<Symbol>] the attributes which are {AttributeMetadata#fetched?}
262
+ def fetched_domain_attributes
263
+ @ftchd_dom_attrs ||= domain_attributes.compose { |attr_md| attr_md.fetched? }
264
+ end
265
+
266
+ #@return [<Symbol>] the #domain_attributes which are not #fetched_domain_attributes
267
+ def unfetched_domain_attributes
268
+ @unftchd_attrs ||= domain_attributes.compose { |attr_md| not attr_md.fetched? }
269
+ end
270
+
271
+ # @return [<Symbol>] the Java property non-abstract {#unfetched_domain_attributes}
272
+ def loadable_attributes
273
+ @unftchd_attrs ||= unfetched_domain_attributes.compose { |attr_md| attr_md.java_property? and not attr_md.type.abstract? }
274
+ end
275
+
276
+ # @return [<Symbol>] the {#loadable_attributes} which are not {AttributeMetadata#autogenerated?}
277
+ def non_autogenerated_loadable_attributes
278
+ @unftchd_attrs ||= unfetched_domain_attributes.compose { |attr_md| not attr_md.type.autogenerated? }
279
+ end
280
+
281
+ # @param [Symbol] attribute the attribute to check
282
+ # @return [Boolean] whether attribute return type is a domain object or collection thereof
283
+ def domain_attribute?(attribute)
284
+ attribute_metadata(attribute).domain?
285
+ end
286
+
287
+ # @param [Symbol] attribute the attribute to check
288
+ # @return [Boolean] whether attribute is not a domain attribute
289
+ def nondomain_attribute?(attribute)
290
+ not domain_attribute?(attribute)
291
+ end
292
+
293
+ # @param [Symbol] attribute the attribute to check
294
+ # @return [Boolean] whether attribute is an instance of a Java domain class
295
+ def collection_attribute?(attribute)
296
+ attribute_metadata(attribute).collection?
297
+ end
298
+
299
+ protected
300
+
301
+ # @return [{Symbol => AttributeMetadata}] the attribute => metadata hash
302
+ def attribute_metadata_hash
303
+ # initialize the meta-data if necessary
304
+ superclass.introspect_subclass(self) if @attr_md_hash.nil?
305
+ @attr_md_hash
306
+ end
307
+
308
+ # @return [{Symbol => Symbol}] the attribute alias => standard hash
309
+ def alias_standard_attribute_hash
310
+ # initialize the meta-data if necessary
311
+ superclass.introspect_subclass(self) if @alias_std_attr_map.nil?
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(hash, &filter)
328
+ raise ArgumentError.new("Attribute filter missing hash argument") if hash.nil?
329
+ raise ArgumentError.new("Attribute filter missing filter block") unless block_given?
330
+ @hash = hash
331
+ @filter = filter
332
+ end
333
+
334
+ # @yield [attribute] enumerates each filtered attribute
335
+ # @yieldparam [Symbol] the attribute which satisfies the filter condition
336
+ def each(&block)
337
+ @hash.each { |k, v| yield(k) if @filter.call(v) }
338
+ end
339
+
340
+ # @return [Filter] a new Filter which applies the filter block given to this
341
+ # method with the AttributeMetadata enumerated by this filter.
342
+ def compose
343
+ Filter.new(@hash) { |attr_md| @filter.call(attr_md) and yield(attr_md) }
344
+ end
345
+ end
346
+
347
+ # Initializes the attribute meta-data structures.
348
+ def init_attributes
349
+ @local_std_attr_hash = {}
350
+ @alias_std_attr_map = append_parent_enum(@local_std_attr_hash) { |par| par.alias_standard_attribute_hash }
351
+ @local_attr_md_hash = {}
352
+ @attr_md_hash = append_parent_enum(@local_attr_md_hash) { |par| par.attribute_metadata_hash }
353
+ @attributes = Enumerable::Enumerator.new(@attr_md_hash, :each_key)
354
+ @local_mndty_attrs = Set.new
355
+ @local_defaults = {}
356
+ @defaults = append_parent_enum(@local_defaults) { |par| par.defaults }
357
+ end
358
+
359
+ def add_attribute_aliases(hash)
360
+ hash.each { |aliaz, attr| delegate_to_attribute(aliaz, attr) }
361
+ end
362
+
363
+ # Sets this class's secondary key attributes to the given attributes.
364
+ # If attributes is set to nil, then the secondary key is cleared.
365
+ def set_secondary_key_attributes(*attributes)
366
+ attributes.compact!
367
+ @scndy_key_attrs = attributes.map { |attr| standard_attribute(attr) }
368
+ end
369
+
370
+ # Sets this class's alternate key attributes to the given attributes.
371
+ # If attributes is set to nil, then the alternate key is cleared.
372
+ def set_alternate_key_attributes(*attributes)
373
+ attributes.compact!
374
+ @alt_key_attrs = attributes.map { |attr| standard_attribute(attr) }
375
+ end
376
+
377
+ # Sets the given attribute type to klass. If attribute is defined in a superclass,
378
+ # then klass must be a subclass of the superclass attribute type.
379
+ #
380
+ # Raises ArgumentError if klass is incompatible with the current attribute type.
381
+ def set_attribute_type(attribute, klass)
382
+ attr_md = attribute_metadata(attribute)
383
+ if attr_md.declarer == self then
384
+ attr_md.type = klass
385
+ elsif attr_md.type.nil? or klass < attr_md.type then
386
+ new_attr_md = attr_md.restrict(self, klass)
387
+ add_attribute_metadata(new_attr_md)
388
+ elsif klass != attr_md.type then
389
+ raise ArgumentError.new("Cannot reset #{qp}.#{attribute} type #{attr_md.type} to incompatible #{klass.qp}")
390
+ end
391
+ end
392
+
393
+ # Sets the given attribute inverse to the inverse symbol.
394
+ #
395
+ # Raises ArgumentError if the inverse type is incompatible with this Resource.
396
+ def set_attribute_inverse(attribute, inverse)
397
+ attr_md = attribute_metadata(attribute)
398
+ # return if inverse is already set
399
+ return if attr_md.inverse == inverse
400
+ # the inverse attribute meta-data
401
+ inv_md = attr_md.type.attribute_metadata(inverse)
402
+ # if the attribute is the many side of a 1:M relation, then delegate to the one side.
403
+ if attr_md.collection? and not inv_md.collection? then
404
+ return attr_md.type.set_attribute_inverse(inverse, attribute)
405
+ end
406
+ # this class must be the same as or a subclass of the inverse attribute type
407
+ unless self <= inv_md.type then
408
+ raise ArgumentError.new("Cannot set #{qp}.#{attribute} inverse to #{attr_md.type.qp}.#{attribute} with incompatible type #{inv_md.type.qp}")
409
+ end
410
+
411
+ # if the attribute is defined by this class, then set the inverse in the attribute metadata.
412
+ # otherwise, make a new attribute metadata specialized for this class.
413
+ unless attr_md.declarer == self then
414
+ attr_md = attr_md.dup
415
+ attr_md.declarer = self
416
+ add_attribute_metadata(attribute, inverse)
417
+ end
418
+ attr_md.inverse = inverse
419
+
420
+ # if attribute is the one side of a 1:M relation, then add the inverse updater.
421
+ unless attr_md.collection? then
422
+ add_inverse_updater(attribute, inverse)
423
+ end
424
+ end
425
+
426
+ def add_attribute_defaults(hash)
427
+ hash.each { |attr, value| @local_defaults[standard_attribute(attr)] = value }
428
+ end
429
+
430
+ def add_mandatory_attributes(*attributes)
431
+ attributes.each { |attr| @local_mndty_attrs << standard_attribute(attr) }
432
+ end
433
+
434
+ # @param [Symbol] ref_attr the reference attribute of the domain object being copied
435
+ # @param [<Symbol>] copy_attrs the domain attributes to copy
436
+ def add_storable_copy_attributes(ref_attr, copy_attrs)
437
+ @stbl_cp_attrs ||= {}
438
+ @stbl_cp_attrs[ref_attr] = copy_attrs
439
+ end
440
+
441
+ # Marks the given attribute with flags supported by {AttributeMetadata#qualify}.
442
+ def qualify_attribute(attribute, *flags)
443
+ attribute_metadata(attribute).qualify(*flags)
444
+ end
445
+
446
+ # Removes the given attribute from this Resource.
447
+ # An attribute declared in a superclass Resource is hidden from this Resource but retained in
448
+ # the declaring Resource.
449
+ def remove_attribute(attribute)
450
+ std_attr = standard_attribute(attribute)
451
+ # if the attribute is local, then delete it, otherwise filter out the superclass attribute
452
+ if @local_attr_md_hash.delete(std_attr) then
453
+ @local_mndty_attrs.delete(std_attr)
454
+ @local_std_attr_hash.delete_if { |aliaz, attr| attr == std_attr }
455
+ else
456
+ @attr_md_hash = attribute_metadata_hash.filter_on_key { |attr| attr != attribute }
457
+ @attributes = Enumerable::Enumerator.new(@attr_md_hash, :each_key)
458
+ @alias_std_attr_map = alias_standard_attribute_hash.filter_on_key { |attr| attr != attribute }
459
+ end
460
+ end
461
+
462
+ def add_attribute_metadata(attr_md)
463
+ symbol = attr_md.to_sym
464
+ @local_attr_md_hash[symbol] = attr_md
465
+ # map the attribute symbol to itself in the alias map
466
+ @local_std_attr_hash[symbol] = symbol
467
+ end
468
+
469
+ # Records that the given aliaz aliases a standard attribute.
470
+ def add_alias(aliaz, attribute)
471
+ std_attr = standard_attribute(attribute)
472
+ raise ArgumentError.new("#{self} attribute not found: #{attribute}") if std_attr.nil?
473
+ @local_std_attr_hash[aliaz.to_sym] = std_attr
474
+ end
475
+
476
+ # Returns a new Enumerable which appends the evaluation of the given block in the parent
477
+ # metadata context. The default enum is the evaluation of the given block on this Metadata.
478
+ def append_parent_enum(enum)
479
+ superclass < Resource ? enum.union(yield(superclass)) : enum
480
+ end
481
+
482
+ def each_attribute_metadata(&block)
483
+ attribute_metadata_hash.each_value(&block)
484
+ end
485
+
486
+ # Makes a new synthetic attribute for each _method_ => _original_ hash entry.
487
+ #
488
+ # @param (see Class#offset_attr_accessor)
489
+ def offset_attribute(hash, offset=nil)
490
+ offset_attr_accessor(hash, offset)
491
+ hash.each { |attr, original| add_attribute(attr, attribute_metadata(original).type) }
492
+ end
493
+
494
+ # Collects the {AttributeMetadata#fetched_dependent?} and {AttributeMetadata#fetched_independent?}
495
+ # standard domain attributes.
496
+ #
497
+ # @return [<Symbol>] the fetched attributes
498
+ def collect_default_fetched_domain_attributes
499
+ attribute_filter do |attr_md|
500
+ if attr_md.domain? then
501
+ attr_md.dependent? ? fetched_dependent?(attr_md) : fetched_independent?(attr_md)
502
+ end
503
+ end
504
+ end
505
+
506
+ # Merges the secondary key, owner and additional mandatory attributes defined in the properties.
507
+ #
508
+ # @see #mandatory_attributes
509
+ def collect_mandatory_attributes
510
+ mandatory = Set.new
511
+ # add the secondary key
512
+ mandatory.merge(secondary_key_attributes)
513
+ # add the owner attribute, if any
514
+ mandatory << owner_attribute unless owner_attribute.nil? or not attribute_metadata(owner_attribute).java_property?
515
+ # remove autogenerated or optional attributes
516
+ mandatory.delete_if { |attr| attribute_metadata(attr).autogenerated? or attribute_metadata(attr).optional? }
517
+ @local_mndty_attrs.merge!(mandatory)
518
+ append_parent_enum(@local_mndty_attrs) { |par| par.mandatory_attributes }
519
+ end
520
+
521
+ # Raises a NameError. Domain classes can override this method to dynamically create a new reference attribute.
522
+ #
523
+ # @raise [NameError] always
524
+ def attribute_missing(attribute)
525
+ raise NameError.new("#{name.demodulize} attribute not found: #{attribute}")
526
+ end
527
+ end
528
+ end