caruby-core 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
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