activefacts 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9375bff433eb3d5ca379bb09c75cf61dbf06d0f9
4
- data.tar.gz: 0eb5ae1999ee3022eb304366dcff00d0416e5562
3
+ metadata.gz: 4e9276d073a9b3444de848bf033b76f373784a51
4
+ data.tar.gz: 0ab5602a9298cc84ce688168a3635bd7f4d976a8
5
5
  SHA512:
6
- metadata.gz: ea7c95187824dcbceb58d036483f63611d1ed079deea269b53b1f52b0a1db53f62fe525c65561bd79aea4592902671dc69d607b84739392eab776bf785c460e6
7
- data.tar.gz: ebc6351f9ef09824dd11629be72734cc55f99092c6f4b7e90405ae5aeb062ede4eea0ea7376c1ef8b714bdd36d3f75061f3d046e808c1b9dff987dc992d8fa67
6
+ metadata.gz: a984577de5c0aa667d5975f70b35c76f2bb4ab65555fc6a19ca0b7a8bae635cd77fb6d3fbac3959f9f9d76bc53c81846214940ea5109d3e58c6ae5b06197c6c5
7
+ data.tar.gz: 169adf1025e3c1bb46fa58fd45e79d05c2eb45e4ffb069c25bec1c10b786f3dddca32c97f54556353bd48fb9580e2becc279f5a11185373857af4dff64694635
@@ -16,7 +16,7 @@ Enforcement Code is written as String(16);
16
16
  Ephemera URL is written as String;
17
17
  Exponent is written as Signed Integer(16);
18
18
  Frequency is written as Unsigned Integer(32);
19
- Guid is written as Guid;
19
+ Guid is written as Guid auto-assigned at assert;
20
20
  Implication Rule Name is written as String;
21
21
  Length is written as Unsigned Integer(32);
22
22
  Literal is written as String;
@@ -122,7 +122,7 @@ module ActiveFacts
122
122
  if (pc)
123
123
  pc.is_preferred_identifier = true
124
124
  pc.name = "#{@entity_type.name}PK" unless pc.name
125
- trace "Existing PC #{pc.verbalise} is now PK for #{@entity_type.name}"
125
+ trace :constraint, "Existing PC #{pc.verbalise} is now PK for #{@entity_type.name}"
126
126
  else
127
127
  # Add a unique constraint over all identifying roles
128
128
  pc = @constellation.PresenceConstraint(
@@ -254,7 +254,7 @@ module ActiveFacts
254
254
  @constellation.RoleRef(rs2, 0, :role => super_role)
255
255
  @constellation.RoleRef(rs2, 1, :role => sub_role)
256
256
  # Decide in which order to include is a/is an. Provide both, but in order.
257
- n = 'aeiouh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
257
+ n = 'aeioh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
258
258
  @constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false)
259
259
 
260
260
  if is_identifying_supertype
@@ -287,6 +287,7 @@ module ActiveFacts
287
287
  epc = ref.embedded_presence_constraint or next
288
288
  epc.max_frequency == 1 or next
289
289
  next if epc.enforcement
290
+ trace :constraint, "Converting UC into PI for #{@fact_type.entity_type.name}"
290
291
  epc.is_preferred_identifier = true
291
292
  return
292
293
  end
@@ -131,13 +131,16 @@ module ActiveFacts
131
131
  sorted.each{|o|
132
132
  next if o.ordered_dumped # Already done
133
133
 
134
+ trace :ordered, "Panicing to dump #{panic.name}" if panic
134
135
  # Can we do this yet?
136
+ remaining_precursors = Array(@precursors[o])-[o]
135
137
  if (o != panic and # We don't *have* to do it (panic mode)
136
- (p = @precursors[o]) and # There might be...
137
- p.size > 0) # precursors - still blocked
138
+ remaining_precursors.size > 0) # precursors - still blocked
139
+ trace :ordered, "Can't dump #{o.name} despite panic for #{panic.name}, it still needs #{remaining_precursors.map(&:name)*', '}" if panic
138
140
  skipped_this_pass += 1
139
141
  next
140
142
  end
143
+ trace :ordered, "Dumping #{o.name} in panic mode, even though it still needs #{remaining_precursors.map(&:name)*', '}" if panic
141
144
 
142
145
  entity_type_banner unless done_banner
143
146
  done_banner = true
@@ -162,26 +165,39 @@ module ActiveFacts
162
165
 
163
166
  # Check that we made progress if there's any to make:
164
167
  if count_this_pass == 0 && skipped_this_pass > 0
168
+ =begin
165
169
  if panic # We were already panicing... what to do now?
166
170
  # This won't happen again unless the above code is changed to decide it can't dump "panic".
171
+ bad = sorted.select do |o|
172
+ o.is_a?(ActiveFacts::Metamodel::EntityType) &&
173
+ !o.ordered_dumped &&
174
+ (Array(@precursors[o])-[o]).size > 0 &&
175
+ (Array(@followers[o])-[o]).size > 0
176
+ end
177
+
167
178
  raise "Unresolvable cycle of forward references: " +
168
- (bad = sorted.select{|o| EntityType === o && !o.ordered_dumped}).map{|o| o.name }.inspect +
169
- ":\n\t" + bad.map{|o|
170
- o.name +
171
- ": " +
172
- @precursors[o].map{|p| p.name}.uniq.inspect
173
- } * "\n\t" + "\n"
174
- else
179
+ bad.map { |o| o.name }*', ' +
180
+ ":\n\t" +
181
+ (
182
+ bad.map do |o|
183
+ o.name +
184
+ " depends on " +
185
+ (@precursors[o].uniq.map{|p| p.name}.sort*', ')
186
+ end
187
+ ) * "\n\t" +
188
+ "\n"
189
+ =end
190
+ # else
175
191
  # Find the object that has the most followers and no fwd-ref'd supertypes:
176
192
  # This selection might be better if we allow PI roles to be fwd-ref'd...
177
193
  panic = sorted.
178
- select{|o| o.ordered_dumped }.
194
+ select{|o| !o.ordered_dumped }.
179
195
  sort_by{|o|
180
- f = @followers[o] || [];
196
+ f = (@followers[o] || []) - [o];
181
197
  o.supertypes.detect{|s| !s.ordered_dumped } ? 0 : -f.size
182
198
  }[0]
183
- # trace "Panic mode, selected #{panic.name} next"
184
- end
199
+ trace :ordered, "Panic mode, selected #{panic.name} next"
200
+ # end
185
201
  end
186
202
 
187
203
  break if skipped_this_pass == 0 # All done.
@@ -19,6 +19,16 @@ module ActiveFacts
19
19
  end
20
20
 
21
21
  def set_option(option)
22
+ @gen_bootstrap = false
23
+ case option
24
+ when 'help', '?'
25
+ $stderr.puts "Usage:\t\tafgen --html/glossary[=option] input_file.cql\n"+
26
+ "\t\tbootstrap\tGenerate bootstrap styled glossary html"
27
+ exit 0
28
+ when /bootstrap/
29
+ @gen_bootstrap = true
30
+ else super
31
+ end
22
32
  end
23
33
 
24
34
  def puts(*a)
@@ -31,134 +41,156 @@ module ActiveFacts
31
41
 
32
42
  def generate(out = $>)
33
43
  @out = out
44
+
45
+ @all_object_type =
46
+ @vocabulary.
47
+ all_object_type.
48
+ sort_by{|o| o.name.gsub(/ /,'').downcase}
49
+
34
50
  vocabulary_start
35
51
 
36
- object_types_dump()
37
-
52
+ if @gen_bootstrap
53
+ object_types_dump_toc()
54
+ object_types_dump_def()
55
+ else
56
+ object_types_dump_def()
57
+ object_types_dump_toc()
58
+ end
38
59
  vocabulary_end
39
60
  end
40
61
 
41
62
  def vocabulary_start
42
- # puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
43
- File.open(File.dirname(__FILE__)+"/../../../../css/orm2.css") do |f|
44
- puts "<style media='screen' type='text/css'>"
45
- puts f.read
46
- puts %Q{
47
- .glossary-facttype, .glossary-constraints { display: block; }
48
- .glossary-doc.hide-alternates .glossary-alternates { display: none; }
49
- .glossary-doc.hide-constraints .glossary-constraints { display: none; }
50
- .glossary-doc.hide-examples .glossary-example { display: none; }
51
- }.gsub(/^\s+/, '')
52
- puts "</style>"
53
- end
54
-
55
- puts %Q{
56
- <style media='print' type='text/css'>
57
- .keyword { color: #0000CC; font-style: italic; display: inline; }
58
- .vocabulary, .object_type { color: #8A0092; font-weight: bold; }
59
- .copula { color: #0E5400; }
60
- .value { color: #FF990E; display: inline; }
61
- .glossary-toc { display: none; }
62
- .glossary-facttype, .glossary-reading { display: inline; }
63
- </style>
64
- }.gsub(/^\s+/, '')
63
+ if !@gen_bootstrap
64
+ # puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
65
+ css_file = "/../../../../css/orm2.css"
66
+
67
+ File.open(File.dirname(__FILE__)+css_file) do |f|
68
+ puts "<style media='screen' type='text/css'>"
69
+ puts f.read
70
+ puts %Q{
71
+ .glossary-facttype, .glossary-constraints { display: block; }
72
+ .glossary-doc.hide-alternates .glossary-alternates { display: none; }
73
+ .glossary-doc.hide-constraints .glossary-constraints { display: none; }
74
+ .glossary-doc.hide-examples .glossary-example { display: none; }
75
+ }.gsub(/^\s+/, '')
76
+ puts "</style>"
77
+ end
78
+
79
+ puts %Q{
80
+ <style media='print' type='text/css'>
81
+ .keyword { color: #0000CC; font-style: italic; display: inline; }
82
+ .vocabulary, .object_type { color: #8A0092; font-weight: bold; }
83
+ .copula { color: #0E5400; }
84
+ .value { color: #FF990E; display: inline; }
85
+ .glossary-toc { display: none; }
86
+ .glossary-facttype, .glossary-reading { display: inline; }
87
+ </style>
88
+ }.gsub(/^\s+/, '')
89
+ end
65
90
  end
66
91
 
67
92
  def vocabulary_end
68
- puts %Q{
69
- <script type="text/javascript">
70
- function toggle_class(e, c) {
71
- if (!e) return;
72
- var n = e.className;
73
- var i = n.indexOf(c);
74
- if (i == -1) {
75
- e.className = n+' '+c;
76
- } else {
77
- e.className = n.slice(0, i)+n.slice(i+c.length);
78
- }
79
- if (document.location.toString().indexOf('#') >= 0)
80
- document.location = document.location; // Re-scroll to the current fragment
81
- }
82
- function toggle_constraints() {
83
- toggle_class(document.getElementById('glossary-doc'), 'hide-constraints');
84
- }
85
- function toggle_alternates() {
86
- toggle_class(document.getElementById('glossary-doc'), 'hide-alternates');
87
- }
88
- function toggle_examples() {
89
- toggle_class(document.getElementById('glossary-doc'), 'hide-examples');
90
- }
91
- </script>
92
- }.gsub(/^\s+/, '')
93
+ if !@gen_bootstrap
94
+ puts %Q{
95
+ <script type="text/javascript">
96
+ function toggle_class(e, c) {
97
+ if (!e) return;
98
+ var n = e.className;
99
+ var i = n.indexOf(c);
100
+ if (i == -1) {
101
+ e.className = n+' '+c;
102
+ } else {
103
+ e.className = n.slice(0, i)+n.slice(i+c.length);
104
+ }
105
+ if (document.location.toString().indexOf('#') >= 0)
106
+ document.location = document.location; // Re-scroll to the current fragment
107
+ }
108
+ function toggle_constraints() {
109
+ toggle_class(document.getElementById('glossary-doc'), 'hide-constraints');
110
+ }
111
+ function toggle_alternates() {
112
+ toggle_class(document.getElementById('glossary-doc'), 'hide-alternates');
113
+ }
114
+ function toggle_examples() {
115
+ toggle_class(document.getElementById('glossary-doc'), 'hide-examples');
116
+ }
117
+ </script>
118
+ }.gsub(/^\s+/, '')
119
+ end
93
120
  end
94
121
 
95
- def object_types_dump
96
- all_object_type =
97
- @vocabulary.
98
- all_object_type.
99
- sort_by{|o| o.name.gsub(/ /,'').downcase}
100
-
101
- # Put out a table of contents first:
102
- puts '<div class="glossary-sidebar">'
103
- puts '<ol class="glossary-toc">'
104
- all_object_type.
105
- reject do |o|
106
- o.name == '_ImplicitBooleanValueType' or
107
- o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
108
- o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
109
- end.
110
- each do |o|
111
- puts "<li>#{termref(o.name)}</li>"
112
- end
113
- puts '</ol>'
114
- puts '<div class="glossary-controls">'
115
- puts ' <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint">'
116
- puts ' <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates">'
117
- puts ' <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples">'
118
- puts '</div>'
119
- puts '</div>'
120
-
121
- puts '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
122
- puts "<h1>#{@vocabulary.name}</h1>"
123
- puts '<dl>'
124
- all_object_type.
125
- each do |o|
126
- case o
127
- when ActiveFacts::Metamodel::TypeInheritance
128
- nil
129
- when ActiveFacts::Metamodel::ValueType
130
- value_type_dump(o)
131
- else
132
- if o.fact_type
133
- objectified_fact_type_dump(o)
134
- else
135
- entity_type_dump(o)
136
- end
137
- end
138
- end
139
- puts '</dl>'
140
- puts '</div>'
141
- end
122
+ def object_types_dump_toc
123
+ if @gen_bootstrap
124
+ puts '<div class="col-md-3 glossary-sidebar">'
125
+ else
126
+ puts '<div class="glossary-sidebar">'
127
+ end
128
+ puts '<h1 style="visibility: hidden">X</h1>'
129
+ puts '<ol class="glossary-toc">'
130
+ @all_object_type.
131
+ reject do |o|
132
+ o.name == '_ImplicitBooleanValueType' or
133
+ o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
134
+ o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
135
+ end.
136
+ each do |o|
137
+ puts "<li>#{termref(o.name)}</li>"
138
+ end
139
+ puts '</ol>'
140
+ puts '<div class="glossary-controls">'
141
+ puts ' <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint">'
142
+ puts ' <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates">'
143
+ puts ' <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples">'
144
+ puts '</div>'
145
+ puts '</div>'
146
+ end
147
+
148
+ def object_types_dump_def
149
+ if @gen_bootstrap
150
+ puts '<div class="col-md-5 glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
151
+ else
152
+ puts '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
153
+ end
154
+ puts "<h1>#{@vocabulary.name}</h1>"
155
+ puts '<dl>'
156
+ @all_object_type.
157
+ each do |o|
158
+ case o
159
+ when ActiveFacts::Metamodel::TypeInheritance
160
+ nil
161
+ when ActiveFacts::Metamodel::ValueType
162
+ value_type_dump(o)
163
+ else
164
+ if o.fact_type
165
+ objectified_fact_type_dump(o)
166
+ else
167
+ entity_type_dump(o)
168
+ end
169
+ end
170
+ end
171
+ puts '</dl>'
172
+ puts '</div>'
173
+ end
142
174
 
143
175
  def element(text, attrs, tag = 'span')
144
176
  "<#{tag}#{attrs.empty? ? '' : attrs.map{|k,v| " #{k}='#{v}'"}*''}>#{text}</#{tag}>"
145
177
  end
146
178
 
147
- def span(text, klass = nil)
148
- element(text, klass ? {:class => klass} : {})
149
- end
179
+ def span(text, klass = nil)
180
+ element(text, klass ? {:class => klass} : {})
181
+ end
150
182
 
151
- def div(text, klass = nil)
152
- element(text, klass ? {:class => klass} : {}, 'div')
153
- end
183
+ def div(text, klass = nil)
184
+ element(text, klass ? {:class => klass} : {}, 'div')
185
+ end
154
186
 
155
- def h1(text, klass = nil)
156
- element(text, klass ? {:class => klass} : {}, 'h1')
157
- end
187
+ def h1(text, klass = nil)
188
+ element(text, klass ? {:class => klass} : {}, 'h1')
189
+ end
158
190
 
159
- def dl(text, klass = nil)
160
- element(text, klass ? {:class => klass} : {}, 'dl')
161
- end
191
+ def dl(text, klass = nil)
192
+ element(text, klass ? {:class => klass} : {}, 'dl')
193
+ end
162
194
 
163
195
  # A definition of a term
164
196
  def termdef(name)
@@ -178,54 +210,54 @@ module ActiveFacts
178
210
 
179
211
  def value_type_dump(o)
180
212
  return if o.all_role.size == 0 or # Skip value types that are only used as supertypes
181
- o.name == '_ImplicitBooleanValueType'
182
- puts " <dt>" +
183
- "#{termdef(o.name)} " +
184
- (if o.supertype
185
- span('is written as ', :keyword) + termref(o.supertype.name)
186
- else
187
- " (a fundamental data type)"
188
- end) +
189
- "</dt>"
190
-
191
- puts " <dd>"
192
- value_sub_types(o)
193
- relevant_facts_and_constraints(o)
194
- values(o)
195
- puts " </dd>"
213
+ o.name == '_ImplicitBooleanValueType'
214
+ puts " <dt>" +
215
+ "#{termdef(o.name)} " +
216
+ (if o.supertype
217
+ span('is written as ', :keyword) + termref(o.supertype.name)
218
+ else
219
+ " (a fundamental data type)"
220
+ end) +
221
+ "</dt>"
222
+
223
+ puts " <dd>"
224
+ value_sub_types(o)
225
+ relevant_facts_and_constraints(o)
226
+ values(o)
227
+ puts " </dd>"
196
228
  end
197
229
 
198
- def value_sub_types(o)
199
- o.
200
- all_value_type_as_supertype. # All value types for which o is a supertype
201
- sort_by{|sub| sub.name}.
202
- each do |sub|
203
- puts div(
204
- "#{termref(sub.name)} #{span('is written as', 'keyword')} #{termref(o.name)}",
205
- 'glossary-facttype'
206
- )
207
- end
208
- end
209
-
210
- def values(o)
211
- o.all_instance.
212
- sort_by{|i|
213
- [i.population.name, i.value.literal]
214
- }.
215
- each do |i|
216
- v = i.value
217
- puts div(
218
- (i.population.name.empty? ? '' : i.population.name+': ') +
219
- termref(o.name) + ' ' +
220
- div(
221
- # v.is_literal_string ? v.literal.inspect : v.literal,
222
- v.literal.inspect,
223
- 'value'
224
- ),
225
- 'glossary-example'
226
- )
227
- end
228
- end
230
+ def value_sub_types(o)
231
+ o.
232
+ all_value_type_as_supertype. # All value types for which o is a supertype
233
+ sort_by{|sub| sub.name}.
234
+ each do |sub|
235
+ puts div(
236
+ "#{termref(sub.name)} #{span('is written as', 'keyword')} #{termref(o.name)}",
237
+ 'glossary-facttype'
238
+ )
239
+ end
240
+ end
241
+
242
+ def values(o)
243
+ o.all_instance.
244
+ sort_by{|i|
245
+ [i.population.name, i.value.literal]
246
+ }.
247
+ each do |i|
248
+ v = i.value
249
+ puts div(
250
+ (i.population.name.empty? ? '' : i.population.name+': ') +
251
+ termref(o.name) + ' ' +
252
+ div(
253
+ # v.is_literal_string ? v.literal.inspect : v.literal,
254
+ v.literal.inspect,
255
+ 'value'
256
+ ),
257
+ 'glossary-example'
258
+ )
259
+ end
260
+ end
229
261
 
230
262
  def relevant_facts_and_constraints(o)
231
263
  puts(
@@ -234,14 +266,14 @@ module ActiveFacts
234
266
  map{|r| r.fact_type}.
235
267
  uniq.
236
268
  reject do |ft|
237
- ft.is_a?(ActiveFacts::Metamodel::LinkFactType)
238
- end.
239
- map { |ft| [ft, " #{fact_type_with_constraints(ft, o)}"] }.
240
- sort_by{|ft, text|
241
- [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]
242
- }.
243
- map{|ft, text| text}.
244
- join "\n"
269
+ ft.is_a?(ActiveFacts::Metamodel::LinkFactType)
270
+ end.
271
+ map { |ft| [ft, " #{fact_type_with_constraints(ft, o)}"] }.
272
+ sort_by{|ft, text|
273
+ [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]
274
+ }.
275
+ map{|ft, text| text}.
276
+ join "\n"
245
277
  )
246
278
  end
247
279
 
@@ -259,9 +291,9 @@ module ActiveFacts
259
291
  element(
260
292
  reading.expand([], include_rolenames) do |rr, freq_con, l_adj, name, t_adj, role_name_def, literal|
261
293
  if role_name_def
262
- role_name_def = role_name_def.gsub(/\(as ([^)]+)\)/) {
263
- span("(as #{ termref(rr.role.object_type.name, $1) })", 'keyword')
264
- }
294
+ role_name_def = role_name_def.gsub(/\(as ([^)]+)\)/) {
295
+ span("(as #{ termref(rr.role.object_type.name, $1) })", 'keyword')
296
+ }
265
297
  end
266
298
  role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal
267
299
  end,
@@ -270,53 +302,53 @@ module ActiveFacts
270
302
  end
271
303
 
272
304
  def fact_type_block(ft, include_alternates = true, wrt = nil, include_rolenames = true)
273
- div(fact_type(ft, include_alternates, wrt, include_rolenames), 'glossary-facttype')
274
- end
305
+ div(fact_type(ft, include_alternates, wrt, include_rolenames), 'glossary-facttype')
306
+ end
275
307
 
276
308
  def fact_type(ft, include_alternates = true, wrt = nil, include_rolenames = true)
277
309
  role = ft.all_role.detect{|r| r.object_type == wrt}
278
310
  preferred_reading = ft.reading_preferably_starting_with_role(role)
279
311
  alternate_readings = ft.all_reading.reject{|r| r == preferred_reading}
280
312
 
281
- div(
282
- expand_reading(preferred_reading, include_rolenames),
283
- 'glossary-reading'
284
- )+
285
- (if include_alternates and alternate_readings.size > 0
286
- div(
287
- "(alternatively: " +
288
- alternate_readings.map do |reading|
289
- div(
290
- expand_reading(reading, include_rolenames),
291
- 'glossary-reading'
292
- )
293
- end*",\n"+')',
294
- 'glossary-alternates'
295
- )
296
- else
297
- ''
298
- end
299
- )
313
+ div(
314
+ expand_reading(preferred_reading, include_rolenames),
315
+ 'glossary-reading'
316
+ )+
317
+ (if include_alternates and alternate_readings.size > 0
318
+ div(
319
+ "(alternatively: " +
320
+ alternate_readings.map do |reading|
321
+ div(
322
+ expand_reading(reading, include_rolenames),
323
+ 'glossary-reading'
324
+ )
325
+ end*",\n"+')',
326
+ 'glossary-alternates'
327
+ )
328
+ else
329
+ ''
330
+ end
331
+ )
300
332
  end
301
333
 
302
334
  def fact_type_with_constraints(ft, wrt = nil)
303
- if ft.entity_type
304
- div(
305
- termref(ft.entity_type.name) +
306
- div(' is where ', 'keyword') +
307
- fact_type(ft, true, wrt),
308
- 'glossary-objectification'
309
- )
310
- else
311
- fact_type_block(ft, true, wrt)
312
- end +
313
- %Q{\n<ul class="glossary-constraints">\n}+
314
- (unless ft.is_a?(ActiveFacts::Metamodel::TypeInheritance)
315
- fact_type_constraints(ft)
316
- else
317
- ''
318
- end) +
319
- "</ul>"
335
+ if ft.entity_type
336
+ div(
337
+ termref(ft.entity_type.name) +
338
+ div(' is where ', 'keyword') +
339
+ fact_type(ft, true, wrt),
340
+ 'glossary-objectification'
341
+ )
342
+ else
343
+ fact_type_block(ft, true, wrt)
344
+ end +
345
+ %Q{\n<ul class="glossary-constraints">\n}+
346
+ (unless ft.is_a?(ActiveFacts::Metamodel::TypeInheritance)
347
+ fact_type_constraints(ft)
348
+ else
349
+ ''
350
+ end) +
351
+ "</ul>"
320
352
  end
321
353
 
322
354
  def fact_type_constraints(ft)
@@ -327,14 +359,14 @@ module ActiveFacts
327
359
  reading.role_sequence.all_role_ref_in_order[reading.role_numbers[-1]].role == residual_role
328
360
  }
329
361
  next nil unless reading
330
- div(
331
- element(
332
- reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
333
- {:class => 'copula'}
334
- ),
335
- 'glossary-constraint'
336
- )+"\n"
337
- end.compact*''
362
+ div(
363
+ element(
364
+ reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
365
+ {:class => 'copula'}
366
+ ),
367
+ 'glossary-constraint'
368
+ )+"\n"
369
+ end.compact*''
338
370
  end
339
371
 
340
372
  def objectified_fact_type_dump(o)
@@ -345,7 +377,7 @@ module ActiveFacts
345
377
  # REVISIT: Handle separate identification
346
378
 
347
379
  puts " <dd>"
348
- puts fact_type_with_constraints(o.fact_type)
380
+ puts fact_type_with_constraints(o.fact_type)
349
381
 
350
382
  o.fact_type.all_role_in_order.each do |r|
351
383
  n = r.object_type.name
@@ -367,59 +399,59 @@ module ActiveFacts
367
399
  [
368
400
  (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}" : nil),
369
401
  (if pi
370
- "#{span('is identified by', 'keyword')} " +
371
- pi.role_sequence.all_role_ref_in_order.map do |rr|
372
- termref(
373
- rr.role.object_type.name,
374
- [ rr.leading_adjective,
375
- rr.role.role_name || rr.role.object_type.name,
376
- rr.trailing_adjective
377
- ].compact*'-'
378
- )
379
- end*", "
380
- else
381
- nil
382
- end)
402
+ "#{span('is identified by', 'keyword')} " +
403
+ pi.role_sequence.all_role_ref_in_order.map do |rr|
404
+ termref(
405
+ rr.role.object_type.name,
406
+ [ rr.leading_adjective,
407
+ rr.role.role_name || rr.role.object_type.name,
408
+ rr.trailing_adjective
409
+ ].compact*'-'
410
+ )
411
+ end*", "
412
+ else
413
+ nil
414
+ end)
383
415
  ].compact*', '
384
- "</dt>"
416
+ "</dt>"
385
417
 
386
- puts " <dd>"
387
- relevant_facts_and_constraints(o)
388
- entities(o)
389
- puts " </dd>"
418
+ puts " <dd>"
419
+ relevant_facts_and_constraints(o)
420
+ entities(o)
421
+ puts " </dd>"
390
422
  end
391
423
 
392
- def entities(o)
393
- return if o.preferred_identifier.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
394
- o.all_instance.each do |i|
395
- v = i.value
396
- ii = i # The identifying instance
397
-
398
- until v
399
- pi = ii.object_type.preferred_identifier # ii is an Entity Type
400
- break if pi.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
401
-
402
- identifying_fact_type = pi.role_sequence.all_role_ref.single.role.fact_type
403
- # Find the role played by this instance through which it is identified:
404
- irv = i.all_role_value.detect{|rv| rv.fact.fact_type == identifying_fact_type }
405
- # Get the other RoleValue in what must be a binary fact type:
406
- orv = irv.fact.all_role_value.detect{|rv| rv != irv}
407
- ii = orv.instance
408
- v = ii.value # Does this instance have a value? If so, we're done.
409
- end
410
-
411
- next unless v
412
- puts div(
413
- (i.population.name.empty? ? '' : i.population.name+': ') +
414
- termref(o.name) + ' ' +
415
- div(
416
- # v.is_literal_string ? v.literal.inspect : v.literal,
417
- v.literal.inspect,
418
- 'value'),
419
- 'glossary-example'
420
- )
421
- end
422
- end
424
+ def entities(o)
425
+ return if o.preferred_identifier.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
426
+ o.all_instance.each do |i|
427
+ v = i.value
428
+ ii = i # The identifying instance
429
+
430
+ until v
431
+ pi = ii.object_type.preferred_identifier # ii is an Entity Type
432
+ break if pi.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
433
+
434
+ identifying_fact_type = pi.role_sequence.all_role_ref.single.role.fact_type
435
+ # Find the role played by this instance through which it is identified:
436
+ irv = i.all_role_value.detect{|rv| rv.fact.fact_type == identifying_fact_type }
437
+ # Get the other RoleValue in what must be a binary fact type:
438
+ orv = irv.fact.all_role_value.detect{|rv| rv != irv}
439
+ ii = orv.instance
440
+ v = ii.value # Does this instance have a value? If so, we're done.
441
+ end
442
+
443
+ next unless v
444
+ puts div(
445
+ (i.population.name.empty? ? '' : i.population.name+': ') +
446
+ termref(o.name) + ' ' +
447
+ div(
448
+ # v.is_literal_string ? v.literal.inspect : v.literal,
449
+ v.literal.inspect,
450
+ 'value'),
451
+ 'glossary-example'
452
+ )
453
+ end
454
+ end
423
455
 
424
456
  end
425
457
  end
@@ -143,13 +143,14 @@ module ActiveFacts
143
143
 
144
144
  foreign_keys.concat(
145
145
  if (from_columns.length == 1)
146
+ index_name = Persistence.rails_name_trunc('index_'+fk.from.rails_name+'_on_'+from_columns[0])
146
147
  [
147
148
  " add_foreign_key :#{fk.from.rails_name}, :#{fk.to.rails_name}, :column => :#{from_columns[0]}, :primary_key => :#{to_columns[0]}, :on_delete => :cascade"
148
149
  ]+
149
150
  Array(
150
151
  # Index it non-uniquely only if it's not unique already:
151
152
  fk.jump_reference.to_role.unique ? nil :
152
- " add_index :#{fk.from.rails_name}, [:#{from_columns[0]}], :unique => false"
153
+ " add_index :#{fk.from.rails_name}, [:#{from_columns[0]}], :unique => false, :name => :#{index_name}"
153
154
  )
154
155
  else
155
156
  # This probably isn't going to work without Dr Nic's CPK gem:
@@ -128,7 +128,10 @@ module ActiveFacts
128
128
 
129
129
  irf.flatten!
130
130
 
131
- # Multi-part identifiers are only allowed if each part is a foreign key (i.e. it's a join table) and the object is not the target of a foreign key:
131
+ # Multi-part identifiers are only allowed if:
132
+ # * each part is a foreign key (i.e. it's a join table),
133
+ # * there are no other columns (that might require updating) and
134
+ # * the object is not the target of a foreign key:
132
135
  if irf.size >= 2
133
136
  if pk_fks.include?(nil)
134
137
  trace :transform_surrogate, "#{self.name} needs a surrogate because its multi-part key contains a non-table"
@@ -136,6 +139,9 @@ module ActiveFacts
136
139
  elsif references_to.size != 0
137
140
  trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect} but is also an FK target"
138
141
  return true
142
+ elsif (references_from-identifying_refs_from).size > 0
143
+ # There are other attributes to worry about
144
+ return true
139
145
  else
140
146
  trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect}"
141
147
  return false
@@ -14,9 +14,13 @@ module ActiveFacts
14
14
  class CQL
15
15
  # Read the specified file
16
16
  def self.readfile(filename)
17
- File.open(filename) {|file|
18
- read(file, filename)
19
- }
17
+ if File.basename(filename, '.cql') == "-"
18
+ read(STDIN, "<standard input>")
19
+ else
20
+ File.open(filename) {|file|
21
+ read(file, filename)
22
+ }
23
+ end
20
24
  rescue => e
21
25
  # Augment the exception message, but preserve the backtrace
22
26
  ne = StandardError.new("In #{filename} #{e.message.strip}")
@@ -72,9 +72,13 @@ module ActiveFacts
72
72
 
73
73
  private
74
74
  def self.readfile(filename, *options)
75
- File.open(filename) {|file|
76
- self.read(file, filename, *options)
77
- }
75
+ if File.basename(filename, '.orm') == "-"
76
+ self.read(STDIN, "<standard input>", options)
77
+ else
78
+ File.open(filename) {|file|
79
+ self.read(file, filename, *options)
80
+ }
81
+ end
78
82
  end
79
83
 
80
84
  def self.read(file, filename = "stdin", *options)
@@ -378,7 +382,7 @@ module ActiveFacts
378
382
  rs2 = @constellation.RoleSequence(:new)
379
383
  @constellation.RoleRef(rs2, 0, :role => supertype_role)
380
384
  @constellation.RoleRef(rs2, 1, :role => subtype_role)
381
- n = 'aeiouh'.include?(subtype_role.object_type.name.downcase[0]) ? 1 : 0
385
+ n = 'aeioh'.include?(subtype_role.object_type.name.downcase[0]) ? 1 : 0
382
386
  @constellation.Reading(inheritance_fact, 2+n, :role_sequence => rs2, :text => "{0} is a {1}", :is_negative => false)
383
387
  @constellation.Reading(inheritance_fact, 3-n, :role_sequence => rs2, :text => "{0} is an {1}", :is_negative => false)
384
388
  }
@@ -835,12 +839,12 @@ module ActiveFacts
835
839
  @constellation.Variable(query, query.all_variable.size, :object_type => ti.subtype)
836
840
  supertype_node = query.all_variable.detect{|jn| jn.object_type == ti.supertype } ||
837
841
  @constellation.Variable(query, query.all_variable.size, :object_type => ti.supertype)
842
+ step = @constellation.Step(:guid => :new, :fact_type => ti)
838
843
  rs = @constellation.RoleSequence(:new)
839
844
  @constellation.RoleRef(rs, 0, :role => ti.subtype_role)
840
- sub_play = @constellation.Play(subtype_node, ti.subtype_role)
845
+ sub_play = @constellation.Play(:step => step, :variable => subtype_node, :role => ti.subtype_role)
841
846
  @constellation.RoleRef(rs, 1, :role => ti.supertype_role)
842
- sup_play = @constellation.Play(supertype_node, ti.supertype_role)
843
- step = @constellation.Step(sub_play, sup_play, :fact_type => ti)
847
+ sup_play = @constellation.Play(:step => step, :variable => supertype_node, :role => ti.supertype_role, :is_input => true)
844
848
  trace :query, "New subtyping step #{step.describe}"
845
849
  step
846
850
  end
@@ -972,11 +976,11 @@ module ActiveFacts
972
976
  # There may be more than one supertyping level. Make the steps:
973
977
  subtyping_steps = subtype_steps(query, subtype, end_point)
974
978
  step = subtyping_steps[0]
975
- constrained_play = subtyping_steps[-1].input_play
979
+ constrained_play = subtyping_steps[-1].all_play.detect{|p| p.role.object_type == end_point}
976
980
 
977
981
  # Replace the constrained role and node with the supertype ones:
978
982
  end_node = query.all_variable.detect{|jn| jn.object_type == end_point }
979
- projecting_play = step.output_play
983
+ projecting_play = step.all_play.detect{|p| p.role.object_type == subtype}
980
984
  end_role = step.fact_type.all_role.detect{|r| r.object_type == end_point }
981
985
  role_node = query.all_variable.detect{|jn| jn.object_type == role_ref.role.object_type }
982
986
  end
@@ -984,7 +988,7 @@ module ActiveFacts
984
988
 
985
989
  raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, end role mismatch" if end_role.object_type != end_node.object_type
986
990
  rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => end_role)
987
- projecting_play ||= (constrained_play = @constellation.Play(end_node, end_role))
991
+ projecting_play ||= (constrained_play = @constellation.Play(:variable => end_node, :role => end_role))
988
992
  projecting_play.role_ref = rr # Project this RoleRef
989
993
  # projecting_play.variable.projection = rr.role # REVISIT: The variable should project a role, not the Play a RoleRef
990
994
 
@@ -997,13 +1001,12 @@ module ActiveFacts
997
1001
  rs = @constellation.RoleSequence(:new)
998
1002
  # Detect the fact type over which we're stepping (may involve objectification)
999
1003
  raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, object type mismatch" if role_ref.role.object_type != role_node.object_type
1004
+ step = @constellation.Step(:guid => :new, :fact_type => joined_role.fact_type)
1000
1005
  @constellation.RoleRef(rs, 0, :role => role_ref.role)
1001
- role_play = @constellation.Play(role_node, role_ref.role)
1006
+ role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role, :is_input => true)
1002
1007
  raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, joined_role mismatch" if joined_role.object_type != variable.object_type
1003
1008
  @constellation.RoleRef(rs, 1, :role => joined_role)
1004
- join_play = @constellation.Play(variable, joined_role)
1005
-
1006
- step = @constellation.Step(role_play, join_play, :fact_type => joined_role.fact_type)
1009
+ join_play = @constellation.Play(:step => step, :variable => variable, :role => joined_role)
1007
1010
  trace :query, "New step #{step.describe}"
1008
1011
  end
1009
1012
  else
@@ -1012,13 +1015,13 @@ module ActiveFacts
1012
1015
  # Here we have an end join (step already created) but no sequence join
1013
1016
  if variable
1014
1017
  raise "Internal error in #{constraint_type} #{name}: making illegal step" if role_ref.role.object_type != role_node.object_type
1015
- join_play = @constellation.Play(variable, query_role)
1016
- role_play = @constellation.Play(role_node, role_ref.role)
1017
- step = @constellation.Step(join_play, role_play, :fact_type => role_ref.role.fact_type)
1018
+ step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
1019
+ join_play = @constellation.Play(:step => step, :variable => variable, :role => query_role, :is_input => true)
1020
+ role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role)
1018
1021
  roles -= [query_role, role_ref.role]
1019
1022
  roles.each do |incidental_role|
1020
1023
  jn = @constellation.Variable(query, query.all_variable.size, :object_type => incidental_role.object_type)
1021
- play = @constellation.Play(jn, incidental_role, :step => step)
1024
+ play = @constellation.Play(:step => step, :variable => jn, :role => incidental_role, :step => step)
1022
1025
  end
1023
1026
  else
1024
1027
  if role_sequence.all_role_ref.size > 1
@@ -1028,25 +1031,20 @@ module ActiveFacts
1028
1031
  # There's no query in this role sequence, so we'd drop off the bottom without doing the right things. Why?
1029
1032
  # Without this case, Supervision.orm omits "that runs Company" from the exclusion constraint, and I'm not sure why.
1030
1033
  # I think the "then" code causes it to drop out the bottom without making the step (which is otherwise made in every case, see CompanyDirectorEmployee for example)
1031
- role_play = @constellation.Play(role_node, role_ref.role)
1032
- step = nil
1034
+ step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
1035
+ role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role, :is_input => true)
1033
1036
  role_ref.role.fact_type.all_role.each do |role|
1034
1037
  next if role == role_play.role
1035
1038
  next if role_sequence.all_role_ref.detect{|rr| rr.role == role}
1036
1039
  jn = @constellation.Variable(query, query.all_variable.size, :object_type => role.object_type)
1037
- play = @constellation.Play(jn, role)
1038
- if step
1039
- play.step = step # Incidental role
1040
- else
1041
- step = @constellation.Step(role_play, play, :fact_type => role_ref.role.fact_type)
1042
- end
1040
+ play = @constellation.Play(:step => step, :variable => jn, :role => role)
1043
1041
  end
1044
1042
  end
1045
1043
  end
1046
1044
  else
1047
1045
  # Unary fact type, make a Step from and to the constrained_play
1048
- play = @constellation.Play(constrained_play.variable, role_ref.role)
1049
- step = @constellation.Step(play, play, :fact_type => role_ref.role.fact_type)
1046
+ step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
1047
+ play = @constellation.Play(:step => step, :variable => constrained_play.variable, :role => role_ref.role, :is_input => true)
1050
1048
  end
1051
1049
  end
1052
1050
  end
@@ -1092,9 +1090,15 @@ module ActiveFacts
1092
1090
  @mandatory_constraints_by_rs.delete(mc_rs)
1093
1091
  end
1094
1092
 
1095
- next if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
1093
+ if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
1094
+ trace :orm, "skipped exclusion constraint #{id}, missing role sequence"
1095
+ next
1096
+ end
1096
1097
 
1097
- next unless make_queries('exclusion', name+(x_mandatory ? '/'+x_mandatory['Name'] : ''), role_sequences)
1098
+ unless make_queries('exclusion', name+(x_mandatory ? '/'+x_mandatory['Name'] : ''), role_sequences)
1099
+ trace :orm, "skipped exclusion constraint #{id}, can't make_queries"
1100
+ next
1101
+ end
1098
1102
 
1099
1103
  ec = @constellation.SetExclusionConstraint(id_of(x))
1100
1104
  ec.vocabulary = @vocabulary
@@ -5,6 +5,13 @@ require 'digest/sha1'
5
5
 
6
6
  module ActiveFacts
7
7
  module Persistence
8
+ def self.rails_name_trunc name
9
+ if name.length > 63
10
+ hash = Digest::SHA1.hexdigest name
11
+ name = name[0, 53] + '__' + hash[0, 8]
12
+ end
13
+ name
14
+ end
8
15
 
9
16
  def self.rails_plural_name name
10
17
  # Crunch spaces and pluralise the first part, all in snake_case
@@ -87,11 +94,7 @@ module ActiveFacts
87
94
  def rails_name
88
95
  column_names = columns.map{|c| c.rails_name }
89
96
  index_name = "index_#{on.rails_name+'_on_'+column_names*'_'}"
90
- if index_name.length > 63
91
- hash = Digest::SHA1.hexdigest index_name
92
- index_name = index_name[0, 53] + '__' + hash[0, 8]
93
- end
94
- index_name
97
+ Persistence.rails_name_trunc index_name
95
98
  end
96
99
  end
97
100
 
@@ -304,7 +304,8 @@ module ActiveFacts
304
304
  when :supertype # A subtype absorbs a reference to its supertype when separate, or all when partitioned
305
305
  # REVISIT: Or when partitioned
306
306
  raise hell unless role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
307
- if role.fact_type.assimilation # assimilation == 'separate' or assimilation == 'partitioned'
307
+ counterpart_role = (role.fact_type.all_role.to_a-[role])[0]
308
+ if role.fact_type.assimilation or counterpart_role.object_type.is_independent # assimilation == 'separate' or assimilation == 'partitioned'
308
309
  trace :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
309
310
  else
310
311
  r = ActiveFacts::Persistence::Reference.new(self, role)
@@ -314,7 +315,7 @@ module ActiveFacts
314
315
  end
315
316
 
316
317
  when :subtype # This object is a supertype, which can absorb the subtype unless that's independent
317
- if role.fact_type.assimilation
318
+ if role.fact_type.assimilation or is_independent
318
319
  ActiveFacts::Persistence::Reference.new(self, role).tabulate
319
320
  # If partitioned, the supertype is absorbed into *each* subtype; a reference to the supertype needs to know which
320
321
  else
@@ -7,8 +7,8 @@
7
7
  module ActiveFacts
8
8
  module Version
9
9
  MAJOR = 1
10
- MINOR = 2
11
- PATCH = 1
10
+ MINOR = 3
11
+ PATCH = 0
12
12
 
13
13
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clifford Heath
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-12 00:00:00.000000000 Z
11
+ date: 2015-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activefacts-api