activefacts 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|