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.
Files changed (35) hide show
  1. data/Manifest.txt +7 -2
  2. data/examples/CQL/Address.cql +0 -2
  3. data/examples/CQL/Blog.cql +2 -2
  4. data/examples/CQL/CompanyDirectorEmployee.cql +1 -1
  5. data/examples/CQL/Death.cql +1 -1
  6. data/examples/CQL/Metamodel.cql +5 -5
  7. data/examples/CQL/MultiInheritance.cql +2 -0
  8. data/examples/CQL/PersonPlaysGame.cql +1 -1
  9. data/lib/activefacts/cql/Concepts.treetop +17 -8
  10. data/lib/activefacts/cql/Language/English.treetop +1 -2
  11. data/lib/activefacts/generate/absorption.rb +1 -1
  12. data/lib/activefacts/generate/null.rb +8 -1
  13. data/lib/activefacts/generate/oo.rb +174 -0
  14. data/lib/activefacts/generate/ruby.rb +49 -208
  15. data/lib/activefacts/generate/sql/server.rb +137 -72
  16. data/lib/activefacts/generate/text.rb +1 -1
  17. data/lib/activefacts/input/orm.rb +12 -2
  18. data/lib/activefacts/persistence.rb +5 -1
  19. data/lib/activefacts/persistence/columns.rb +324 -0
  20. data/lib/activefacts/persistence/foreignkey.rb +87 -0
  21. data/lib/activefacts/persistence/index.rb +171 -0
  22. data/lib/activefacts/persistence/reference.rb +326 -0
  23. data/lib/activefacts/persistence/tables.rb +307 -0
  24. data/lib/activefacts/support.rb +1 -1
  25. data/lib/activefacts/version.rb +1 -1
  26. data/lib/activefacts/vocabulary/extensions.rb +42 -5
  27. data/spec/absorption_spec.rb +8 -6
  28. data/spec/cql_cql_spec.rb +1 -0
  29. data/spec/cql_sql_spec.rb +2 -1
  30. data/spec/cql_unit_spec.rb +0 -6
  31. data/spec/norma_cql_spec.rb +1 -0
  32. data/spec/norma_sql_spec.rb +1 -1
  33. data/spec/norma_tables_spec.rb +41 -43
  34. metadata +9 -4
  35. 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/composition.rb
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
@@ -42,5 +42,3 @@ Person lives at at most one Address;
42
42
  */
43
43
  Street includes third-StreetLine
44
44
  only if Street includes second-StreetLine;
45
- Street includes second-StreetLine
46
- only if Street includes first-StreetLine;
@@ -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 at most one Comment,
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 at most one Paragraph,
37
+ Content is of Paragraph,
38
38
  Paragraph contains one Content;
39
39
  Paragraph has Comment,
40
40
  Comment is on one Paragraph;
@@ -1,4 +1,4 @@
1
- vocabulary CompanyDirector;
1
+ vocabulary CompanyDirectorEmployee;
2
2
 
3
3
  /*
4
4
  * Value Types
@@ -1,4 +1,4 @@
1
- vocabulary DeathAsUnary;
1
+ vocabulary Death;
2
2
 
3
3
  /*
4
4
  * Value Types
@@ -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 at most one Ordinal position,
63
+ Reading is in one Ordinal position,
64
64
  Ordinal reading for fact type is Reading;
65
- Reading has one ReadingText;
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
- UnitBasis is where
92
+ Derivation is where
93
93
  Unit (as DerivedUnit) is derived from base-Unit (as BaseUnit) [acyclic, intransitive];
94
- UnitBasis has at most one Exponent;
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
 
@@ -1,4 +1,4 @@
1
- vocabulary AbsorbViaObjFact;
1
+ vocabulary PersonPlaysGame;
2
2
 
3
3
  /*
4
4
  * Value Types
@@ -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 supertype_list ident:identification?
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
@@ -3,8 +3,7 @@ module ActiveFacts
3
3
  grammar Language
4
4
 
5
5
  rule subtype_prefix
6
- defined_as 'subtype' S 'of' S
7
- / 'is' s 'a' s ('kind'/'subtype') s 'of' S
6
+ 'is' s 'a' s ('kind'/'subtype') s 'of' S
8
7
  end
9
8
 
10
9
  rule defined_as
@@ -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/ordered'
6
+ require 'activefacts/generate/oo'
7
7
 
8
8
  module ActiveFacts
9
9
 
10
10
  module Generate
11
- class RUBY < OrderedDumper
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) || !(o = fact_type.entity_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 show_role(r)
292
- puts "Role player #{r.concept.name} facttype #{r.fact_type.name} lead_adj #{r.leading_adjective} trail_adj #{r.trailing_adjective} allows #{r.allowed_values.inspect}"
293
- end
294
-
295
- def entity_type_banner
296
- end
297
-
298
- def entity_type_group_end
299
- end
300
-
301
- def append_ring_to_reading(reading, ring)
302
- # REVISIT: debug "Should override append_ring_to_reading"
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 fact_type_end
129
+ def unary_dump(role, role_name)
130
+ puts " maybe :"+role_name
309
131
  end
310
132
 
311
- def constraint_banner
312
- # debug "Should override constraint_banner"
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 constraint_end
316
- # debug "Should override constraint_end"
317
- end
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
- def constraint_dump(c)
320
- # debug "Should override constraint_dump"
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