jinx 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.yardopts +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +27 -0
- data/History.md +6 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +44 -0
- data/Rakefile +41 -0
- data/examples/family/README.md +10 -0
- data/examples/family/ext/build.xml +35 -0
- data/examples/family/ext/src/family/Address.java +68 -0
- data/examples/family/ext/src/family/Child.java +24 -0
- data/examples/family/ext/src/family/DomainObject.java +26 -0
- data/examples/family/ext/src/family/Household.java +36 -0
- data/examples/family/ext/src/family/Parent.java +48 -0
- data/examples/family/ext/src/family/Person.java +42 -0
- data/examples/family/lib/family.rb +15 -0
- data/examples/family/lib/family/address.rb +6 -0
- data/examples/family/lib/family/domain_object.rb +6 -0
- data/examples/family/lib/family/household.rb +6 -0
- data/examples/family/lib/family/parent.rb +16 -0
- data/examples/family/lib/family/person.rb +6 -0
- data/examples/model/README.md +25 -0
- data/examples/model/ext/build.xml +35 -0
- data/examples/model/ext/src/domain/Child.java +192 -0
- data/examples/model/ext/src/domain/Dependent.java +29 -0
- data/examples/model/ext/src/domain/DomainObject.java +26 -0
- data/examples/model/ext/src/domain/Independent.java +83 -0
- data/examples/model/ext/src/domain/Parent.java +129 -0
- data/examples/model/ext/src/domain/Person.java +14 -0
- data/examples/model/lib/model.rb +13 -0
- data/examples/model/lib/model/child.rb +13 -0
- data/examples/model/lib/model/domain_object.rb +6 -0
- data/examples/model/lib/model/independent.rb +11 -0
- data/examples/model/lib/model/parent.rb +17 -0
- data/jinx.gemspec +22 -0
- data/lib/jinx.rb +3 -0
- data/lib/jinx/active_support/README.txt +2 -0
- data/lib/jinx/active_support/core_ext/string.rb +7 -0
- data/lib/jinx/active_support/core_ext/string/inflections.rb +167 -0
- data/lib/jinx/active_support/inflections.rb +55 -0
- data/lib/jinx/active_support/inflector.rb +398 -0
- data/lib/jinx/cli/application.rb +36 -0
- data/lib/jinx/cli/command.rb +214 -0
- data/lib/jinx/helpers/array.rb +108 -0
- data/lib/jinx/helpers/boolean.rb +42 -0
- data/lib/jinx/helpers/case_insensitive_hash.rb +39 -0
- data/lib/jinx/helpers/class.rb +149 -0
- data/lib/jinx/helpers/collection.rb +33 -0
- data/lib/jinx/helpers/collections.rb +11 -0
- data/lib/jinx/helpers/collector.rb +20 -0
- data/lib/jinx/helpers/conditional_enumerator.rb +21 -0
- data/lib/jinx/helpers/enumerable.rb +242 -0
- data/lib/jinx/helpers/enumerate.rb +35 -0
- data/lib/jinx/helpers/error.rb +15 -0
- data/lib/jinx/helpers/file_separator.rb +65 -0
- data/lib/jinx/helpers/filter.rb +52 -0
- data/lib/jinx/helpers/flattener.rb +38 -0
- data/lib/jinx/helpers/hash.rb +12 -0
- data/lib/jinx/helpers/hashable.rb +502 -0
- data/lib/jinx/helpers/inflector.rb +36 -0
- data/lib/jinx/helpers/key_transformer_hash.rb +43 -0
- data/lib/jinx/helpers/lazy_hash.rb +44 -0
- data/lib/jinx/helpers/log.rb +106 -0
- data/lib/jinx/helpers/math.rb +12 -0
- data/lib/jinx/helpers/merge.rb +60 -0
- data/lib/jinx/helpers/module.rb +18 -0
- data/lib/jinx/helpers/multi_enumerator.rb +31 -0
- data/lib/jinx/helpers/options.rb +92 -0
- data/lib/jinx/helpers/os.rb +19 -0
- data/lib/jinx/helpers/partial_order.rb +37 -0
- data/lib/jinx/helpers/pretty_print.rb +207 -0
- data/lib/jinx/helpers/set.rb +8 -0
- data/lib/jinx/helpers/stopwatch.rb +76 -0
- data/lib/jinx/helpers/transformer.rb +24 -0
- data/lib/jinx/helpers/transitive_closure.rb +55 -0
- data/lib/jinx/helpers/uniquifier.rb +50 -0
- data/lib/jinx/helpers/validation.rb +33 -0
- data/lib/jinx/helpers/visitor.rb +370 -0
- data/lib/jinx/import/class_path_modifier.rb +77 -0
- data/lib/jinx/import/java.rb +337 -0
- data/lib/jinx/importer.rb +240 -0
- data/lib/jinx/metadata.rb +155 -0
- data/lib/jinx/metadata/attribute_enumerator.rb +73 -0
- data/lib/jinx/metadata/dependency.rb +244 -0
- data/lib/jinx/metadata/id_alias.rb +23 -0
- data/lib/jinx/metadata/introspector.rb +179 -0
- data/lib/jinx/metadata/inverse.rb +170 -0
- data/lib/jinx/metadata/java_property.rb +169 -0
- data/lib/jinx/metadata/propertied.rb +500 -0
- data/lib/jinx/metadata/property.rb +401 -0
- data/lib/jinx/metadata/property_characteristics.rb +114 -0
- data/lib/jinx/resource.rb +862 -0
- data/lib/jinx/resource/copy_visitor.rb +36 -0
- data/lib/jinx/resource/inversible.rb +90 -0
- data/lib/jinx/resource/match_visitor.rb +180 -0
- data/lib/jinx/resource/matcher.rb +20 -0
- data/lib/jinx/resource/merge_visitor.rb +73 -0
- data/lib/jinx/resource/mergeable.rb +185 -0
- data/lib/jinx/resource/reference_enumerator.rb +49 -0
- data/lib/jinx/resource/reference_path_visitor.rb +38 -0
- data/lib/jinx/resource/reference_visitor.rb +55 -0
- data/lib/jinx/resource/unique.rb +35 -0
- data/lib/jinx/version.rb +3 -0
- data/spec/defaults_spec.rb +30 -0
- data/spec/definitions/model/alias/child.rb +5 -0
- data/spec/definitions/model/base/child.rb +5 -0
- data/spec/definitions/model/base/domain_object.rb +5 -0
- data/spec/definitions/model/base/independent.rb +5 -0
- data/spec/definitions/model/defaults/child.rb +5 -0
- data/spec/definitions/model/dependency/child.rb +5 -0
- data/spec/definitions/model/dependency/parent.rb +6 -0
- data/spec/definitions/model/inverse/child.rb +5 -0
- data/spec/definitions/model/inverse/independent.rb +5 -0
- data/spec/definitions/model/inverse/parent.rb +5 -0
- data/spec/definitions/model/mandatory/child.rb +6 -0
- data/spec/dependency_spec.rb +47 -0
- data/spec/family_spec.rb +64 -0
- data/spec/inverse_spec.rb +53 -0
- data/spec/mandatory_spec.rb +43 -0
- data/spec/metadata_spec.rb +68 -0
- data/spec/resource_spec.rb +30 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/model.rb +19 -0
- data/test/fixtures/line_separator/cr_line_sep.txt +1 -0
- data/test/fixtures/line_separator/crlf_line_sep.txt +3 -0
- data/test/fixtures/line_separator/lf_line_sep.txt +3 -0
- data/test/fixtures/mixed/ext/build.xml +35 -0
- data/test/fixtures/mixed/ext/src/mixed/Case/Example.java +5 -0
- data/test/helper.rb +7 -0
- data/test/lib/jinx/command_test.rb +41 -0
- data/test/lib/jinx/helpers/boolean_test.rb +27 -0
- data/test/lib/jinx/helpers/class_test.rb +60 -0
- data/test/lib/jinx/helpers/collections_test.rb +402 -0
- data/test/lib/jinx/helpers/file_separator_test.rb +29 -0
- data/test/lib/jinx/helpers/inflector_test.rb +11 -0
- data/test/lib/jinx/helpers/lazy_hash_test.rb +32 -0
- data/test/lib/jinx/helpers/module_test.rb +24 -0
- data/test/lib/jinx/helpers/options_test.rb +66 -0
- data/test/lib/jinx/helpers/partial_order_test.rb +41 -0
- data/test/lib/jinx/helpers/pretty_print_test.rb +83 -0
- data/test/lib/jinx/helpers/stopwatch_test.rb +16 -0
- data/test/lib/jinx/helpers/transitive_closure_test.rb +80 -0
- data/test/lib/jinx/helpers/visitor_test.rb +288 -0
- data/test/lib/jinx/import/java_test.rb +78 -0
- data/test/lib/jinx/import/mixed_case_test.rb +16 -0
- metadata +272 -0
@@ -0,0 +1,500 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
require 'jinx/helpers/collections'
|
3
|
+
|
4
|
+
require 'jinx/metadata/property'
|
5
|
+
require 'jinx/metadata/attribute_enumerator'
|
6
|
+
|
7
|
+
module Jinx
|
8
|
+
# Meta-data mix-in for attribute accessors.
|
9
|
+
module Propertied
|
10
|
+
# @return [<Symbol>] this class's attributes
|
11
|
+
attr_reader :attributes
|
12
|
+
|
13
|
+
# @return [Hashable] the default attribute => value associations
|
14
|
+
attr_reader :defaults
|
15
|
+
|
16
|
+
# Returns whether this class has an attribute with the given symbol.
|
17
|
+
#
|
18
|
+
# @param [Symbol] symbol the potential attribute
|
19
|
+
# @return [Boolean] whether there is a corresponding attribute
|
20
|
+
def property_defined?(symbol)
|
21
|
+
unless Symbol === symbol then
|
22
|
+
Jinx.fail(ArgumentError, "Property argument #{symbol.qp} of type #{symbol.class.qp} is not a symbol")
|
23
|
+
end
|
24
|
+
!!@alias_std_prop_map[symbol.to_sym]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adds the given attribute to this Class.
|
28
|
+
#
|
29
|
+
# @param [Symbol] attribute the attribute to add
|
30
|
+
# @param [Class] type (see Property#initialize)
|
31
|
+
# @param flags (see Property#initialize)
|
32
|
+
# @return [Property] the attribute meta-data
|
33
|
+
def add_attribute(attribute, type, *flags)
|
34
|
+
prop = create_nonjava_property(attribute, type, *flags)
|
35
|
+
add_property(prop)
|
36
|
+
prop
|
37
|
+
end
|
38
|
+
|
39
|
+
# Adds the given attribute restriction to this Class.
|
40
|
+
# This method is intended for the exclusive use of {Property.restrict}.
|
41
|
+
# Clients restrict an attribute by calling that method.
|
42
|
+
#
|
43
|
+
# @param [Property] attribute the restricted attribute
|
44
|
+
def add_restriction(attribute)
|
45
|
+
add_property(attribute)
|
46
|
+
logger.debug { "Added restriction #{attribute} to #{qp}." }
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [<Symbol>] the primary key attributes
|
50
|
+
def primary_key_attributes
|
51
|
+
@prm_key or Class === self && superclass < Resource ? superclass.primary_key_attributes : Array::EMPTY_ARRAY
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns this class's secondary key attribute array.
|
55
|
+
# If this class's secondary key is not set, then the secondary key is the Metadata superclass
|
56
|
+
# secondary key, if any.
|
57
|
+
#
|
58
|
+
# @return [<Symbol>] the secondary key attributes
|
59
|
+
def secondary_key_attributes
|
60
|
+
@scnd_key or Class === self && superclass < Resource ? superclass.secondary_key_attributes : Array::EMPTY_ARRAY
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns this class's alternate key attribute array.
|
64
|
+
# If this class's secondary key is not set, then the alternate key is the {Metadata} superclass
|
65
|
+
# alternate key, if any.
|
66
|
+
#
|
67
|
+
# @return [<Symbol>] the alternate key attributes
|
68
|
+
def alternate_key_attributes
|
69
|
+
@alt_key or superclass < Resource ? superclass.alternate_key_attributes : Array::EMPTY_ARRAY
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [<Symbol>] the primary, secondary and alternate key attributes
|
73
|
+
def all_key_attributes
|
74
|
+
primary_key_attributes + secondary_key_attributes + alternate_key_attributes
|
75
|
+
end
|
76
|
+
|
77
|
+
# @yield [prop] operate on the given property
|
78
|
+
# @yieldparam [Property] prop the property in this class
|
79
|
+
def each_property(&block)
|
80
|
+
@prop_hash.each_value(&block)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return the Property for the given attribute symbol or alias
|
84
|
+
# @raise [NameError] if the attribute is not recognized
|
85
|
+
def property(attribute)
|
86
|
+
# Simple and predominant case is that the attribute is a standard attribute.
|
87
|
+
# Otherwise, resolve attribute to the standard symbol.
|
88
|
+
prop = @prop_hash[attribute] || @prop_hash[standard_attribute(attribute)]
|
89
|
+
# If not found, then raise a NameError.
|
90
|
+
if prop.nil? then
|
91
|
+
Jinx.fail(NameError, "#{name.demodulize} attribute not found: #{attribute}")
|
92
|
+
end
|
93
|
+
prop
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param [<Symbol>] attributes an attribute reference path leading from this class
|
97
|
+
# @return [<Property>] the corresponding property path
|
98
|
+
# @raise [ArgumentError] if there are no attributes or one of the attributes besides the last
|
99
|
+
# is not a domain attribute
|
100
|
+
# @raise (see #property)
|
101
|
+
def property_path(*attributes)
|
102
|
+
raise ArgumentError.new("#{self} property path attributes is missing") if attributes.empty?
|
103
|
+
# the property of the first attribute
|
104
|
+
prop = property(attributes.shift)
|
105
|
+
return [prop] if attributes.empty?
|
106
|
+
unless prop.type < Resource then
|
107
|
+
raise ArgumentError.new("#{self} property path attribute #{prop} is not a domain type")
|
108
|
+
end
|
109
|
+
# Prepend the first property to the remaining properties.
|
110
|
+
prop.type.property_path(*attributes).unshift(prop)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @param [Symbol, String] name_or_alias the attribute name or alias
|
114
|
+
# @return [Symbol] the standard attribute symbol for the given name or alias
|
115
|
+
# @raise [ArgumentError] if the attribute name or alias argument is missing
|
116
|
+
# @raise [NameError] if the attribute is not found
|
117
|
+
def standard_attribute(name_or_alias)
|
118
|
+
if name_or_alias.nil? then
|
119
|
+
Jinx.fail(ArgumentError, "#{qp} standard attribute call is missing the attribute name/alias parameter")
|
120
|
+
end
|
121
|
+
@alias_std_prop_map[name_or_alias.to_sym] or Jinx.fail(NameError, "#{self} attribute not found: #{name_or_alias}")
|
122
|
+
end
|
123
|
+
|
124
|
+
## Metadata ATTRIBUTE FILTERS ##
|
125
|
+
|
126
|
+
# @return [<Symbol>] the domain attributes which wrap a java attribute
|
127
|
+
# @see Property#java_property?
|
128
|
+
def java_attributes
|
129
|
+
@java_flt ||= attribute_filter { |prop| prop.java_property? }
|
130
|
+
end
|
131
|
+
|
132
|
+
alias :printable_attributes :java_attributes
|
133
|
+
|
134
|
+
# @return [<Symbol>] the domain attributes
|
135
|
+
def domain_attributes
|
136
|
+
@dom_flt ||= attribute_filter { |prop| prop.domain? }
|
137
|
+
end
|
138
|
+
|
139
|
+
# @return [<Symbol>] the non-domain Java attributes
|
140
|
+
def nondomain_attributes
|
141
|
+
@ndom_flt ||= attribute_filter { |prop| prop.java_property? and prop.nondomain? }
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [<Symbol>] the non-domain Java attribute wrapper attributes
|
145
|
+
def nondomain_java_attributes
|
146
|
+
@ndom_java_flt ||= nondomain_attributes.compose { |prop| prop.java_property? }
|
147
|
+
end
|
148
|
+
|
149
|
+
# @return [<Symbol>] the standard attributes which can be merged into an instance of the subject class.
|
150
|
+
# The default mergeable attributes consist of the {#nondomain_java_attributes}.
|
151
|
+
# @see Mergeable#mergeable_attributes
|
152
|
+
alias :mergeable_attributes :nondomain_java_attributes
|
153
|
+
|
154
|
+
# @return [<Symbol>] the {Property#independent?} attributes
|
155
|
+
def independent_attributes
|
156
|
+
@ind_flt ||= attribute_filter { |prop| prop.independent? }
|
157
|
+
end
|
158
|
+
|
159
|
+
# @param [Boolean, nil] inc_super flag indicating whether to include dependents defined in the superclass
|
160
|
+
# @return [<Symbol>] the dependent attributes
|
161
|
+
def dependent_attributes(inc_super=true)
|
162
|
+
if inc_super then
|
163
|
+
@dep_flt ||= attribute_filter { |prop| prop.dependent? }
|
164
|
+
else
|
165
|
+
@local_dep_flt ||= dependent_attributes.compose { |prop| prop.declarer == self }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# @return [<Symbol>] the unidirectional dependent attributes
|
170
|
+
# @see Property#unidirectional?
|
171
|
+
def unidirectional_dependent_attributes
|
172
|
+
@uni_dep_flt ||= dependent_attributes.compose { |prop| prop.unidirectional? }
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns the subject class's required attributes, determined as follows:
|
176
|
+
# * An attribute marked with the :mandatory flag is mandatory.
|
177
|
+
# * An attribute marked with the :optional or :autogenerated flag is not mandatory.
|
178
|
+
# * Otherwise, A secondary key or owner attribute is mandatory.
|
179
|
+
def mandatory_attributes
|
180
|
+
@mnd_flt ||= collect_mandatory_attributes
|
181
|
+
end
|
182
|
+
|
183
|
+
def nonowner_attributes
|
184
|
+
@nownr_atts ||= attribute_filter { |prop| not prop.owner? }
|
185
|
+
end
|
186
|
+
|
187
|
+
# @return [<Symbol>] # the non-owner secondary key domain attributes
|
188
|
+
def secondary_key_non_owner_domain_attributes
|
189
|
+
@scd_key_nown_flt ||= attribute_filter(secondary_key_attributes) { |prop| prop.domain? and not prop.owner? }
|
190
|
+
end
|
191
|
+
|
192
|
+
# @param [Symbol] attribute the attribute to check
|
193
|
+
# @return [Boolean] whether attribute return type is a domain object or collection thereof
|
194
|
+
def domain_attribute?(attribute)
|
195
|
+
property(attribute).domain?
|
196
|
+
end
|
197
|
+
|
198
|
+
# @param [Symbol] attribute the attribute to check
|
199
|
+
# @return [Boolean] whether attribute is not a domain attribute
|
200
|
+
def nondomain_attribute?(attribute)
|
201
|
+
not domain_attribute?(attribute)
|
202
|
+
end
|
203
|
+
|
204
|
+
# @param [Symbol] attribute the attribute to check
|
205
|
+
# @return [Boolean] whether attribute is an instance of a Java domain class
|
206
|
+
def collection_attribute?(attribute)
|
207
|
+
property(attribute).collection?
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns an {AttributeEnumerator} on this Resource class's attributes which iterates on each
|
211
|
+
# of the given attributes. If a filter block is given, then only those properties which
|
212
|
+
# satisfy the filter block are enumerated.
|
213
|
+
#
|
214
|
+
# @param [<Symbol>, nil] attributes the optional attributes to filter on (default all attributes)
|
215
|
+
# @yield [prop] the optional attribute selector
|
216
|
+
# @yieldparam [Property] prop the candidate attribute
|
217
|
+
# @return [AttributeEnumerator] a new attribute enumerator
|
218
|
+
def attribute_filter(attributes=nil, &filter)
|
219
|
+
# make the attribute filter
|
220
|
+
raise MetadataError.new("#{self} has not been introspected") if @prop_hash.nil?
|
221
|
+
ph = attributes ? attributes.to_compact_hash { |pa| @prop_hash[pa] } : @prop_hash
|
222
|
+
AttributeEnumerator.new(ph, &filter)
|
223
|
+
end
|
224
|
+
|
225
|
+
protected
|
226
|
+
|
227
|
+
# @return [{Symbol => Property}] the attribute => metadata hash
|
228
|
+
def property_hash
|
229
|
+
@prop_hash
|
230
|
+
end
|
231
|
+
|
232
|
+
# @return [{Symbol => Symbol}] the attribute alias => standard hash
|
233
|
+
def alias_standard_attribute_hash
|
234
|
+
@alias_std_prop_map
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
# Initializes the property meta-data structures.
|
240
|
+
def init_property_classifiers
|
241
|
+
@local_std_prop_hash = {}
|
242
|
+
@alias_std_prop_map = append_ancestor_enum(@local_std_prop_hash) { |par| par.alias_standard_attribute_hash }
|
243
|
+
@local_prop_hash = {}
|
244
|
+
@prop_hash = append_ancestor_enum(@local_prop_hash) { |par| par.property_hash }
|
245
|
+
@attributes = Enumerable::Enumerator.new(@prop_hash, :each_key)
|
246
|
+
@local_mndty_flt = Set.new
|
247
|
+
@local_defaults = {}
|
248
|
+
@defaults = append_ancestor_enum(@local_defaults) { |par| par.defaults }
|
249
|
+
end
|
250
|
+
|
251
|
+
# @param (see #add_attribute)
|
252
|
+
# @return (see #add_attribute)
|
253
|
+
def create_nonjava_property(attribute, type, *flags)
|
254
|
+
Property.new(attribute, self, type, *flags)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns the most specific attribute which references the given target type, or nil if none.
|
258
|
+
# If the given class can be returned by more than on of the attributes, then the attribute
|
259
|
+
# is chosen whose return type most closely matches the given class.
|
260
|
+
#
|
261
|
+
# @param [Class] klass the target type
|
262
|
+
# @param [AttributeEnumerator, nil] attributes the attributes to check (default all domain attributes)
|
263
|
+
# @return [Symbol, nil] the most specific reference attribute, or nil if none
|
264
|
+
def most_specific_domain_attribute(klass, attributes=nil)
|
265
|
+
attributes ||= domain_attributes
|
266
|
+
candidates = attributes.properties
|
267
|
+
best = candidates.inject(nil) do |better, prop|
|
268
|
+
# If the attribute can return the klass then the return type is a candidate.
|
269
|
+
# In that case, the klass replaces the best candidate if it is more specific than
|
270
|
+
# the best candidate so far.
|
271
|
+
klass <= prop.type ? (better && better.type <= prop.type ? better : prop) : better
|
272
|
+
end
|
273
|
+
if best then
|
274
|
+
logger.debug { "Most specific #{qp} -> #{klass.qp} reference from among #{candidates.qp} is #{best.declarer.qp}.#{best}." }
|
275
|
+
best.to_sym
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Detects the first attribute with the given type.
|
280
|
+
#
|
281
|
+
# @param [Class] klass the target attribute type
|
282
|
+
# @return [Symbol, nil] the attribute with the given type
|
283
|
+
def detect_attribute_with_type(klass)
|
284
|
+
property_hash.detect_key_with_value { |prop| prop.type == klass }
|
285
|
+
end
|
286
|
+
|
287
|
+
# Creates the given attribute alias. If the attribute metadata is registered with this class, then
|
288
|
+
# this method overrides +Class.alias_attribute+ to create a new alias reader (writer) method
|
289
|
+
# which delegates to the attribute reader (writer, resp.). This aliasing mechanism differs from
|
290
|
+
# {Class#alias_attribute}, which directly aliases the existing reader or writer method.
|
291
|
+
# Delegation allows the alias to pick up run-time redefinitions of the aliased reader and writer.
|
292
|
+
# If the attribute metadata is not registered with this class, then this method delegates to
|
293
|
+
# {Class#alias_attribute}.
|
294
|
+
#
|
295
|
+
# @param [Symbol] aliaz the attribute alias
|
296
|
+
# @param [Symbol] attribute the attribute to alias
|
297
|
+
def alias_attribute(aliaz, attribute)
|
298
|
+
if property_defined?(attribute) then
|
299
|
+
delegate_to_attribute(aliaz, attribute)
|
300
|
+
register_property_alias(aliaz, attribute)
|
301
|
+
else
|
302
|
+
super
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Creates the given aliases to attributes.
|
307
|
+
#
|
308
|
+
# @param [{Symbol => Symbol}] hash the alias => attribute hash
|
309
|
+
# @see #attribute_alias
|
310
|
+
# @deprecated Use {#alias_attribute} instead
|
311
|
+
def add_attribute_aliases(hash)
|
312
|
+
hash.each { |aliaz, pa| alias_attribute(aliaz, pa) }
|
313
|
+
end
|
314
|
+
|
315
|
+
# Adds the given attribute to this class's primary key.
|
316
|
+
def add_primary_key_attribute(attribute)
|
317
|
+
@prm_key ||= []
|
318
|
+
@prm_key << standard_attribute(attribute)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Sets this class's primary key attributes to the given attributes.
|
322
|
+
# If attributes is set to nil, then the primary key is cleared.
|
323
|
+
def set_primary_key_attributes(*attributes)
|
324
|
+
attributes.each { |a| add_primary_key_attribute(a) }
|
325
|
+
end
|
326
|
+
|
327
|
+
# Adds the given attribute to this class's secondary key.
|
328
|
+
def add_secondary_key_attribute(attribute)
|
329
|
+
@scnd_key ||= []
|
330
|
+
@scnd_key << standard_attribute(attribute)
|
331
|
+
end
|
332
|
+
|
333
|
+
# Sets this class's secondary key attributes to the given attributes.
|
334
|
+
# If attributes is set to nil, then the secondary key is cleared.
|
335
|
+
def set_secondary_key_attributes(*attributes)
|
336
|
+
attributes.each { |a| add_secondary_key_attribute(a) }
|
337
|
+
end
|
338
|
+
|
339
|
+
# Adds the given attribute to this class's alternate key.
|
340
|
+
def add_alternate_key_attribute(attribute)
|
341
|
+
@alt_key ||= []
|
342
|
+
@alt_key << standard_attribute(attribute)
|
343
|
+
end
|
344
|
+
|
345
|
+
# Sets this class's alternate key attributes to the given attributes.
|
346
|
+
# If attributes is set to nil, then the alternate key is cleared.
|
347
|
+
def set_alternate_key_attributes(*attributes)
|
348
|
+
attributes.each { |a| add_alternate_key_attribute(a) }
|
349
|
+
end
|
350
|
+
|
351
|
+
# Sets the given attribute type to klass. If attribute is defined in a superclass,
|
352
|
+
# then klass must be a subclass of the superclass attribute type.
|
353
|
+
#
|
354
|
+
# @param [Symbol] attribute the attribute to modify
|
355
|
+
# @param [Class] klass the attribute type
|
356
|
+
# @raise [ArgumentError] if the new type is incompatible with the current attribute type
|
357
|
+
def set_attribute_type(attribute, klass)
|
358
|
+
prop = property(attribute)
|
359
|
+
# degenerate no-op case
|
360
|
+
return if klass == prop.type
|
361
|
+
# If this class is the declarer, then simply set the attribute type.
|
362
|
+
# Otherwise, if the attribute type is unspecified or is a superclass of the given class,
|
363
|
+
# then make a new attribute metadata for this class.
|
364
|
+
if prop.declarer == self then
|
365
|
+
prop.type = klass
|
366
|
+
logger.debug { "Set #{qp}.#{attribute} type to #{klass.qp}." }
|
367
|
+
elsif prop.type.nil? or klass < prop.type then
|
368
|
+
prop.restrict(self, :type => klass)
|
369
|
+
logger.debug { "Restricted #{prop.declarer.qp}.#{attribute}(#{prop.type.qp}) to #{qp} with return type #{klass.qp}." }
|
370
|
+
else
|
371
|
+
Jinx.fail(ArgumentError, "Cannot reset #{qp}.#{attribute} type #{prop.type.qp} to incompatible #{klass.qp}")
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# @param [Hash] hash the attribute => value defaults
|
376
|
+
def add_attribute_defaults(hash)
|
377
|
+
hash.each { |da, value| add_attribute_default(da, value) }
|
378
|
+
end
|
379
|
+
|
380
|
+
# @param [Symbol] attribute the attribute
|
381
|
+
# @param value the default value
|
382
|
+
def add_attribute_default(attribute, value)
|
383
|
+
@local_defaults[standard_attribute(attribute)] = value
|
384
|
+
end
|
385
|
+
|
386
|
+
# @param [<Symbol>] attributes the mandatory attributes
|
387
|
+
def add_mandatory_attributes(*attributes)
|
388
|
+
attributes.each { |ma| add_mandatory_attribute(ma) }
|
389
|
+
end
|
390
|
+
|
391
|
+
# @param [Symbol] attribute the mandatory attribute
|
392
|
+
def add_mandatory_attribute(attribute)
|
393
|
+
@local_mndty_flt << standard_attribute(attribute)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Marks the given attribute with flags supported by {Property#qualify}.
|
397
|
+
#
|
398
|
+
# @param [Symbol] attribute the attribute to qualify
|
399
|
+
# @param [{Symbol => Object}] the flags to apply to the restricted attribute
|
400
|
+
def qualify_attribute(attribute, *flags)
|
401
|
+
prop = property(attribute)
|
402
|
+
if prop.declarer == self then
|
403
|
+
prop.qualify(*flags)
|
404
|
+
else
|
405
|
+
logger.debug { "Restricting #{prop.declarer.qp}.#{attribute} to #{qp} with additional flags #{flags.to_series}" }
|
406
|
+
prop.restrict_flags(self, *flags)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
# Removes the given attribute from this Resource.
|
411
|
+
# An attribute declared in a superclass Resource is hidden from this Resource but retained in
|
412
|
+
# the declaring Resource.
|
413
|
+
def remove_attribute(attribute)
|
414
|
+
std_prop = standard_attribute(attribute)
|
415
|
+
# if the attribute is local, then delete it, otherwise filter out the superclass attribute
|
416
|
+
prop = @local_prop_hash.delete(std_prop)
|
417
|
+
if prop then
|
418
|
+
# clear the inverse, if any
|
419
|
+
prop.inverse = nil
|
420
|
+
# remove from the mandatory attributes, if necessary
|
421
|
+
@local_mndty_flt.delete(std_prop)
|
422
|
+
# remove from the attribute => metadata hash
|
423
|
+
@local_std_prop_hash.delete_if { |aliaz, pa| pa == std_prop }
|
424
|
+
else
|
425
|
+
# Filter the superclass hashes.
|
426
|
+
anc_prop_hash = @prop_hash.components[1]
|
427
|
+
@prop_hash.components[1] = anc_prop_hash.filter_on_key { |pa| pa != attribute }
|
428
|
+
anc_alias_hash = @alias_std_prop_map.components[1]
|
429
|
+
@alias_std_prop_map.components[1] = anc_alias_hash.filter_on_key { |pa| pa != attribute }
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# @param [Property] the property to add
|
434
|
+
def add_property(property)
|
435
|
+
pa = property.attribute
|
436
|
+
@local_prop_hash[pa] = property
|
437
|
+
# map the attribute symbol to itself in the alias map
|
438
|
+
@local_std_prop_hash[pa] = pa
|
439
|
+
end
|
440
|
+
|
441
|
+
# Registers an alias to an attribute.
|
442
|
+
#
|
443
|
+
# @param (see #alias_attribute)
|
444
|
+
def register_property_alias(aliaz, attribute)
|
445
|
+
std = standard_attribute(attribute)
|
446
|
+
Jinx.fail(ArgumentError, "#{self} attribute not found: #{attribute}") if std.nil?
|
447
|
+
@local_std_prop_hash[aliaz.to_sym] = std
|
448
|
+
end
|
449
|
+
|
450
|
+
# Appends to the given enumerable the result of evaluating the block given to this method
|
451
|
+
# on the superclass, if the superclass is in the same parent module as this class.
|
452
|
+
#
|
453
|
+
# @param [Enumerable] enum the base collection
|
454
|
+
# @return [Enumerable] the {Enumerable#union} of the base collection with the superclass
|
455
|
+
# collection, if applicable
|
456
|
+
def append_ancestor_enum(enum)
|
457
|
+
return enum unless Class === self and superclass.parent_module == parent_module
|
458
|
+
anc_enum = yield superclass
|
459
|
+
if anc_enum.nil? then
|
460
|
+
Jinx.fail(MetadataError, "#{qp} superclass #{superclass.qp} does not have required metadata")
|
461
|
+
end
|
462
|
+
enum.union(anc_enum)
|
463
|
+
end
|
464
|
+
|
465
|
+
# Makes a new synthetic {Class#offset_attr_accessor} attribute for each
|
466
|
+
# _method_ => _original_ hash entry.
|
467
|
+
#
|
468
|
+
# @param (see Class#offset_attr_accessor)
|
469
|
+
def offset_attribute(hash, offset=nil)
|
470
|
+
offset_attr_accessor(hash, offset)
|
471
|
+
hash.each { |ja, original| add_attribute(ja, property(original).type) }
|
472
|
+
end
|
473
|
+
|
474
|
+
# Merges the secondary key, owner and additional mandatory attributes defined in the attributes.
|
475
|
+
#
|
476
|
+
# @see #mandatory_attributes
|
477
|
+
def collect_mandatory_attributes
|
478
|
+
@local_mndty_flt.merge!(default_mandatory_local_attributes)
|
479
|
+
append_ancestor_enum(@local_mndty_flt) { |par| par.mandatory_attributes }
|
480
|
+
end
|
481
|
+
|
482
|
+
def default_mandatory_local_attributes
|
483
|
+
mandatory = Set.new
|
484
|
+
# add the secondary key
|
485
|
+
mandatory.merge(secondary_key_attributes)
|
486
|
+
# add the owner attribute, if any
|
487
|
+
oa = mandatory_owner_attribute
|
488
|
+
mandatory << oa if oa
|
489
|
+
# remove optional attributes
|
490
|
+
mandatory.delete_if { |ma| property(ma).flags.include?(:optional) }
|
491
|
+
end
|
492
|
+
|
493
|
+
# @return [Symbol, nil] the unique non-self-referential owner attribute, if one exists
|
494
|
+
def mandatory_owner_attribute
|
495
|
+
oa = owner_attribute || return
|
496
|
+
prop = property(oa)
|
497
|
+
oa if prop.java_property? and prop.type != self
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|