jinx 2.1.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/.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
|