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.
- data/History.txt +4 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +51 -0
- data/doc/website/css/site.css +1 -5
- data/doc/website/images/avatar.png +0 -0
- data/doc/website/images/favicon.ico +0 -0
- data/doc/website/images/logo.png +0 -0
- data/doc/website/index.html +82 -0
- data/doc/website/install.html +87 -0
- data/doc/website/quick_start.html +87 -0
- data/doc/website/tissue.html +85 -0
- data/doc/website/uom.html +10 -0
- data/lib/caruby.rb +3 -0
- data/lib/caruby/active_support/README.txt +2 -0
- data/lib/caruby/active_support/core_ext/string.rb +7 -0
- data/lib/caruby/active_support/core_ext/string/inflections.rb +167 -0
- data/lib/caruby/active_support/inflections.rb +55 -0
- data/lib/caruby/active_support/inflector.rb +398 -0
- data/lib/caruby/cli/application.rb +36 -0
- data/lib/caruby/cli/command.rb +169 -0
- data/lib/caruby/csv/csv_mapper.rb +157 -0
- data/lib/caruby/csv/csvio.rb +185 -0
- data/lib/caruby/database.rb +252 -0
- data/lib/caruby/database/fetched_matcher.rb +66 -0
- data/lib/caruby/database/persistable.rb +432 -0
- data/lib/caruby/database/persistence_service.rb +162 -0
- data/lib/caruby/database/reader.rb +599 -0
- data/lib/caruby/database/saved_merger.rb +131 -0
- data/lib/caruby/database/search_template_builder.rb +59 -0
- data/lib/caruby/database/sql_executor.rb +75 -0
- data/lib/caruby/database/store_template_builder.rb +200 -0
- data/lib/caruby/database/writer.rb +469 -0
- data/lib/caruby/domain/annotatable.rb +25 -0
- data/lib/caruby/domain/annotation.rb +23 -0
- data/lib/caruby/domain/attribute_metadata.rb +447 -0
- data/lib/caruby/domain/java_attribute_metadata.rb +160 -0
- data/lib/caruby/domain/merge.rb +91 -0
- data/lib/caruby/domain/properties.rb +95 -0
- data/lib/caruby/domain/reference_visitor.rb +289 -0
- data/lib/caruby/domain/resource_attributes.rb +528 -0
- data/lib/caruby/domain/resource_dependency.rb +205 -0
- data/lib/caruby/domain/resource_introspection.rb +159 -0
- data/lib/caruby/domain/resource_metadata.rb +117 -0
- data/lib/caruby/domain/resource_module.rb +285 -0
- data/lib/caruby/domain/uniquify.rb +38 -0
- data/lib/caruby/import/annotatable_class.rb +28 -0
- data/lib/caruby/import/annotation_class.rb +27 -0
- data/lib/caruby/import/annotation_module.rb +67 -0
- data/lib/caruby/import/java.rb +338 -0
- data/lib/caruby/migration/migratable.rb +167 -0
- data/lib/caruby/migration/migrator.rb +533 -0
- data/lib/caruby/migration/resource.rb +8 -0
- data/lib/caruby/migration/resource_module.rb +11 -0
- data/lib/caruby/migration/uniquify.rb +20 -0
- data/lib/caruby/resource.rb +969 -0
- data/lib/caruby/util/attribute_path.rb +46 -0
- data/lib/caruby/util/cache.rb +53 -0
- data/lib/caruby/util/class.rb +99 -0
- data/lib/caruby/util/collection.rb +1053 -0
- data/lib/caruby/util/controlled_value.rb +35 -0
- data/lib/caruby/util/coordinate.rb +75 -0
- data/lib/caruby/util/domain_extent.rb +49 -0
- data/lib/caruby/util/file_separator.rb +65 -0
- data/lib/caruby/util/inflector.rb +20 -0
- data/lib/caruby/util/log.rb +95 -0
- data/lib/caruby/util/math.rb +12 -0
- data/lib/caruby/util/merge.rb +59 -0
- data/lib/caruby/util/module.rb +34 -0
- data/lib/caruby/util/options.rb +92 -0
- data/lib/caruby/util/partial_order.rb +36 -0
- data/lib/caruby/util/person.rb +119 -0
- data/lib/caruby/util/pretty_print.rb +184 -0
- data/lib/caruby/util/properties.rb +112 -0
- data/lib/caruby/util/stopwatch.rb +66 -0
- data/lib/caruby/util/topological_sync_enumerator.rb +53 -0
- data/lib/caruby/util/transitive_closure.rb +45 -0
- data/lib/caruby/util/tree.rb +48 -0
- data/lib/caruby/util/trie.rb +37 -0
- data/lib/caruby/util/uniquifier.rb +30 -0
- data/lib/caruby/util/validation.rb +48 -0
- data/lib/caruby/util/version.rb +56 -0
- data/lib/caruby/util/visitor.rb +351 -0
- data/lib/caruby/util/weak_hash.rb +36 -0
- data/lib/caruby/version.rb +3 -0
- 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
|