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,25 @@
1
+ require 'caruby/resource'
2
+
3
+ module CaRuby
4
+ # The Annotatable module marks a domain class as an anchor which holds at least one annotation attribute.
5
+ module Annotatable
6
+ include Resource
7
+
8
+ # Dynamically creates a new annotation reference method with the given symbol if symbol is the camelized form of a
9
+ # class in one of the Annotatable class's annotation modules.
10
+ def method_missing(symbol, *args)
11
+ name = symbol.to_s
12
+ # remove trailing assignment = if present
13
+ name.chop! if name =~ /=$/
14
+ # the class with the camelized form of the name
15
+ klass = self.class.annotation_class(name.camelize)
16
+ # delegate to super to print an error if no class
17
+ super if klass.nil?
18
+ # add the annotation attribute
19
+ klass.add_annotation(self.class)
20
+ raise NotImplementedError.new("#{self.class.qp} annotation method not created: #{symbol}") unless respond_to?(symbol)
21
+ #call the annotation attribute method
22
+ send(symbol, *args)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'caruby/resource'
2
+
3
+ module CaRuby
4
+ # The Annotation module marks a domain object as an Annotation.
5
+ module Annotation
6
+ include Resource
7
+
8
+ # Dynamically creates a new annotable owner attribute with the given symbol if symbol is an annotatable owner attribute
9
+ # accessor method.
10
+ #
11
+ # @see #attribute_missing
12
+ def method_missing(symbol, *args)
13
+ name = symbol.to_s
14
+ # remove trailing assignment = if present
15
+ name.chop! if name =~ /=$/
16
+ # try to make the owner attribute
17
+ self.class.attribute_metadata(name.to_sym)
18
+ # if we reached here, then the owner was created so verify and call the new method
19
+ raise NoMethodError.new("#{name.demodulize} owner attribute #{name} created but accessor method not found: #{symbol}") unless method_defined?(symbol)
20
+ send(symbol, *args)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,447 @@
1
+ require 'set'
2
+ require 'caruby/util/inflector'
3
+ require 'caruby/util/collection'
4
+ require 'caruby/util/validation'
5
+
6
+ module CaRuby
7
+ # An Attribute captures the following metadata about a domain class attribute:
8
+ # * attribute symbol
9
+ # * declarer type
10
+ # * return type
11
+ # * reader method symbol
12
+ # * writer method symbol
13
+ class AttributeMetadata
14
+ # The supported attribute qualifier flags.
15
+ SUPPORTED_FLAGS = [
16
+ :autogenerated, :collection, :dependent, :derived, :logical, :disjoint, :owner, :cascaded,
17
+ :no_cascade_update_to_create, :saved, :unsaved, :saved_unmergeable, :optional, :fetched, :unfetched,
18
+ :create_only, :update_only, :unidirectional, :volatile].to_set
19
+
20
+ # The standard attribute reader and writer methods.
21
+ attr_reader :accessors
22
+
23
+ # The declaring class.
24
+ attr_accessor :declarer
25
+
26
+ # The return class.
27
+ attr_accessor :type
28
+
29
+ # The qualifier flags.
30
+ # @see SUPPORTED_FLAGS
31
+ attr_accessor :flags
32
+
33
+ protected :declarer=
34
+
35
+ # Creates a new AttributeMetadata from the given attribute.
36
+ #
37
+ # The type is the referenced entity type; an attribute whose return type is a collection
38
+ # of domain objects is thus the domain object class rather than a collection class.
39
+ #
40
+ # If the type is given, then the following flags are recognized:
41
+ # * +:dependent+ - the attribute references a dependent
42
+ # * +:collection+ - the attribute return type is a collection
43
+ # * +:owner+ - the attribute references the owner of a dependent
44
+ # * +:cascaded+ - a database create/update/delete operation propagates to the attribute reference
45
+ #
46
+ # @param [String,Symbol] attr the subject attribute
47
+ # @param [Class] declarer the declaring class
48
+ # @param [Class] declarer the return type
49
+ # @param [<Symbol>] flags the qualifying flags
50
+ def initialize(attribute, declarer, type=nil, *flags)
51
+ # the attribute symbol
52
+ @symbol = attribute.to_sym
53
+ # the declaring class
54
+ @declarer = declarer
55
+ # the Ruby class
56
+ @type = Class.to_ruby(type) if type
57
+ # the read and write methods
58
+ @accessors = [@symbol, "#{attribute}=".to_sym]
59
+ # the qualifier flags
60
+ @flags = Set.new
61
+ # identifier is always volatile
62
+ if @symbol == :identifier then flags << :volatile end
63
+ qualify(*flags)
64
+ end
65
+
66
+ # @return [Symbol] the reader method
67
+ def reader
68
+ accessors.first
69
+ end
70
+
71
+ # @return [Symbol] the writer method
72
+ def writer
73
+ accessors.last
74
+ end
75
+
76
+ # @return [Symbol, nil] the inverse of this attribute, if any
77
+ def inverse
78
+ @inv_md.to_sym if @inv_md
79
+ end
80
+
81
+ # Creates a new declarer attribute which restricts this attribute {#type} to the given type.
82
+ #
83
+ #@param [Class] declarer the class for which the restriction holds
84
+ # @return [AttributeMetadata] the metadata for the new declarer attribute
85
+ def restrict(declarer, type)
86
+ unless declarer < self.type then
87
+ raise ArgumentError.new("Cannot restrict #{self.declarer}.#{self} to incompatible declarer type #{declarer}")
88
+ end
89
+ if self.type and not type < self.type then
90
+ raise ArgumentError.new("Cannot restrict #{self.declarer}.#{self} to incompatible type #{type}")
91
+ end
92
+ # set aside the restrictions prior to the copy
93
+ rstr = @restrictions
94
+ @restrictions = nil
95
+ copy = dup
96
+ # Restore the restrictions. Initialize the array if necessary, since the copy
97
+ # will be added to the list.
98
+ @restrictions = rstr || []
99
+ # specialize the copy declarer and type
100
+ copy.declarer = declarer
101
+ copy.type = type
102
+ # specialize the inverse to the restricted type attribute, if necessary
103
+ if inverse then copy.inverse = inverse end
104
+ # Capture the restriction to propagate modifications to this metadata, esp.
105
+ # adding an inverse.
106
+ @restrictions << copy
107
+ copy
108
+ end
109
+
110
+ # Sets the inverse of the subject attribute to the given attribute.
111
+ # The inverse relation is symmetric, i.e. the inverse of the referenced AttributeMetadata
112
+ # is set to this AttributeMetadata's subject attribute.
113
+ #
114
+ # @param attribute the inverse attribute
115
+ def inverse=(attribute)
116
+ logger.debug { "Set #{@declarer.qp}.#{self} inverse to #{type.qp}.#{attribute}." }
117
+ @inv_md = type.attribute_metadata(attribute)
118
+ unless @inv_md then
119
+ raise MetadataError.new("#{@declarer.qp}.#{self} inverse attribute #{type.qp}.#{attribute} not found")
120
+ end
121
+ inv_inv_md = @inv_md.inverse_attribute_metadata
122
+ if inv_inv_md then
123
+ unless inv_inv_md == self then
124
+ if @inv_md.inverse == @symbol then
125
+ @inv_md.inverse = @symbol
126
+ else
127
+ raise MetadataError.new("Cannot set #{type.qp}.#{@inv_md} inverse attribute to #{@declarer.qp}.#{self} since it conflicts with existing inverse #{@inv_md.inverse}")
128
+ end
129
+ end
130
+ else
131
+ @inv_md.inverse = @symbol
132
+ @inv_md.set_flag(:disjoint) if disjoint?
133
+ end
134
+ # propagate to restrictions
135
+ if @restrictions then @restrictions.each { |attr_md| attr_md.inverse = attribute } end
136
+ end
137
+
138
+ # @return [AttributeMetadata, nil] the metadata for the {#inverse} attribute, if any
139
+ def inverse_attribute_metadata
140
+ @inv_md
141
+ end
142
+
143
+ # Qualifies this attribute with the given flags. Supported flags are listed in {SUPPORTED_FLAGS}.
144
+ #
145
+ # @param [<Symbol>] the flags to add
146
+ # @raise [ArgumentError] if the flag is not supported
147
+ def qualify(*flags)
148
+ flags.each { |flag| set_flag(flag) }
149
+ # propagate to restrictions
150
+ if @restrictions then @restrictions.each { |attr_md| attr_md.qualify(*flags) } end
151
+ end
152
+
153
+ # @return whether the subject attribute encapsulates a Java property
154
+ def java_property?
155
+ CaRuby::JavaAttributeMetadata === self
156
+ end
157
+
158
+ # @return whether the subject attribute returns a domain object or collection of domain objects
159
+ def domain?
160
+ # the type must be a Ruby class rather than a Java Class, and include the Domain mix-in
161
+ Class === type and type < Resource
162
+ end
163
+
164
+ # @return whether the subject attribute is not a domain object attribute
165
+ def nondomain?
166
+ not domain?
167
+ end
168
+
169
+ # Returns whether the subject attribute is fetched, determined as follows:
170
+ # * An attribute marked with the :fetched flag is fetched.
171
+ # * An attribute marked with the :unfetched flag is not fetched.
172
+ # Otherwise, a non-domain attribute is fetched, and a domain attribute is
173
+ # fetched if one of the following conditions hold:
174
+ # * A dependent domain attribute is fetched if it is not logical.
175
+ # * An owner domain attribute is fetched by default.
176
+ # * An independent domain attribute is fetched if it is abstract and not derived.
177
+ #
178
+ # @return [Boolean] whether the attribute is fetched
179
+ def fetched?
180
+ return true if @flags.include?(:fetched)
181
+ return false if @flags.include?(:unfetched)
182
+ nondomain? or dependent? ? fetched_dependent? : fetched_independent?
183
+ end
184
+
185
+ # @return whether the subject attribute return type is a collection
186
+ def collection?
187
+ @flags.include?(:collection)
188
+ end
189
+
190
+ # Returns whether the subject attribute is a dependent on a parent. See the caRuby configuration
191
+ # documentation for a dependency description.
192
+ #
193
+ # @return [Boolean] whether the attribute references a dependent
194
+ def dependent?
195
+ annotation? or @flags.include?(:dependent)
196
+ end
197
+
198
+ # Returns whether the subject attribute is marked as optional in a create.
199
+ # This method returns true only if the :optional flag is explicitly set.
200
+ # Other attributes are optional by default.
201
+ #
202
+ # @return [Boolean] whether the attribute is optional
203
+ # @see ResourceAttributes#mandatory_attributes.
204
+ def optional?
205
+ @flags.include?(:optional)
206
+ end
207
+
208
+ # Returns whether the subject attribute is not saved.
209
+ #
210
+ # @return [Boolean] whether the attribute is unsaved
211
+ def unsaved?
212
+ @flags.include?(:unsaved)
213
+ end
214
+
215
+ # Returns whether the subject attribute is a dependent whose value is automatically generated
216
+ # with place-holder domain objects when the parent is created.
217
+ #
218
+ # @return [Boolean] whether the attribute is auto-generated
219
+ def autogenerated?
220
+ @flags.include?(:autogenerated)
221
+ end
222
+
223
+ # Returns whether the subject attribute is either an #autogenerated? {#dependent?}
224
+ # or {#cascaded?} and marked with the :unfetched flag.
225
+ def unfetched_created?
226
+ (dependent? and autogenerated?) or (cascaded? and @flags.include?(:unfetched))
227
+ end
228
+
229
+ # Returns whether the subject attribute is a dependent whose owner does not automatically
230
+ # cascade application service creation or update to the dependent. It is incumbent upon
231
+ # CaRuby::Database to cascade the changes.
232
+ def logical?
233
+ annotation? or @flags.include?(:logical)
234
+ end
235
+
236
+ # @return whether the subject attribute is an annotation
237
+ def annotation?
238
+ false
239
+ # TODO - enable when annotation enabled
240
+ end
241
+
242
+ # @return whether this attribute is derived from another attribute
243
+ # This occurs when the attribute value is set by setting another attribute, e.g. if this
244
+ # attribute is the inverse of a dependent owner attribute.
245
+ def derived?
246
+ @flags.include?(:derived) or (dependent? and not inverse.nil?)
247
+ end
248
+
249
+ # @return this attribute's inverse attribute if the inverse is a derived attribute, or nil otherwise
250
+ def derived_inverse
251
+ @inv_md.to_sym if @inv_md and @inv_md.derived?
252
+ end
253
+
254
+ # @return whether the subject attribute is a non-dependent domain attribute
255
+ def independent?
256
+ domain? and not dependent?
257
+ end
258
+
259
+ # A Java attribute is creatable if all of the following conditions hold:
260
+ # * the attribute is {#saved?}
261
+ # * the attribute is not a {#proxied_save?}
262
+ # * the attribute :update_only flag is not set
263
+ #
264
+ # @return [Boolean] whether this attribute is saved in a create operation
265
+ def creatable?
266
+ saved? and not @flags.include?(:update_only)
267
+ end
268
+
269
+ # A Java attribute is an uncreated dependent if any of the following conditions hold:
270
+ # * the attribute is a {#logical?} dependent
271
+ # * the attribute is a #dependent? which is not {#creatable?}
272
+ #
273
+ # @return [Boolean] whether this attribute is saved in a create operation
274
+ def uncreated_dependent?
275
+ logical? or (dependent? and not creatable?)
276
+ end
277
+
278
+ # @return whether this attribute is saved in a update operation
279
+ #
280
+ # A Java attribute is updatable if all of the following conditions hold:
281
+ # * the attribute is {#saved?}
282
+ # * the attribute :create_only flag is not set
283
+ def updatable?
284
+ saved? and not @flags.include?(:create_only)
285
+ end
286
+
287
+ # @return whether this attribute is navigated to build a template used in a create or update operation
288
+ # A cascaded attribute determines where to prune a database create or update object graph.
289
+ #
290
+ # An attribute is cascaded if it is a physical dependent or the :cascaded flag is set.
291
+ def cascaded?
292
+ (dependent? and not logical?) or @flags.include?(:cascaded)
293
+ end
294
+
295
+ # Returns whether this attribute is #{#cascaded} and cascades a parent update to a child
296
+ # create. This corresponds to the Hibernate +save-update+ cascade style but not the Hibernate
297
+ # +all+ cascade style.
298
+ #
299
+ # This method returns true if this attribute is cascaded and the +:no_cascade_update_to_create+
300
+ # flag is not set. Set this flag if the Hibernate mapping specifies the +all+ cascade style.
301
+ # Failure to set this flag will result in the caTissue Hibernate error:
302
+ # Exception: gov.nih.nci.system.applicationservice.ApplicationException:
303
+ # The given object has a null identifier:
304
+ # followed by the attribute type name.
305
+ def cascade_update_to_create?
306
+ cascaded? and not @flags.include?(:no_cascade_update_to_create)
307
+ end
308
+
309
+ # A Java property attribute is saved if none of the following conditions hold:
310
+ # * the attribute :unsaved flag is set
311
+ # * the attribute is {#proxied_save?}
312
+ # and any of the following conditions hold:
313
+ # * the attibute is {#nondomain?}
314
+ # * the attribute is cascaded
315
+ # * the attribute value is not a collection
316
+ # * the attribute does not have an inverse
317
+ # * the attribute :saved flag is set
318
+ #
319
+ # @return [Boolean] whether this attribute is saved in a create or update operation
320
+ def saved?
321
+ java_property? and not @flags.include?(:unsaved) and not proxied_save? and
322
+ (nondomain? or cascaded? or not collection? or inverse.nil? or unidirectional_java_dependent? or @flags.include?(:saved))
323
+ end
324
+
325
+ # @return [Boolean] whether this attribute is not {#saved?}
326
+ def unsaved?
327
+ not saved?
328
+ end
329
+
330
+ # @return [Boolean] whether the attribute return {#type} is a Resource class which
331
+ # implements the saver_proxy method
332
+ def proxied_save?
333
+ domain? and type.method_defined?(:saver_proxy)
334
+ end
335
+
336
+ # Each saved attribute is a saved mergeable attribute unless the :saved_unmergeable flag is set.
337
+ # @return [Boolean] whether this attribute can be merged from a save result
338
+ def saved_mergeable?
339
+ not @flags.include?(:saved_unmergeable)
340
+ end
341
+
342
+ # Returns whether this attribute's referents must exist before an instance of the
343
+ # declarer class can be created. An attribute is a storable prerequisite if it is
344
+ # either:
345
+ # * a {#cascaded?} dependent which does not #{#cascade_update_to_create?}, or
346
+ # * a {#saved?} {#independent?} 1:M or M:N association.
347
+ #
348
+ # @return [Boolean] whether this attribute is a create prerequisite
349
+ def storable_prerequisite?
350
+ return true if cascaded? and @flags.include?(:no_cascade_update_to_create)
351
+ return false unless independent? and saved?
352
+ return true unless collection?
353
+ inv_md = inverse_attribute_metadata
354
+ inv_md.nil? or inv_md.collection?
355
+ end
356
+
357
+ # @return [Boolean] whether this attribute is a collection with a collection inverse
358
+ def many_to_many?
359
+ return false unless collection?
360
+ inv_md = inverse_attribute_metadata
361
+ inv_md and inv_md.collection?
362
+ end
363
+
364
+ # @return [Boolean] whether the subject attribute is not saved
365
+ def transient?
366
+ not saved?
367
+ end
368
+
369
+ # Returns whether this attribute is set on the server as a side-effect
370
+ # of a change to the declarer object. The volatile attributes include
371
+ # those which are {#unsaved?} and those which are saved but marked
372
+ # with the +:volatile+ flag.
373
+ #
374
+ # @return [Boolean] whether this attribute's value is determined by the server
375
+ def volatile?
376
+ unsaved? or @flags.include?(:volatile)
377
+ end
378
+
379
+ # @return [Boolean] whether this is a non-collection Java attribute
380
+ def searchable?
381
+ java_property? and not collection?
382
+ end
383
+
384
+ # @return [Boolean] whether the subject attribute is a dependency owner
385
+ def owner?
386
+ @flags.include?(:owner)
387
+ end
388
+
389
+ # @return [Boolean] whether this is a dependent attribute which has exactly one owner value chosen from
390
+ # several owner attributes.
391
+ def disjoint?
392
+ @flags.include?(:disjoint)
393
+ end
394
+
395
+ # @return [Boolean] whether this attribute is a dependent which does not have a Java inverse owner attribute
396
+ def unidirectional_java_dependent?
397
+ # TODO - can this be relaxed to java_unidirectional? i.e. eliminate dependent filter
398
+ dependent? and not bidirectional_java_association?
399
+ end
400
+
401
+ # @return [Boolean] whether this is a Java attribute which has a Java inverse
402
+ def bidirectional_java_association?
403
+ inverse and java_property? and inverse_attribute_metadata.java_property?
404
+ end
405
+
406
+ def to_sym
407
+ @symbol
408
+ end
409
+
410
+ def to_s
411
+ @symbol.to_s
412
+ end
413
+
414
+ alias :inspect :to_s
415
+
416
+ alias :qp :to_s
417
+
418
+ private
419
+
420
+ # @param [Symbol] the flag to set
421
+ # @raise [ArgumentError] if flag is not supported
422
+ def set_flag(flag)
423
+ return if @flags.include?(flag)
424
+ raise ArgumentError.new("Attribute flag not supported: #{flag}") unless SUPPORTED_FLAGS.include?(flag)
425
+ @flags << flag
426
+ if flag == :owner then
427
+ inv_attr = type.dependent_attribute(@declarer)
428
+ if inv_attr.nil? then
429
+ raise MetadataError.new("#{@declarer.qp} owner attribute #{self} does not have a #{type.qp} dependent inverse")
430
+ end
431
+ self.inverse = type.dependent_attribute(@declarer)
432
+ @flags << :logical if inverse_attribute_metadata.logical?
433
+ end
434
+ end
435
+
436
+ # @return [Boolean] whether this dependent attribute is fetched. Only physical dependents are fetched by default.
437
+ def fetched_dependent?
438
+ not (logical? or @flags.include?(:unfetched))
439
+ end
440
+
441
+ # @return [Boolean] whether this independent attribute is fetched. Only abstract, non-derived independent
442
+ # references are fetched by default.
443
+ def fetched_independent?
444
+ type.abstract? and not (derived? or @flags.include?(:unfetched))
445
+ end
446
+ end
447
+ end