activefacts 1.2.1 → 1.3.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.
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