activefacts 0.7.2 → 0.7.3
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/Manifest.txt +1 -0
- data/Rakefile +3 -0
- data/bin/afgen +9 -3
- data/bin/cql +0 -0
- data/examples/CQL/Address.cql +7 -7
- data/examples/CQL/Blog.cql +8 -8
- data/examples/CQL/CompanyDirectorEmployee.cql +3 -3
- data/examples/CQL/Death.cql +2 -2
- data/examples/CQL/Genealogy.cql +21 -21
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +34 -29
- data/examples/CQL/MultiInheritance.cql +3 -3
- data/examples/CQL/OilSupply.cql +9 -9
- data/examples/CQL/Orienteering.cql +27 -27
- data/examples/CQL/PersonPlaysGame.cql +2 -2
- data/examples/CQL/SchoolActivities.cql +3 -3
- data/examples/CQL/SimplestUnary.cql +1 -1
- data/examples/CQL/SubtypePI.cql +4 -4
- data/examples/CQL/Warehousing.cql +12 -12
- data/examples/CQL/WindowInRoomInBldg.cql +4 -4
- data/lib/activefacts/api/concept.rb +3 -2
- data/lib/activefacts/api/constellation.rb +1 -1
- data/lib/activefacts/api/entity.rb +12 -1
- data/lib/activefacts/api/instance.rb +1 -1
- data/lib/activefacts/api/role.rb +1 -1
- data/lib/activefacts/api/standard_types.rb +9 -1
- data/lib/activefacts/api/support.rb +4 -0
- data/lib/activefacts/api/value.rb +1 -0
- data/lib/activefacts/api/vocabulary.rb +2 -59
- data/lib/activefacts/cql/DataTypes.treetop +10 -1
- data/lib/activefacts/cql/Expressions.treetop +1 -1
- data/lib/activefacts/cql/FactTypes.treetop +1 -1
- data/lib/activefacts/cql/Language/English.treetop +2 -2
- data/lib/activefacts/generate/absorption.rb +0 -2
- data/lib/activefacts/generate/cql.rb +6 -8
- data/lib/activefacts/generate/cql/html.rb +1 -1
- data/lib/activefacts/generate/oo.rb +60 -40
- data/lib/activefacts/generate/ordered.rb +30 -21
- data/lib/activefacts/generate/ruby.rb +38 -15
- data/lib/activefacts/generate/sql/mysql.rb +257 -0
- data/lib/activefacts/generate/sql/server.rb +0 -1
- data/lib/activefacts/input/cql.rb +0 -2
- data/lib/activefacts/persistence/columns.rb +51 -24
- data/lib/activefacts/persistence/concept.rb +158 -36
- data/lib/activefacts/persistence/reference.rb +13 -8
- data/lib/activefacts/support.rb +40 -2
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +5 -6
- data/spec/absorption_spec.rb +8 -11
- data/spec/api/autocounter.rb +1 -1
- data/spec/api/constellation.rb +1 -1
- data/spec/api/entity_type.rb +1 -1
- data/spec/api/instance.rb +1 -1
- data/spec/api/roles.rb +1 -1
- data/spec/api/value_type.rb +1 -1
- data/spec/cql_cql_spec.rb +2 -4
- data/spec/cql_parse_spec.rb +2 -4
- data/spec/cql_ruby_spec.rb +2 -4
- data/spec/cql_sql_spec.rb +4 -4
- data/spec/cql_symbol_tables_spec.rb +1 -1
- data/spec/cql_unit_spec.rb +6 -6
- data/spec/cqldump_spec.rb +6 -6
- data/spec/norma_cql_spec.rb +2 -4
- data/spec/norma_ruby_spec.rb +2 -4
- data/spec/norma_sql_spec.rb +2 -4
- data/spec/norma_tables_spec.rb +4 -7
- metadata +29 -6
@@ -3,18 +3,18 @@ vocabulary Warehousing;
|
|
3
3
|
/*
|
4
4
|
* Value Types
|
5
5
|
*/
|
6
|
-
BinID is
|
7
|
-
DispatchID is
|
8
|
-
DispatchItemID is
|
9
|
-
PartyID is
|
10
|
-
ProductID is
|
11
|
-
PurchaseOrderID is
|
12
|
-
Quantity is
|
13
|
-
ReceiptID is
|
14
|
-
ReceivedItemID is
|
15
|
-
SalesOrderID is
|
16
|
-
TransferRequestID is
|
17
|
-
WarehouseID is
|
6
|
+
BinID is written as AutoCounter();
|
7
|
+
DispatchID is written as AutoCounter();
|
8
|
+
DispatchItemID is written as AutoCounter();
|
9
|
+
PartyID is written as AutoCounter();
|
10
|
+
ProductID is written as AutoCounter();
|
11
|
+
PurchaseOrderID is written as AutoCounter();
|
12
|
+
Quantity is written as UnsignedInteger(32);
|
13
|
+
ReceiptID is written as AutoCounter();
|
14
|
+
ReceivedItemID is written as AutoCounter();
|
15
|
+
SalesOrderID is written as AutoCounter();
|
16
|
+
TransferRequestID is written as AutoCounter();
|
17
|
+
WarehouseID is written as AutoCounter();
|
18
18
|
|
19
19
|
/*
|
20
20
|
* Entity Types
|
@@ -3,10 +3,10 @@ vocabulary WindowInRoomInBldg;
|
|
3
3
|
/*
|
4
4
|
* Value Types
|
5
5
|
*/
|
6
|
-
Building is
|
7
|
-
RoomNumber is
|
8
|
-
WallNumber is
|
9
|
-
WindowNumber is
|
6
|
+
Building is written as SignedInteger(32);
|
7
|
+
RoomNumber is written as SignedInteger(32);
|
8
|
+
WallNumber is written as SignedInteger(32);
|
9
|
+
WindowNumber is written as UnsignedInteger(32);
|
10
10
|
|
11
11
|
/*
|
12
12
|
* Entity Types
|
@@ -172,7 +172,7 @@ module ActiveFacts
|
|
172
172
|
def define_binary_fact_type(one_to_one, role_name, related, mandatory, related_role_name)
|
173
173
|
# puts "#{self}.#{role_name} is to #{related.inspect}, #{mandatory ? :mandatory : :optional}, related role is #{related_role_name}"
|
174
174
|
|
175
|
-
raise "#{
|
175
|
+
raise "#{name} cannot have more than one role named #{role_name}" if roles[role_name]
|
176
176
|
roles[role_name] = role = Role.new(self, related, nil, role_name, mandatory)
|
177
177
|
|
178
178
|
# There may be a forward reference here where role_name is a Symbol,
|
@@ -350,6 +350,7 @@ module ActiveFacts
|
|
350
350
|
|
351
351
|
# resolve the Symbol to a Class now if possible:
|
352
352
|
resolved = vocabulary.concept(related) rescue nil
|
353
|
+
#puts "#{related} resolves to #{resolved}"
|
353
354
|
related = resolved if resolved
|
354
355
|
# puts "related = #{related.inspect}"
|
355
356
|
|
@@ -399,7 +400,7 @@ module ActiveFacts
|
|
399
400
|
when String # Arrange for this to happen later
|
400
401
|
vocabulary.__delay(concept, args, &block)
|
401
402
|
else
|
402
|
-
raise "Delayed binding not possible for #{concept.inspect}"
|
403
|
+
raise "Delayed binding not possible for #{concept.class.name} #{concept.inspect}"
|
403
404
|
end
|
404
405
|
end
|
405
406
|
end
|
@@ -79,7 +79,7 @@ module ActiveFacts
|
|
79
79
|
|
80
80
|
# REVISIT: It would be better not to rely on the role name pattern here:
|
81
81
|
single_roles, multiple_roles = klass.roles.keys.sort_by(&:to_s).partition{|r| r.to_s !~ /\Aall_/ }
|
82
|
-
single_roles -= klass.identifying_role_names if (klass.
|
82
|
+
single_roles -= klass.identifying_role_names if (klass.is_entity_type)
|
83
83
|
# REVISIT: Need to include superclass roles also.
|
84
84
|
|
85
85
|
instances = send(concept.to_sym)
|
@@ -109,6 +109,16 @@ module ActiveFacts
|
|
109
109
|
@identifying_role_names ||= []
|
110
110
|
end
|
111
111
|
|
112
|
+
def identifying_roles
|
113
|
+
debug :persistence, "Identifying roles for #{basename}" do
|
114
|
+
@identifying_role_names.map{|name|
|
115
|
+
role = roles[name] || (!superclass.is_entity_type || superclass.roles[name])
|
116
|
+
debug :persistence, "#{name} -> #{role ? "found" : "NOT FOUND"}"
|
117
|
+
role
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
112
122
|
# Convert the passed arguments into an array of Instance objects that can identify an instance of this Entity type:
|
113
123
|
def identifying_role_values(*args)
|
114
124
|
#puts "Getting identifying role values #{identifying_role_names.inspect} of #{basename} using #{args.inspect}"
|
@@ -211,7 +221,7 @@ module ActiveFacts
|
|
211
221
|
# inherited from a superclass.
|
212
222
|
def initialise_entity_type(*args) #:nodoc:
|
213
223
|
#puts "Initialising entity type #{self} using #{args.inspect}"
|
214
|
-
@identifying_role_names = superclass.identifying_role_names if superclass.
|
224
|
+
@identifying_role_names = superclass.identifying_role_names if superclass.is_entity_type
|
215
225
|
# REVISIT: @identifying_role_names here are the symbols passed in, not the Role objects we should use.
|
216
226
|
# We'd need late binding to use Role objects...
|
217
227
|
@identifying_role_names = args if args.size > 0 || !@identifying_role_names
|
@@ -235,6 +245,7 @@ module ActiveFacts
|
|
235
245
|
|
236
246
|
# Register ourselves with the parent module, which has become a Vocabulary:
|
237
247
|
vocabulary = other.modspace
|
248
|
+
# puts "Entity.included(#{other.inspect})"
|
238
249
|
unless vocabulary.respond_to? :concept # Extend module with Vocabulary if necessary
|
239
250
|
vocabulary.send :extend, Vocabulary
|
240
251
|
end
|
@@ -14,7 +14,7 @@ module ActiveFacts
|
|
14
14
|
attr_accessor :constellation
|
15
15
|
|
16
16
|
def initialize(args = []) #:nodoc:
|
17
|
-
unless (self.class.
|
17
|
+
unless (self.class.is_entity_type)
|
18
18
|
#if (self.class.superclass != Object)
|
19
19
|
# puts "constructing #{self.class.superclass} with #{args.inspect}"
|
20
20
|
super(*args)
|
data/lib/activefacts/api/role.rb
CHANGED
@@ -30,7 +30,7 @@ module ActiveFacts
|
|
30
30
|
@name = name
|
31
31
|
@mandatory = mandatory
|
32
32
|
@unique = unique
|
33
|
-
@is_identifying = @owner.
|
33
|
+
@is_identifying = @owner.is_entity_type && @owner.identifying_role_names.include?(@name)
|
34
34
|
end
|
35
35
|
|
36
36
|
# Is this role a unary (created by maybe)? If so, it has no counterpart
|
@@ -43,10 +43,14 @@ class Class
|
|
43
43
|
# Make this Class into a Concept and if necessary its module into a Vocabulary.
|
44
44
|
# The parameters are the names (Symbols) of the identifying roles.
|
45
45
|
def identified_by *args
|
46
|
-
raise "not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
|
46
|
+
raise "#{basename} is not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
|
47
47
|
include ActiveFacts::API::Entity
|
48
48
|
initialise_entity_type(*args)
|
49
49
|
end
|
50
|
+
|
51
|
+
def is_entity_type
|
52
|
+
respond_to?(:identifying_role_names)
|
53
|
+
end
|
50
54
|
end
|
51
55
|
|
52
56
|
# REVISIT: Fix these NORMA types
|
@@ -56,10 +60,14 @@ class SignedInteger < Int #:nodoc:
|
|
56
60
|
end
|
57
61
|
class SignedSmallInteger < Int #:nodoc:
|
58
62
|
end
|
63
|
+
class SignedTinyInteger < Int #:nodoc:
|
64
|
+
end
|
59
65
|
class UnsignedInteger < Int #:nodoc:
|
60
66
|
end
|
61
67
|
class UnsignedSmallInteger < Int #:nodoc:
|
62
68
|
end
|
69
|
+
class UnsignedTinyInteger < Int #:nodoc:
|
70
|
+
end
|
63
71
|
class LargeLengthText < String #:nodoc:
|
64
72
|
end
|
65
73
|
class FixedLengthText < String #:nodoc:
|
@@ -118,6 +118,7 @@ module ActiveFacts
|
|
118
118
|
|
119
119
|
# Register ourselves with the parent module, which has become a Vocabulary:
|
120
120
|
vocabulary = other.modspace
|
121
|
+
# puts "ValueType.included(#{other.inspect})"
|
121
122
|
unless vocabulary.respond_to? :concept # Extend module with Vocabulary if necessary
|
122
123
|
vocabulary.send :extend, Vocabulary
|
123
124
|
end
|
@@ -28,7 +28,7 @@ module ActiveFacts
|
|
28
28
|
__bind(camel)
|
29
29
|
return c
|
30
30
|
end
|
31
|
-
return (const_get(camel) rescue nil)
|
31
|
+
return (const_get("#{name}::#{camel}") rescue nil)
|
32
32
|
end
|
33
33
|
|
34
34
|
def add_concept(klass) #:nodoc:
|
@@ -48,6 +48,7 @@ module ActiveFacts
|
|
48
48
|
# __bind raises an error if the named class doesn't exist yet.
|
49
49
|
def __bind(concept_name) #:nodoc:
|
50
50
|
concept = const_get(concept_name)
|
51
|
+
# puts "#{name}.__bind #{concept_name} -> #{concept.name}" if concept
|
51
52
|
if (@delayed && @delayed.include?(concept_name))
|
52
53
|
# $stderr.puts "#{concept_name} was delayed, binding now"
|
53
54
|
d = @delayed[concept_name]
|
@@ -67,64 +68,6 @@ module ActiveFacts
|
|
67
68
|
}*"\n\t"
|
68
69
|
end
|
69
70
|
|
70
|
-
=begin
|
71
|
-
# Create or find an instance of klass in constellation using value to identify it
|
72
|
-
def adopt(klass, constellation, value) #:nodoc:
|
73
|
-
puts "Adopting #{ value.verbalise rescue value.class.to_s+' '+value.inspect} as #{klass} into constellation #{constellation.object_id}"
|
74
|
-
|
75
|
-
path = "unknown"
|
76
|
-
# Create a value instance we can hack if the value isn't already in this constellation
|
77
|
-
if (c = constellation)
|
78
|
-
if value.is_a?(klass) # Right class?
|
79
|
-
value = value.__getobj__ if RoleProxy === value
|
80
|
-
vc = value.constellation rescue nil
|
81
|
-
if (c == vc) # Right constellation?
|
82
|
-
# Already right class, in the right constellation
|
83
|
-
path = "right constellation, right class, just use it"
|
84
|
-
else
|
85
|
-
# We need a new object from our constellation, so copy the value.
|
86
|
-
if klass.respond_to?(:identifying_role_names)
|
87
|
-
# Make a new entity having only the identifying roles set.
|
88
|
-
# Someone will complain that this is wrong, and all functional role values should also
|
89
|
-
# be cloned, and I'm listening... but not there yet. Why just those?
|
90
|
-
cloned = c.send(
|
91
|
-
:"#{klass.basename}",
|
92
|
-
*klass.identifying_role_names.map{|role| value.send(role) }
|
93
|
-
)
|
94
|
-
path = "wrong constellation, right class, cloned entity"
|
95
|
-
else
|
96
|
-
# Just copy a value:
|
97
|
-
cloned = c.send(:"#{klass.basename}", *value)
|
98
|
-
path = "wrong constellation, right class, copied value"
|
99
|
-
end
|
100
|
-
value.constellation = c
|
101
|
-
end
|
102
|
-
else
|
103
|
-
# Wrong class, assume it's a valid constructor arg. Get our constellation to find/make it:
|
104
|
-
value = [ value ] unless Array === value
|
105
|
-
value = c.send(:"#{klass.basename}", *value)
|
106
|
-
path = "right constellation but wrong class, constructed from args"
|
107
|
-
end
|
108
|
-
else
|
109
|
-
# This object's not in a constellation
|
110
|
-
if value.is_a?(klass) # Right class?
|
111
|
-
value = value.__getobj__ if RoleProxy === value
|
112
|
-
if vc = value.constellation rescue nil
|
113
|
-
raise "REVISIT: Assigning to #{self.class.basename}.#{role_name} with constellation=#{c.inspect}: Can't dis-associate object from its constellation #{vc.object_id} yet"
|
114
|
-
end
|
115
|
-
# Right class, no constellation, just use it
|
116
|
-
path = "no constellation, correct class"
|
117
|
-
else
|
118
|
-
# Wrong class, construct one
|
119
|
-
value = klass.send(:new, *value)
|
120
|
-
path = "no constellation, constructed from wrong class"
|
121
|
-
end
|
122
|
-
end
|
123
|
-
# print "#{path}"; puts ", adopted as #{value.verbalise rescue value.inspect}"
|
124
|
-
value
|
125
|
-
end
|
126
|
-
=end
|
127
|
-
|
128
71
|
end
|
129
72
|
end
|
130
73
|
end
|
@@ -9,7 +9,7 @@ module ActiveFacts
|
|
9
9
|
grammar DataTypes
|
10
10
|
rule data_type
|
11
11
|
s name:id
|
12
|
-
( s '=' s /
|
12
|
+
( s '=' s / written_as )
|
13
13
|
base:id s
|
14
14
|
'(' s tpl:type_parameter_list? ')' s
|
15
15
|
u0:(!restricted u1:unit s)?
|
@@ -34,6 +34,15 @@ module ActiveFacts
|
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
37
|
+
rule unit_definition
|
38
|
+
numerator:number denominator:(s '/' s number)?
|
39
|
+
baseunits:(unit+ (s '/' s unit+)?) s
|
40
|
+
offset:(('+' / '-') s number)
|
41
|
+
'makes' s ('approximately' s)?
|
42
|
+
singular:id plural:(s '/' s id)?
|
43
|
+
(s '[' s 'ephemeral' s ']')?
|
44
|
+
end
|
45
|
+
|
37
46
|
rule type_parameter_list
|
38
47
|
head:number s tail:( ',' s number s )*
|
39
48
|
{
|
@@ -19,8 +19,6 @@ module ActiveFacts
|
|
19
19
|
# * no_identifier Don't show the identified_by columns for an EntityType
|
20
20
|
|
21
21
|
class ABSORPTION
|
22
|
-
include Metamodel
|
23
|
-
|
24
22
|
def initialize(vocabulary, *options) #:nodoc:
|
25
23
|
@vocabulary = vocabulary
|
26
24
|
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
@@ -14,8 +14,6 @@ module ActiveFacts
|
|
14
14
|
# afgen --cql <file>.cql
|
15
15
|
class CQL < OrderedDumper
|
16
16
|
private
|
17
|
-
include Metamodel
|
18
|
-
|
19
17
|
def vocabulary_start(vocabulary)
|
20
18
|
puts "vocabulary #{vocabulary.name};\n\n"
|
21
19
|
end
|
@@ -46,7 +44,7 @@ module ActiveFacts
|
|
46
44
|
|
47
45
|
#" restricted to {#{(allowed_values.map{|r| r.inspect}*", ").gsub('"',"'")}}")
|
48
46
|
|
49
|
-
puts "#{o.name} is
|
47
|
+
puts "#{o.name} is written as #{o.supertype.name}#{ parameters }#{
|
50
48
|
o.value_restriction ? " restricted to {#{
|
51
49
|
o.value_restriction.all_allowed_range.sort_by{|ar|
|
52
50
|
((min = ar.value_range.minimum_bound) && min.value) ||
|
@@ -342,7 +340,7 @@ module ActiveFacts
|
|
342
340
|
end
|
343
341
|
#puts "#{c.class.basename} has players #{players.map{|p| p.name}*", "}"
|
344
342
|
|
345
|
-
if (SetEqualityConstraint
|
343
|
+
if c.is_a?(ActiveFacts::Metamodel::SetEqualityConstraint)
|
346
344
|
# REVISIT: Need a proper approach to some/that and adjective disambiguation:
|
347
345
|
puts \
|
348
346
|
scrs.map{|scr|
|
@@ -436,13 +434,13 @@ module ActiveFacts
|
|
436
434
|
|
437
435
|
def constraint_dump(c)
|
438
436
|
case c
|
439
|
-
when PresenceConstraint
|
437
|
+
when ActiveFacts::Metamodel::PresenceConstraint
|
440
438
|
dump_presence_constraint(c)
|
441
|
-
when RingConstraint
|
439
|
+
when ActiveFacts::Metamodel::RingConstraint
|
442
440
|
dump_ring_constraint(c)
|
443
|
-
when SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
|
441
|
+
when ActiveFacts::Metamodel::SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
|
444
442
|
dump_set_constraint(c)
|
445
|
-
when SubsetConstraint
|
443
|
+
when ActiveFacts::Metamodel::SubsetConstraint
|
446
444
|
dump_subset_constraint(c)
|
447
445
|
else
|
448
446
|
"#{c.class.basename} #{c.name}: unhandled constraint type"
|
@@ -72,7 +72,7 @@ module ActiveFacts
|
|
72
72
|
].compact
|
73
73
|
parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : "()"
|
74
74
|
|
75
|
-
puts "#{concept o.name} #{keyword "is
|
75
|
+
puts "#{concept o.name} #{keyword "is written as"} #{concept o.supertype.name + parameters }#{
|
76
76
|
if (o.value_restriction)
|
77
77
|
keyword("restricted to")+
|
78
78
|
o.value_restriction.all_allowed_range.map{|ar|
|
@@ -11,8 +11,6 @@ module ActiveFacts
|
|
11
11
|
module Generate
|
12
12
|
# Base class for generators of object-oriented class libraries for an ActiveFacts vocabulary.
|
13
13
|
class OO < OrderedDumper #:nodoc:
|
14
|
-
include Metamodel
|
15
|
-
|
16
14
|
def constraints_dump(constraints_used)
|
17
15
|
# Stub, not needed.
|
18
16
|
end
|
@@ -29,7 +27,7 @@ module ActiveFacts
|
|
29
27
|
role.fact_type.all_role.size <= 2
|
30
28
|
}.
|
31
29
|
sort_by{|role|
|
32
|
-
preferred_role_name(role.fact_type.all_role.select{|r2| r2 != role}[0] || role)
|
30
|
+
preferred_role_name(role.fact_type.all_role.select{|r2| r2 != role}[0] || role, o)
|
33
31
|
}.each{|role|
|
34
32
|
role_dump(role)
|
35
33
|
}
|
@@ -38,54 +36,63 @@ module ActiveFacts
|
|
38
36
|
def role_dump(role)
|
39
37
|
fact_type = role.fact_type
|
40
38
|
if fact_type.all_role.size == 1
|
41
|
-
unary_dump(role, preferred_role_name(role))
|
39
|
+
unary_dump(role, preferred_role_name(role)) unless fact_type.entity_type
|
40
|
+
# REVISIT: If the objectified fact type has already been dumped, we'll get nothing.
|
42
41
|
return
|
43
42
|
elsif fact_type.all_role.size != 2
|
44
43
|
return # ternaries and higher are always objectified
|
45
44
|
end
|
46
45
|
|
47
46
|
# REVISIT: TypeInheritance
|
48
|
-
if TypeInheritance
|
47
|
+
if fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
49
48
|
# debug "Ignoring role #{role} in #{fact_type}, subtype fact type"
|
50
49
|
return
|
51
50
|
end
|
52
51
|
|
53
|
-
other_role = fact_type.all_role.select{|r| r != role}[0]
|
54
|
-
other_role_name = preferred_role_name(other_role)
|
55
|
-
other_player = other_role.concept
|
56
|
-
|
57
52
|
# Find any uniqueness constraint over this role:
|
58
53
|
fact_constraints = @presence_constraints_by_fact[fact_type]
|
59
54
|
#debug "Considering #{fact_constraints.size} fact constraints over fact role #{role.concept.name}"
|
60
|
-
ucs = fact_constraints.select{|c| PresenceConstraint
|
55
|
+
ucs = fact_constraints.select{|c| c.is_a?(ActiveFacts::Metamodel::PresenceConstraint) && c.max_frequency == 1 }
|
61
56
|
# Emit "has_one/one_to_one..." only for functional roles here:
|
62
57
|
#debug "Considering #{ucs.size} unique constraints over role #{role.concept.name}"
|
63
58
|
unless ucs.find {|c|
|
64
|
-
|
65
|
-
#debug "Unique constraint over role #{role.concept.name} has roles #{roles.map{|r| describe_fact_type(r.fact_type, r)}*", "}"
|
66
|
-
roles == [role]
|
59
|
+
c.role_sequence.all_role_ref.map(&:role) == [role]
|
67
60
|
}
|
68
61
|
#debug "No uniqueness constraint found for #{role} in #{fact_type}"
|
69
62
|
return
|
70
63
|
end
|
71
64
|
|
72
|
-
if
|
73
|
-
|
74
|
-
#
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
65
|
+
if fact_type.entity_type
|
66
|
+
# other_role is a phantom played by the entity type that objectifies this fact type.
|
67
|
+
# REVISIT: No rolename can be provided for the phantom role, so _as_XYZ doesn't occur
|
68
|
+
other_player = fact_type.entity_type
|
69
|
+
return unless @concept_types_dumped[other_player]
|
70
|
+
|
71
|
+
other_role_name = other_player.name.snakecase
|
72
|
+
other_role_method = role.concept.name.snakecase
|
73
|
+
one_to_one = true
|
74
|
+
else
|
75
|
+
|
76
|
+
other_role = fact_type.all_role.select{|r| r != role}[0]
|
77
|
+
other_role_name = preferred_role_name(other_role)
|
78
|
+
other_player = other_role.concept
|
79
|
+
|
80
|
+
# It's a one_to_one if there's a uniqueness constraint on the other role:
|
81
|
+
one_to_one = ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] }
|
82
|
+
if one_to_one &&
|
83
|
+
!@concept_types_dumped[other_role.concept]
|
84
|
+
#debug "Will dump 1:1 later for #{role} in #{fact_type}"
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
# Find role name:
|
89
|
+
role_method = preferred_role_name(role)
|
90
|
+
other_role_method = one_to_one ? role_method : "all_"+role_method
|
91
|
+
# puts "---"+role.role_name if role.role_name
|
92
|
+
if other_role_name != other_player.name.snakecase and
|
93
|
+
role_method == role.concept.name.snakecase
|
94
|
+
other_role_method += "_as_#{other_role_name}"
|
95
|
+
end
|
89
96
|
end
|
90
97
|
|
91
98
|
role_name = role_method
|
@@ -94,8 +101,13 @@ module ActiveFacts
|
|
94
101
|
binary_dump(role, other_role_name, other_player, one_to_one, nil, role_name, other_role_method)
|
95
102
|
end
|
96
103
|
|
97
|
-
def preferred_role_name(role)
|
98
|
-
return "" if
|
104
|
+
def preferred_role_name(role, is_for = nil)
|
105
|
+
return "" if role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
106
|
+
|
107
|
+
if is_for && role.fact_type.entity_type == is_for && role.fact_type.all_role.size == 1
|
108
|
+
return role.concept.name.snakecase
|
109
|
+
end
|
110
|
+
|
99
111
|
# debug "Looking for preferred_role_name of #{describe_fact_type(role.fact_type, role)}"
|
100
112
|
reading = role.fact_type.preferred_reading
|
101
113
|
preferred_role_ref = reading.role_sequence.all_role_ref.detect{|reading_rr|
|
@@ -129,19 +141,27 @@ module ActiveFacts
|
|
129
141
|
def skip_fact_type(f)
|
130
142
|
# REVISIT: There might be constraints we have to merge into the nested entity or subtype. These will come up as un-handled constraints.
|
131
143
|
!f.entity_type ||
|
132
|
-
TypeInheritance
|
144
|
+
f.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
133
145
|
end
|
134
146
|
|
135
147
|
# An objectified fact type has internal roles that are always "has_one":
|
136
|
-
def fact_roles_dump(
|
137
|
-
|
138
|
-
preferred_role_name(role)
|
148
|
+
def fact_roles_dump(fact_type)
|
149
|
+
fact_type.all_role.sort_by{|role|
|
150
|
+
preferred_role_name(role, fact_type.entity_type)
|
139
151
|
}.each{|role|
|
140
|
-
role_name = preferred_role_name(role)
|
152
|
+
role_name = preferred_role_name(role, fact_type.entity_type)
|
153
|
+
one_to_one = role.all_role_ref.detect{|rr|
|
154
|
+
rr.role_sequence.all_role_ref.size == 1 &&
|
155
|
+
rr.role_sequence.all_presence_constraint.detect{|pc|
|
156
|
+
pc.max_frequency == 1
|
157
|
+
}
|
158
|
+
}
|
141
159
|
as = role_name != role.concept.name.snakecase ? "_as_#{role_name}" : ""
|
142
|
-
raise "Fact #{
|
143
|
-
other_role_method = "all_"+
|
144
|
-
|
160
|
+
raise "Fact #{fact_type.describe} type is not objectified" unless fact_type.entity_type
|
161
|
+
other_role_method = (one_to_one ? "" : "all_") +
|
162
|
+
fact_type.entity_type.name.snakecase +
|
163
|
+
as
|
164
|
+
binary_dump(role, role_name, role.concept, one_to_one, nil, nil, other_role_method)
|
145
165
|
}
|
146
166
|
end
|
147
167
|
|