activefacts 0.6.0 → 0.7.0
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 +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
|