activefacts 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +7 -2
- data/examples/CQL/Address.cql +0 -2
- data/examples/CQL/Blog.cql +2 -2
- data/examples/CQL/CompanyDirectorEmployee.cql +1 -1
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Metamodel.cql +5 -5
- data/examples/CQL/MultiInheritance.cql +2 -0
- data/examples/CQL/PersonPlaysGame.cql +1 -1
- data/lib/activefacts/cql/Concepts.treetop +17 -8
- data/lib/activefacts/cql/Language/English.treetop +1 -2
- data/lib/activefacts/generate/absorption.rb +1 -1
- data/lib/activefacts/generate/null.rb +8 -1
- data/lib/activefacts/generate/oo.rb +174 -0
- data/lib/activefacts/generate/ruby.rb +49 -208
- data/lib/activefacts/generate/sql/server.rb +137 -72
- data/lib/activefacts/generate/text.rb +1 -1
- data/lib/activefacts/input/orm.rb +12 -2
- data/lib/activefacts/persistence.rb +5 -1
- data/lib/activefacts/persistence/columns.rb +324 -0
- data/lib/activefacts/persistence/foreignkey.rb +87 -0
- data/lib/activefacts/persistence/index.rb +171 -0
- data/lib/activefacts/persistence/reference.rb +326 -0
- data/lib/activefacts/persistence/tables.rb +307 -0
- data/lib/activefacts/support.rb +1 -1
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +42 -5
- data/spec/absorption_spec.rb +8 -6
- data/spec/cql_cql_spec.rb +1 -0
- data/spec/cql_sql_spec.rb +2 -1
- data/spec/cql_unit_spec.rb +0 -6
- data/spec/norma_cql_spec.rb +1 -0
- data/spec/norma_sql_spec.rb +1 -1
- data/spec/norma_tables_spec.rb +41 -43
- metadata +9 -4
- data/lib/activefacts/persistence/composition.rb +0 -653
data/Manifest.txt
CHANGED
@@ -33,7 +33,6 @@ lib/activefacts/api/support.rb
|
|
33
33
|
lib/activefacts/api/value.rb
|
34
34
|
lib/activefacts/api/vocabulary.rb
|
35
35
|
lib/activefacts/cql.rb
|
36
|
-
lib/activefacts/cql/Rakefile
|
37
36
|
lib/activefacts/cql/CQLParser.treetop
|
38
37
|
lib/activefacts/cql/Concepts.treetop
|
39
38
|
lib/activefacts/cql/DataTypes.treetop
|
@@ -41,11 +40,13 @@ lib/activefacts/cql/Expressions.treetop
|
|
41
40
|
lib/activefacts/cql/FactTypes.treetop
|
42
41
|
lib/activefacts/cql/Language/English.treetop
|
43
42
|
lib/activefacts/cql/LexicalRules.treetop
|
43
|
+
lib/activefacts/cql/Rakefile
|
44
44
|
lib/activefacts/cql/parser.rb
|
45
45
|
lib/activefacts/generate/absorption.rb
|
46
46
|
lib/activefacts/generate/cql.rb
|
47
47
|
lib/activefacts/generate/cql/html.rb
|
48
48
|
lib/activefacts/generate/null.rb
|
49
|
+
lib/activefacts/generate/oo.rb
|
49
50
|
lib/activefacts/generate/ordered.rb
|
50
51
|
lib/activefacts/generate/ruby.rb
|
51
52
|
lib/activefacts/generate/sql/server.rb
|
@@ -53,7 +54,11 @@ lib/activefacts/generate/text.rb
|
|
53
54
|
lib/activefacts/input/cql.rb
|
54
55
|
lib/activefacts/input/orm.rb
|
55
56
|
lib/activefacts/persistence.rb
|
56
|
-
lib/activefacts/persistence/
|
57
|
+
lib/activefacts/persistence/columns.rb
|
58
|
+
lib/activefacts/persistence/foreignkey.rb
|
59
|
+
lib/activefacts/persistence/index.rb
|
60
|
+
lib/activefacts/persistence/reference.rb
|
61
|
+
lib/activefacts/persistence/tables.rb
|
57
62
|
lib/activefacts/support.rb
|
58
63
|
lib/activefacts/version.rb
|
59
64
|
lib/activefacts/vocabulary.rb
|
data/examples/CQL/Address.cql
CHANGED
data/examples/CQL/Blog.cql
CHANGED
@@ -27,14 +27,14 @@ Content is identified by Style and Text where
|
|
27
27
|
Content is of at most one Style,
|
28
28
|
Content has one Text,
|
29
29
|
Text is of Content;
|
30
|
-
Content provides text of
|
30
|
+
Content provides text of Comment,
|
31
31
|
Comment consists of one text-Content;
|
32
32
|
|
33
33
|
Post is identified by its Id;
|
34
34
|
Post was written by one Author;
|
35
35
|
Paragraph is where
|
36
36
|
Post includes Ordinal paragraph;
|
37
|
-
Content is of
|
37
|
+
Content is of Paragraph,
|
38
38
|
Paragraph contains one Content;
|
39
39
|
Paragraph has Comment,
|
40
40
|
Comment is on one Paragraph;
|
data/examples/CQL/Death.cql
CHANGED
data/examples/CQL/Metamodel.cql
CHANGED
@@ -16,10 +16,10 @@ Length is defined as UnsignedInteger(32);
|
|
16
16
|
Name is defined as VariableLengthText(64);
|
17
17
|
Numerator is defined as Decimal();
|
18
18
|
Ordinal is defined as UnsignedSmallInteger(32);
|
19
|
-
ReadingText is defined as VariableLengthText(256);
|
20
19
|
RingType is defined as VariableLengthText();
|
21
20
|
RoleSequenceId is defined as AutoCounter();
|
22
21
|
Scale is defined as UnsignedInteger(32);
|
22
|
+
Text is defined as VariableLengthText(256);
|
23
23
|
UnitId is defined as AutoCounter();
|
24
24
|
Value is defined as VariableLengthText(256);
|
25
25
|
ValueRestrictionId is defined as AutoCounter();
|
@@ -60,9 +60,9 @@ PresenceConstraint is preferred identifier;
|
|
60
60
|
Reading is identified by FactType and Ordinal where
|
61
61
|
FactType has at least one Reading,
|
62
62
|
Reading is for one FactType,
|
63
|
-
Reading is in
|
63
|
+
Reading is in one Ordinal position,
|
64
64
|
Ordinal reading for fact type is Reading;
|
65
|
-
Reading has one
|
65
|
+
Reading has one Text;
|
66
66
|
|
67
67
|
RingConstraint is a kind of Constraint;
|
68
68
|
RingConstraint is of one RingType;
|
@@ -89,9 +89,9 @@ Name is of Unit,
|
|
89
89
|
Unit is called one Name;
|
90
90
|
Unit has at most one Coefficient;
|
91
91
|
Unit is fundamental;
|
92
|
-
|
92
|
+
Derivation is where
|
93
93
|
Unit (as DerivedUnit) is derived from base-Unit (as BaseUnit) [acyclic, intransitive];
|
94
|
-
|
94
|
+
Derivation has at most one Exponent;
|
95
95
|
|
96
96
|
ValueRange is identified by minimum-Bound and maximum-Bound where
|
97
97
|
ValueRange has at most one minimum-Bound,
|
@@ -5,6 +5,7 @@ vocabulary MultiInheritance;
|
|
5
5
|
*/
|
6
6
|
EmployeeID is defined as AutoCounter();
|
7
7
|
PersonName is defined as VariableLengthText();
|
8
|
+
TFN is defined as FixedLengthText(9);
|
8
9
|
|
9
10
|
/*
|
10
11
|
* Entity Types
|
@@ -12,6 +13,7 @@ PersonName is defined as VariableLengthText();
|
|
12
13
|
Person is identified by its Name;
|
13
14
|
|
14
15
|
Australian is a kind of Person;
|
16
|
+
Australian has at most one TFN;
|
15
17
|
|
16
18
|
Employee is a kind of Person identified by its ID;
|
17
19
|
|
@@ -20,14 +20,30 @@ module ActiveFacts
|
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
+
rule basetype
|
24
|
+
is s
|
25
|
+
# independency?
|
26
|
+
identification
|
27
|
+
{ def supers; [] end
|
28
|
+
def identifier; identification.value; end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
23
32
|
rule subtype
|
24
|
-
subtype_prefix
|
33
|
+
subtype_prefix
|
34
|
+
# independency?
|
35
|
+
supertype_list ident:identification?
|
25
36
|
{
|
26
37
|
def supers; supertype_list.value; end
|
27
38
|
def identifier; ident.empty? ? nil : ident.value; end
|
28
39
|
}
|
29
40
|
end
|
30
41
|
|
42
|
+
# REVISIT: This doesn't work, and I don't know why.
|
43
|
+
rule independency
|
44
|
+
('independent' S / 'separate' S / 'partitioned' S)
|
45
|
+
end
|
46
|
+
|
31
47
|
rule supertype_list
|
32
48
|
primary:id s
|
33
49
|
alternate_supertypes:( ',' s !identified_by name:id s )*
|
@@ -37,13 +53,6 @@ module ActiveFacts
|
|
37
53
|
}
|
38
54
|
end
|
39
55
|
|
40
|
-
rule basetype
|
41
|
-
is s identification
|
42
|
-
{ def supers; [] end
|
43
|
-
def identifier; identification.value; end
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
56
|
rule identification
|
48
57
|
# REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-"
|
49
58
|
identified_by its s id s # Reference Mode
|
@@ -13,7 +13,7 @@ module ActiveFacts
|
|
13
13
|
|
14
14
|
def initialize(vocabulary, *options)
|
15
15
|
@vocabulary = vocabulary
|
16
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::Constellation === @vocabulary
|
16
|
+
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
17
17
|
@no_columns = options.include? "no_columns"
|
18
18
|
@dependent = options.include? "dependent"
|
19
19
|
@paths = options.include? "paths"
|
@@ -4,14 +4,21 @@
|
|
4
4
|
# Copyright (c) 2007 Clifford Heath. Read the LICENSE file.
|
5
5
|
# Author: Clifford Heath.
|
6
6
|
#
|
7
|
+
require 'activefacts/persistence'
|
8
|
+
|
7
9
|
module ActiveFacts
|
8
10
|
module Generate
|
9
11
|
class NULL
|
10
|
-
def initialize(vocabulary)
|
12
|
+
def initialize(vocabulary, *options)
|
11
13
|
@vocabulary = vocabulary
|
14
|
+
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
15
|
+
@tables = options.include? "tables"
|
16
|
+
@columns = options.include? "columns"
|
17
|
+
@indices = options.include? "indices"
|
12
18
|
end
|
13
19
|
|
14
20
|
def generate(out = $>)
|
21
|
+
@vocabulary.tables if @tables || @columns || @indices
|
15
22
|
end
|
16
23
|
end
|
17
24
|
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
#
|
2
|
+
# OO Generation support for the ActiveFacts API from an ActiveFacts vocabulary.
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
require 'activefacts/vocabulary'
|
6
|
+
require 'activefacts/generate/ordered'
|
7
|
+
|
8
|
+
module ActiveFacts
|
9
|
+
|
10
|
+
module Generate
|
11
|
+
class OO < OrderedDumper
|
12
|
+
include Metamodel
|
13
|
+
|
14
|
+
def constraints_dump(constraints_used)
|
15
|
+
# Stub, not needed.
|
16
|
+
end
|
17
|
+
|
18
|
+
def value_type_banner
|
19
|
+
end
|
20
|
+
|
21
|
+
def value_type_end
|
22
|
+
end
|
23
|
+
|
24
|
+
def roles_dump(o)
|
25
|
+
o.all_role.
|
26
|
+
sort_by{|role|
|
27
|
+
other_role = role.fact_type.all_role[role.fact_type.all_role[0] != role ? 0 : -1]
|
28
|
+
other_role ? preferred_role_name(other_role) : ""
|
29
|
+
#puts "\t#{role.fact_type.describe(other_role)} by #{p}"
|
30
|
+
}.each{|role|
|
31
|
+
fact_type = role.fact_type
|
32
|
+
next if fact_type.all_role.size > 2
|
33
|
+
role_dump(role)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def role_dump(role)
|
38
|
+
fact_type = role.fact_type
|
39
|
+
if fact_type.all_role.size == 1
|
40
|
+
unary_dump(role, preferred_role_name(role))
|
41
|
+
return
|
42
|
+
elsif fact_type.all_role.size != 2
|
43
|
+
return # ternaries and higher are always objectified
|
44
|
+
end
|
45
|
+
|
46
|
+
# REVISIT: TypeInheritance
|
47
|
+
if TypeInheritance === fact_type
|
48
|
+
# debug "Ignoring role #{role} in #{fact_type}, subtype fact type"
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
other_role_number = fact_type.all_role[0] == role ? 1 : 0
|
53
|
+
other_role = fact_type.all_role[other_role_number]
|
54
|
+
other_role_name = preferred_role_name(other_role)
|
55
|
+
other_player = other_role.concept
|
56
|
+
|
57
|
+
# Find any uniqueness constraint over this role:
|
58
|
+
fact_constraints = @presence_constraints_by_fact[fact_type]
|
59
|
+
#debug "Considering #{fact_constraints.size} fact constraints over fact role #{role.concept.name}"
|
60
|
+
ucs = fact_constraints.select{|c| PresenceConstraint === c && c.max_frequency == 1 }
|
61
|
+
# Emit "has_one/one_to_one..." only for functional roles here:
|
62
|
+
#debug "Considering #{ucs.size} unique constraints over role #{role.concept.name}"
|
63
|
+
unless ucs.find {|c|
|
64
|
+
roles = c.role_sequence.all_role_ref.map(&:role)
|
65
|
+
#debug "Unique constraint over role #{role.concept.name} has roles #{roles.map{|r| describe_fact_type(r.fact_type, r)}*", "}"
|
66
|
+
roles == [role]
|
67
|
+
}
|
68
|
+
#debug "No uniqueness constraint found for #{role} in #{fact_type}"
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
if ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] } &&
|
73
|
+
!@concept_types_dumped[other_role.concept]
|
74
|
+
#debug "Will dump 1:1 later for #{role} in #{fact_type}"
|
75
|
+
return
|
76
|
+
end
|
77
|
+
|
78
|
+
# It's a one_to_one if there's a uniqueness constraint on the other role:
|
79
|
+
one_to_one = ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] }
|
80
|
+
|
81
|
+
# Find role name:
|
82
|
+
role_method = preferred_role_name(role)
|
83
|
+
by = other_role_name != other_player.name.snakecase ? "_by_#{other_role_name}" : ""
|
84
|
+
other_role_method = one_to_one ? role_method : "all_"+role_method
|
85
|
+
other_role_method += by
|
86
|
+
|
87
|
+
role_name = role_method
|
88
|
+
role_name = nil if role_name == role.concept.name.snakecase
|
89
|
+
|
90
|
+
binary_dump(role, other_role_name, other_player, one_to_one, nil, role_name, other_role_method)
|
91
|
+
end
|
92
|
+
|
93
|
+
def preferred_role_name(role)
|
94
|
+
return "" if TypeInheritance === role.fact_type
|
95
|
+
# debug "Looking for preferred_role_name of #{describe_fact_type(role.fact_type, role)}"
|
96
|
+
reading = role.fact_type.preferred_reading
|
97
|
+
preferred_role_ref = reading.role_sequence.all_role_ref.detect{|reading_rr|
|
98
|
+
reading_rr.role == role
|
99
|
+
}
|
100
|
+
|
101
|
+
# Unaries are a hack, with only one role for what is effectively a binary:
|
102
|
+
if (role.fact_type.all_role.size == 1)
|
103
|
+
return (role.role_name && role.role_name.snakecase) ||
|
104
|
+
reading.reading_text.gsub(/ *\{0\} */,'').gsub(' ','_').downcase
|
105
|
+
end
|
106
|
+
|
107
|
+
# debug "\tleading_adjective=#{(p=preferred_role_ref).leading_adjective}, role_name=#{role.role_name}, role player=#{role.concept.name}, trailing_adjective=#{p.trailing_adjective}"
|
108
|
+
role_words = []
|
109
|
+
role_name = role.role_name
|
110
|
+
role_name = nil if role_name == ""
|
111
|
+
|
112
|
+
# REVISIT: Consider whether NOT to use the adjective if it's a prefix of the role_name
|
113
|
+
la = preferred_role_ref.leading_adjective
|
114
|
+
role_words << la.gsub(/ /,'_') if la && la != "" and !role.role_name
|
115
|
+
|
116
|
+
role_words << (role_name || role.concept.name)
|
117
|
+
# REVISIT: Same when trailing_adjective is a suffix of the role_name
|
118
|
+
ta = preferred_role_ref.trailing_adjective
|
119
|
+
role_words << ta.gsub(/ /,'_') if ta && ta != "" and !role_name
|
120
|
+
n = role_words.map{|w| w.gsub(/([a-z])([A-Z]+)/,'\1_\2').downcase}*"_"
|
121
|
+
# debug "\tresult=#{n}"
|
122
|
+
n
|
123
|
+
end
|
124
|
+
|
125
|
+
def skip_fact_type(f)
|
126
|
+
# REVISIT: There might be constraints we have to merge into the nested entity or subtype. These will come up as un-handled constraints.
|
127
|
+
!f.entity_type ||
|
128
|
+
TypeInheritance === f
|
129
|
+
end
|
130
|
+
|
131
|
+
# An objectified fact type has internal roles that are always "has_one":
|
132
|
+
def fact_roles_dump(fact)
|
133
|
+
fact.all_role.sort_by{|role|
|
134
|
+
preferred_role_name(role)
|
135
|
+
}.each{|role|
|
136
|
+
role_name = preferred_role_name(role)
|
137
|
+
by = role_name != role.concept.name.snakecase ? "_by_#{role_name}" : ""
|
138
|
+
raise "Fact #{fact.describe} type is not objectified" unless fact.entity_type
|
139
|
+
other_role_method = "all_"+fact.entity_type.name.snakecase+by
|
140
|
+
binary_dump(role, role_name, role.concept, false, nil, nil, other_role_method)
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
def entity_type_banner
|
145
|
+
end
|
146
|
+
|
147
|
+
def entity_type_group_end
|
148
|
+
end
|
149
|
+
|
150
|
+
def append_ring_to_reading(reading, ring)
|
151
|
+
# REVISIT: debug "Should override append_ring_to_reading"
|
152
|
+
end
|
153
|
+
|
154
|
+
def fact_type_banner
|
155
|
+
end
|
156
|
+
|
157
|
+
def fact_type_end
|
158
|
+
end
|
159
|
+
|
160
|
+
def constraint_banner
|
161
|
+
# debug "Should override constraint_banner"
|
162
|
+
end
|
163
|
+
|
164
|
+
def constraint_end
|
165
|
+
# debug "Should override constraint_end"
|
166
|
+
end
|
167
|
+
|
168
|
+
def constraint_dump(c)
|
169
|
+
# debug "Should override constraint_dump"
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -3,17 +3,20 @@
|
|
3
3
|
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
4
|
#
|
5
5
|
require 'activefacts/vocabulary'
|
6
|
-
require 'activefacts/generate/
|
6
|
+
require 'activefacts/generate/oo'
|
7
7
|
|
8
8
|
module ActiveFacts
|
9
9
|
|
10
10
|
module Generate
|
11
|
-
class RUBY <
|
12
|
-
include Metamodel
|
11
|
+
class RUBY < OO
|
13
12
|
|
14
13
|
def set_option(option)
|
15
14
|
@sql ||= false
|
16
15
|
case option
|
16
|
+
when 'help', '?'
|
17
|
+
$stderr.puts "Usage:\t\tafgen --ruby[=option,option] input_file.cql\n"+
|
18
|
+
"options:\tsql\tEmit data to enable mappings to SQL"
|
19
|
+
exit 0
|
17
20
|
when 'sql'; @sql = true
|
18
21
|
else super
|
19
22
|
end
|
@@ -28,20 +31,10 @@ module ActiveFacts
|
|
28
31
|
puts "module #{vocabulary.name}\n\n"
|
29
32
|
end
|
30
33
|
|
31
|
-
def constraints_dump(constraints_used)
|
32
|
-
# Stub, not needed.
|
33
|
-
end
|
34
|
-
|
35
34
|
def vocabulary_end
|
36
35
|
puts "end"
|
37
36
|
end
|
38
37
|
|
39
|
-
def value_type_banner
|
40
|
-
end
|
41
|
-
|
42
|
-
def value_type_end
|
43
|
-
end
|
44
|
-
|
45
38
|
def value_type_dump(o)
|
46
39
|
return if !o.supertype
|
47
40
|
if o.name == o.supertype.name
|
@@ -69,124 +62,6 @@ module ActiveFacts
|
|
69
62
|
puts " end\n\n"
|
70
63
|
end
|
71
64
|
|
72
|
-
def roles_dump(o)
|
73
|
-
ar_by_role = nil
|
74
|
-
if @sql and @tables.include?(o)
|
75
|
-
ar = o.absorbed_roles
|
76
|
-
ar_by_role = ar.all_role_ref.inject({}){|h,rr|
|
77
|
-
input_role = (j=rr.all_join_path).size > 0 ? j[0].input_role : rr.role
|
78
|
-
(h[input_role] ||= []) << rr
|
79
|
-
h
|
80
|
-
}
|
81
|
-
#puts ar.all_role_ref.map{|rr| "\t"+rr.describe}*"\n"
|
82
|
-
end
|
83
|
-
o.all_role.
|
84
|
-
sort_by{|role|
|
85
|
-
other_role = role.fact_type.all_role[role.fact_type.all_role[0] != role ? 0 : -1]
|
86
|
-
other_role ? preferred_role_name(other_role) : ""
|
87
|
-
#puts "\t#{role.fact_type.describe(other_role)} by #{p}"
|
88
|
-
}.each{|role|
|
89
|
-
other_role = role.fact_type.all_role[role.fact_type.all_role[0] != role ? 0 : -1]
|
90
|
-
if ar_by_role and ar_by_role[other_role]
|
91
|
-
puts " # role #{role.fact_type.describe(role)}: absorbs in through #{preferred_role_name(other_role)}: "+ar_by_role[other_role].map(&:column_name)*", "
|
92
|
-
end
|
93
|
-
role_dump(role)
|
94
|
-
}
|
95
|
-
end
|
96
|
-
|
97
|
-
def preferred_role_name(role)
|
98
|
-
return "" if TypeInheritance === role.fact_type
|
99
|
-
# debug "Looking for preferred_role_name of #{describe_fact_type(role.fact_type, role)}"
|
100
|
-
reading = role.fact_type.preferred_reading
|
101
|
-
preferred_role_ref = reading.role_sequence.all_role_ref.detect{|reading_rr|
|
102
|
-
reading_rr.role == role
|
103
|
-
}
|
104
|
-
|
105
|
-
# Unaries are a hack, with only one role for what is effectively a binary:
|
106
|
-
if (role.fact_type.all_role.size == 1)
|
107
|
-
return (role.role_name && role.role_name.snakecase) ||
|
108
|
-
reading.reading_text.gsub(/ *\{0\} */,'').gsub(' ','_').downcase
|
109
|
-
end
|
110
|
-
|
111
|
-
# debug "\tleading_adjective=#{(p=preferred_role_ref).leading_adjective}, role_name=#{role.role_name}, role player=#{role.concept.name}, trailing_adjective=#{p.trailing_adjective}"
|
112
|
-
role_words = []
|
113
|
-
role_name = role.role_name
|
114
|
-
role_name = nil if role_name == ""
|
115
|
-
|
116
|
-
# REVISIT: Consider whether NOT to use the adjective if it's a prefix of the role_name
|
117
|
-
la = preferred_role_ref.leading_adjective
|
118
|
-
role_words << la.gsub(/ /,'_') if la && la != "" and !role.role_name
|
119
|
-
|
120
|
-
role_words << (role_name || role.concept.name)
|
121
|
-
# REVISIT: Same when trailing_adjective is a suffix of the role_name
|
122
|
-
ta = preferred_role_ref.trailing_adjective
|
123
|
-
role_words << ta.gsub(/ /,'_') if ta && ta != "" and !role_name
|
124
|
-
n = role_words.map{|w| w.gsub(/([a-z])([A-Z]+)/,'\1_\2').downcase}*"_"
|
125
|
-
# debug "\tresult=#{n}"
|
126
|
-
n
|
127
|
-
end
|
128
|
-
|
129
|
-
def role_dump(role)
|
130
|
-
fact_type = role.fact_type
|
131
|
-
if fact_type.all_role.size == 1
|
132
|
-
# Handle Unary Roles here
|
133
|
-
puts " maybe :"+preferred_role_name(role)
|
134
|
-
return
|
135
|
-
elsif fact_type.all_role.size != 2
|
136
|
-
return # ternaries and higher are always objectified
|
137
|
-
end
|
138
|
-
|
139
|
-
# REVISIT: TypeInheritance
|
140
|
-
if TypeInheritance === fact_type
|
141
|
-
# debug "Ignoring role #{role} in #{fact_type}, subtype fact type"
|
142
|
-
return
|
143
|
-
end
|
144
|
-
|
145
|
-
other_role_number = fact_type.all_role[0] == role ? 1 : 0
|
146
|
-
other_role = fact_type.all_role[other_role_number]
|
147
|
-
other_role_name = preferred_role_name(other_role)
|
148
|
-
#other_role_name = ruby_role_name(other_role)
|
149
|
-
other_player = other_role.concept
|
150
|
-
|
151
|
-
# Find any uniqueness constraint over this role:
|
152
|
-
fact_constraints = @presence_constraints_by_fact[fact_type]
|
153
|
-
#debug "Considering #{fact_constraints.size} fact constraints over fact role #{role.concept.name}"
|
154
|
-
ucs = fact_constraints.select{|c| PresenceConstraint === c && c.max_frequency == 1 }
|
155
|
-
# Emit "has_one/one_to_one..." only for functional roles here:
|
156
|
-
#debug "Considering #{ucs.size} unique constraints over role #{role.concept.name}"
|
157
|
-
unless ucs.find {|c|
|
158
|
-
roles = c.role_sequence.all_role_ref.map(&:role)
|
159
|
-
#debug "Unique constraint over role #{role.concept.name} has roles #{roles.map{|r| describe_fact_type(r.fact_type, r)}*", "}"
|
160
|
-
roles == [role]
|
161
|
-
}
|
162
|
-
#debug "No uniqueness constraint found for #{role} in #{fact_type}"
|
163
|
-
return
|
164
|
-
end
|
165
|
-
|
166
|
-
if ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] } &&
|
167
|
-
!@concept_types_dumped[other_role.concept]
|
168
|
-
#debug "Will dump 1:1 later for #{role} in #{fact_type}"
|
169
|
-
return
|
170
|
-
end
|
171
|
-
|
172
|
-
# It's a one_to_one if there's a uniqueness constraint on the other role:
|
173
|
-
one_to_one = ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] }
|
174
|
-
|
175
|
-
# REVISIT: Add readings
|
176
|
-
|
177
|
-
# Find role name:
|
178
|
-
role_method = preferred_role_name(role)
|
179
|
-
by = other_role_name != other_player.name.snakecase ? "_by_#{other_role_name}" : ""
|
180
|
-
other_role_method = one_to_one ? role_method : "all_"+role_method
|
181
|
-
other_role_method += by
|
182
|
-
|
183
|
-
role_name = role_method
|
184
|
-
role_name = nil if role_name == role.concept.name.snakecase
|
185
|
-
|
186
|
-
binary_dump(other_role_name, other_player, one_to_one, nil, role_name, other_role_method)
|
187
|
-
puts " \# REVISIT: #{other_role_name} has restricted values\n" if role.role_value_restriction
|
188
|
-
end
|
189
|
-
|
190
65
|
def subtype_dump(o, supertypes, pi = nil)
|
191
66
|
puts " class #{o.name} < #{ supertypes[0].name }"
|
192
67
|
puts " identified_by #{identified_by(o, pi)}" if pi
|
@@ -207,54 +82,10 @@ module ActiveFacts
|
|
207
82
|
@constraints_used[pi] = true
|
208
83
|
end
|
209
84
|
|
210
|
-
def skip_fact_type(f)
|
211
|
-
# REVISIT: There might be constraints we have to merge into the nested entity or subtype.
|
212
|
-
# These will come up as un-handled constraints:
|
213
|
-
#debug "Skipping objectified fact type #{f.entity_type.name}" if f.entity_type
|
214
|
-
#f.entity_type ||
|
215
|
-
TypeInheritance === f
|
216
|
-
end
|
217
|
-
|
218
|
-
# An objectified fact type has internal roles that are always "has_one":
|
219
|
-
def fact_roles_dump(fact)
|
220
|
-
fact.all_role.sort_by{|role|
|
221
|
-
preferred_role_name(role)
|
222
|
-
}.each{|role|
|
223
|
-
role_name = preferred_role_name(role)
|
224
|
-
by = role_name != role.concept.name.snakecase ? "_by_#{role_name}" : ""
|
225
|
-
raise "Fact #{fact.describe} type is not objectified" unless fact.entity_type
|
226
|
-
other_role_method = "all_"+fact.entity_type.name.snakecase+by
|
227
|
-
binary_dump(role_name, role.concept, false, nil, nil, other_role_method)
|
228
|
-
}
|
229
|
-
end
|
230
|
-
|
231
|
-
def binary_dump(role_name, role_player, one_to_one = nil, readings = nil, other_role_name = nil, other_method_name = nil)
|
232
|
-
# Find whether we need the name of the other role player, and whether it's defined yet:
|
233
|
-
if role_name.camelcase(true) == role_player.name
|
234
|
-
# Don't use Class name if implied by rolename
|
235
|
-
role_reference = nil
|
236
|
-
elsif !@concept_types_dumped[role_player]
|
237
|
-
role_reference = '"'+role_player.name+'"'
|
238
|
-
else
|
239
|
-
role_reference = role_player.name
|
240
|
-
end
|
241
|
-
other_role_name = ":"+other_role_name if other_role_name
|
242
|
-
|
243
|
-
line = " #{one_to_one ? "one_to_one" : "has_one" } " +
|
244
|
-
[ ":"+role_name,
|
245
|
-
role_reference,
|
246
|
-
readings,
|
247
|
-
other_role_name
|
248
|
-
].compact*", "+" "
|
249
|
-
line += " "*(48-line.length) if line.length < 48
|
250
|
-
line += "\# See #{role_player.name}.#{other_method_name}" if other_method_name
|
251
|
-
puts line
|
252
|
-
end
|
253
|
-
|
254
85
|
# Dump one fact type.
|
255
|
-
# Include as many as possible internal constraints in the fact type readings.
|
256
86
|
def fact_type_dump(fact_type, name)
|
257
|
-
return if skip_fact_type(fact_type)
|
87
|
+
return if skip_fact_type(fact_type)
|
88
|
+
o = fact_type.entity_type
|
258
89
|
|
259
90
|
primary_supertype = o && (o.identifying_supertype || o.supertypes[0])
|
260
91
|
secondary_supertypes = o.supertypes-[primary_supertype]
|
@@ -275,52 +106,62 @@ module ActiveFacts
|
|
275
106
|
@fact_types_dumped[fact_type] = true
|
276
107
|
end
|
277
108
|
|
278
|
-
def ruby_role_name(role_name)
|
279
|
-
if Role === role_name
|
280
|
-
role_name = role_name.role_name || role_name.concept.name
|
281
|
-
end
|
282
|
-
role_name.snakecase.gsub("-",'_')
|
283
|
-
end
|
284
|
-
|
285
109
|
def identified_by_roles_and_facts(entity_type, identifying_roles, identifying_facts, preferred_readings)
|
286
110
|
identifying_roles.map{|role|
|
287
111
|
":"+preferred_role_name(role)
|
288
112
|
}*", "
|
289
113
|
end
|
290
114
|
|
291
|
-
def
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
end
|
304
|
-
|
305
|
-
def fact_type_banner
|
115
|
+
def roles_dump(o)
|
116
|
+
@ar_by_role = nil
|
117
|
+
if @sql and @tables.include?(o)
|
118
|
+
ar = o.absorbed_roles
|
119
|
+
@ar_by_role = ar.all_role_ref.inject({}){|h,rr|
|
120
|
+
input_role = (j=rr.all_join_path).size > 0 ? j[0].input_role : rr.role
|
121
|
+
(h[input_role] ||= []) << rr
|
122
|
+
h
|
123
|
+
}
|
124
|
+
#puts ar.all_role_ref.map{|rr| "\t"+rr.describe}*"\n"
|
125
|
+
end
|
126
|
+
super
|
306
127
|
end
|
307
128
|
|
308
|
-
def
|
129
|
+
def unary_dump(role, role_name)
|
130
|
+
puts " maybe :"+role_name
|
309
131
|
end
|
310
132
|
|
311
|
-
def
|
312
|
-
|
133
|
+
def role_dump(role)
|
134
|
+
other_role = role.fact_type.all_role[role.fact_type.all_role[0] != role ? 0 : -1]
|
135
|
+
if @ar_by_role and @ar_by_role[other_role] and @sql
|
136
|
+
puts " # role #{role.fact_type.describe(role)}: absorbs in through #{preferred_role_name(other_role)}: "+@ar_by_role[other_role].map(&:column_name)*", "
|
137
|
+
end
|
138
|
+
super
|
313
139
|
end
|
314
140
|
|
315
|
-
def
|
316
|
-
#
|
317
|
-
|
141
|
+
def binary_dump(role, role_name, role_player, one_to_one = nil, readings = nil, other_role_name = nil, other_method_name = nil)
|
142
|
+
# Find whether we need the name of the other role player, and whether it's defined yet:
|
143
|
+
if role_name.camelcase(true) == role_player.name
|
144
|
+
# Don't use Class name if implied by rolename
|
145
|
+
role_reference = nil
|
146
|
+
elsif !@concept_types_dumped[role_player]
|
147
|
+
role_reference = '"'+role_player.name+'"'
|
148
|
+
else
|
149
|
+
role_reference = role_player.name
|
150
|
+
end
|
151
|
+
other_role_name = ":"+other_role_name if other_role_name
|
318
152
|
|
319
|
-
|
320
|
-
|
153
|
+
line = " #{one_to_one ? "one_to_one" : "has_one" } " +
|
154
|
+
[ ":"+role_name,
|
155
|
+
role_reference,
|
156
|
+
readings,
|
157
|
+
other_role_name
|
158
|
+
].compact*", "+" "
|
159
|
+
line += " "*(48-line.length) if line.length < 48
|
160
|
+
line += "\# See #{role_player.name}.#{other_method_name}" if other_method_name
|
161
|
+
puts line
|
162
|
+
puts " \# REVISIT: #{other_role_name} has restricted values\n" if role.role_value_restriction
|
321
163
|
end
|
322
164
|
|
323
165
|
end
|
324
|
-
|
325
166
|
end
|
326
167
|
end
|