activefacts 0.6.0 → 0.7.0

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