activefacts 0.8.16 → 0.8.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +15 -0
  2. data/Manifest.txt +10 -4
  3. data/bin/afgen +26 -20
  4. data/bin/cql +1 -1
  5. data/css/orm2.css +89 -9
  6. data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
  7. data/examples/CQL/Genealogy.cql +5 -5
  8. data/examples/CQL/Metamodel.cql +121 -91
  9. data/examples/CQL/MonthInSeason.cql +2 -6
  10. data/examples/CQL/SeparateSubtype.cql +11 -9
  11. data/examples/CQL/ServiceDirector.cql +21 -33
  12. data/examples/CQL/Supervision.cql +0 -3
  13. data/examples/CQL/WindowInRoomInBldg.cql +10 -4
  14. data/examples/CQL/unit.cql +1 -1
  15. data/lib/activefacts.rb +1 -0
  16. data/lib/activefacts/cql/CQLParser.treetop +5 -1
  17. data/lib/activefacts/cql/Context.treetop +2 -7
  18. data/lib/activefacts/cql/Expressions.treetop +2 -2
  19. data/lib/activefacts/cql/FactTypes.treetop +37 -31
  20. data/lib/activefacts/cql/Language/English.treetop +21 -4
  21. data/lib/activefacts/cql/LexicalRules.treetop +59 -1
  22. data/lib/activefacts/cql/ObjectTypes.treetop +22 -12
  23. data/lib/activefacts/cql/Terms.treetop +13 -9
  24. data/lib/activefacts/cql/ValueTypes.treetop +30 -11
  25. data/lib/activefacts/cql/compiler.rb +34 -5
  26. data/lib/activefacts/cql/compiler/clause.rb +207 -116
  27. data/lib/activefacts/cql/compiler/constraint.rb +129 -105
  28. data/lib/activefacts/cql/compiler/entity_type.rb +49 -27
  29. data/lib/activefacts/cql/compiler/expression.rb +71 -42
  30. data/lib/activefacts/cql/compiler/fact.rb +70 -64
  31. data/lib/activefacts/cql/compiler/fact_type.rb +108 -57
  32. data/lib/activefacts/cql/compiler/query.rb +178 -0
  33. data/lib/activefacts/cql/compiler/shared.rb +13 -12
  34. data/lib/activefacts/cql/compiler/value_type.rb +10 -4
  35. data/lib/activefacts/cql/nodes.rb +1 -1
  36. data/lib/activefacts/cql/parser.rb +6 -2
  37. data/lib/activefacts/generate/absorption.rb +6 -3
  38. data/lib/activefacts/generate/cql.rb +140 -84
  39. data/lib/activefacts/generate/dm.rb +12 -6
  40. data/lib/activefacts/generate/help.rb +25 -6
  41. data/lib/activefacts/generate/helpers/oo.rb +195 -0
  42. data/lib/activefacts/generate/helpers/ordered.rb +589 -0
  43. data/lib/activefacts/generate/helpers/rails.rb +57 -0
  44. data/lib/activefacts/generate/html/glossary.rb +274 -54
  45. data/lib/activefacts/generate/json.rb +25 -22
  46. data/lib/activefacts/generate/null.rb +1 -0
  47. data/lib/activefacts/generate/rails/models.rb +244 -0
  48. data/lib/activefacts/generate/rails/schema.rb +185 -0
  49. data/lib/activefacts/generate/records.rb +1 -0
  50. data/lib/activefacts/generate/ruby.rb +51 -30
  51. data/lib/activefacts/generate/sql/mysql.rb +5 -3
  52. data/lib/activefacts/generate/sql/server.rb +8 -4
  53. data/lib/activefacts/generate/text.rb +1 -0
  54. data/lib/activefacts/generate/transform/surrogate.rb +209 -0
  55. data/lib/activefacts/generate/version.rb +1 -0
  56. data/lib/activefacts/input/orm.rb +234 -181
  57. data/lib/activefacts/mapping/rails.rb +122 -0
  58. data/lib/activefacts/persistence/columns.rb +34 -18
  59. data/lib/activefacts/persistence/foreignkey.rb +129 -71
  60. data/lib/activefacts/persistence/index.rb +42 -12
  61. data/lib/activefacts/persistence/reference.rb +37 -23
  62. data/lib/activefacts/persistence/tables.rb +53 -19
  63. data/lib/activefacts/registry.rb +11 -0
  64. data/lib/activefacts/support.rb +28 -10
  65. data/lib/activefacts/version.rb +1 -1
  66. data/lib/activefacts/vocabulary/extensions.rb +246 -117
  67. data/lib/activefacts/vocabulary/metamodel.rb +105 -65
  68. data/lib/activefacts/vocabulary/verbaliser.rb +226 -194
  69. data/spec/absorption_spec.rb +1 -0
  70. data/spec/cql/comparison_spec.rb +8 -8
  71. data/spec/cql/contractions_spec.rb +16 -43
  72. data/spec/cql/entity_type_spec.rb +2 -1
  73. data/spec/cql/expressions_spec.rb +2 -2
  74. data/spec/cql/fact_type_matching_spec.rb +4 -1
  75. data/spec/cql/parser/bad_literals_spec.rb +30 -30
  76. data/spec/cql/parser/entity_types_spec.rb +6 -6
  77. data/spec/cql/parser/expressions_spec.rb +25 -19
  78. data/spec/cql/samples_spec.rb +5 -4
  79. data/spec/cql_cql_spec.rb +2 -1
  80. data/spec/cql_dm_spec.rb +4 -0
  81. data/spec/cql_mysql_spec.rb +4 -0
  82. data/spec/cql_parse_spec.rb +2 -0
  83. data/spec/cql_ruby_spec.rb +4 -0
  84. data/spec/cql_sql_spec.rb +4 -0
  85. data/spec/cqldump_spec.rb +7 -4
  86. data/spec/helpers/parse_to_ast_matcher.rb +7 -3
  87. data/spec/helpers/test_parser.rb +2 -0
  88. data/spec/norma_cql_spec.rb +5 -2
  89. data/spec/norma_ruby_spec.rb +4 -1
  90. data/spec/norma_ruby_sql_spec.rb +4 -1
  91. data/spec/norma_sql_spec.rb +4 -1
  92. data/spec/norma_tables_spec.rb +2 -2
  93. data/spec/ruby_api_spec.rb +1 -1
  94. data/spec/spec_helper.rb +2 -0
  95. data/spec/transform_surrogate_spec.rb +59 -0
  96. metadata +70 -60
  97. data/TODO +0 -308
  98. data/lib/activefacts/cql/compiler/join.rb +0 -162
  99. data/lib/activefacts/generate/oo.rb +0 -176
  100. data/lib/activefacts/generate/ordered.rb +0 -602
@@ -2,20 +2,20 @@ module ActiveFacts
2
2
  module CQL
3
3
  class Compiler < ActiveFacts::CQL::Parser
4
4
 
5
- # In a declaration, a Variable has one or more VarRef's.
6
- # A Variable is for a single ObjectType, normally related to just one Role,
7
- # and the references (VarRefs) to it will normally be the object_type name
5
+ # In a declaration, a Binding has one or more Reference's.
6
+ # A Binding is for a single ObjectType, normally related to just one Role,
7
+ # and the references (References) to it will normally be the object_type name
8
8
  # with the same adjectives (modulo loose binding),
9
9
  # or a role name or subscript reference.
10
10
  #
11
- # In some situations a Variable will have some VarRefs with the same adjectives,
12
- # and one or more VarRefs with no adjectives - this is called "loose binding".
13
- class Variable
11
+ # In some situations a Binding will have some References with the same adjectives,
12
+ # and one or more References with no adjectives - this is called "loose binding".
13
+ class Binding
14
14
  attr_reader :player # The ObjectType (object type)
15
- attr_reader :refs # an array of the VarRefs
15
+ attr_reader :refs # an array of the References
16
16
  attr_reader :role_name
17
- attr_accessor :rebound_to # Loose binding may set this to another variable
18
- attr_accessor :join_node
17
+ attr_accessor :rebound_to # Loose binding may set this to another binding
18
+ attr_accessor :variable
19
19
  attr_accessor :instance # When binding fact instances, the instance goes here
20
20
 
21
21
  def initialize player, role_name = nil
@@ -43,14 +43,14 @@ module ActiveFacts
43
43
  attr_accessor :left_contraction_allowed
44
44
  attr_accessor :left_contractable_clause
45
45
  attr_accessor :left_contraction_conjunction
46
- attr_reader :variables # The Variables in this declaration
46
+ attr_reader :bindings # The Bindings in this declaration
47
47
  attr_reader :player_by_role_name
48
48
 
49
49
  def initialize vocabulary
50
50
  @vocabulary = vocabulary
51
51
  @vocabulary_identifier = @vocabulary.identifying_role_values
52
52
  @allowed_forward_terms = []
53
- @variables = {}
53
+ @bindings = {}
54
54
  @player_by_role_name = {}
55
55
  @left_contraction_allowed = false
56
56
  end
@@ -64,13 +64,14 @@ module ActiveFacts
64
64
  player ||= @player_by_role_name[name]
65
65
 
66
66
  if !player && @allowed_forward_terms.include?(name)
67
+ @vocabulary.valid_entity_type_name(name) # No need for the result here, just no exceptional condition
67
68
  player = constellation.EntityType(@vocabulary, name, :guid => :new)
68
69
  end
69
70
 
70
71
  player
71
72
  end
72
73
 
73
- # Pass in an array of clauses or VarRefs for player identification and binding (creating the Variables)
74
+ # Pass in an array of clauses or References for player identification and binding (creating the Bindings)
74
75
  # It's necessary to identify all players that define a role name first,
75
76
  # so those names exist in the context for where they're used.
76
77
  def bind *clauses
@@ -87,29 +87,31 @@ module ActiveFacts
87
87
  end
88
88
 
89
89
  class ValueType < ObjectType
90
- def initialize name, base, parameters, unit, value_constraint, pragmas
90
+ def initialize name, base, parameters, unit, value_constraint, pragmas, context_note
91
91
  super name
92
92
  @base_type_name = base
93
93
  @parameters = parameters
94
94
  @unit = unit
95
95
  @value_constraint = value_constraint
96
96
  @pragmas = pragmas
97
+ @context_note = context_note
97
98
  end
98
99
 
99
100
  def compile
100
101
  length, scale = *@parameters
101
102
 
102
- # Create the base type:
103
+ # Create the base type unless it already exists:
103
104
  base_type = nil
104
105
  if (@base_type_name != @name)
105
- unless base_type = @constellation.ValueType[[@vocabulary.identifying_role_values, @constellation.Name(@base_type_name)]]
106
+ unless base_type = @vocabulary.valid_value_type_name(@base_type_name)
106
107
  base_type = @constellation.ValueType(@vocabulary, @base_type_name, :guid => :new)
107
108
  return base_type if @base_type_name == @name
108
109
  end
109
110
  end
110
111
 
111
112
  # Create and initialise the ValueType:
112
- vt = @constellation.ValueType(@vocabulary, @name, :guid => :new)
113
+ vt = @vocabulary.valid_value_type_name(@name) ||
114
+ @constellation.ValueType(@vocabulary, @name, :guid => :new)
113
115
  vt.is_independent = true if (@pragmas.include? 'independent')
114
116
  vt.supertype = base_type if base_type
115
117
  vt.length = length if length
@@ -141,6 +143,10 @@ module ActiveFacts
141
143
  vt.value_constraint = @value_constraint.compile
142
144
  end
143
145
 
146
+ if @context_note
147
+ @context_note.compile(@constellation, vt)
148
+ end
149
+
144
150
  vt
145
151
  end
146
152
 
@@ -13,7 +13,7 @@ module ActiveFacts
13
13
  trailing_adjective = t[gt.size+1..-1]
14
14
  trailing_adjective.sub!(/ (\S*)\Z/, '-\1') if !tail.elements[-1].dbl.empty?
15
15
  end
16
- Compiler::VarRef.new(gt, leading_adjective, trailing_adjective, quantifier, function_call, role_name, value_constraint, literal, nested_clauses)
16
+ Compiler::Reference.new(gt, leading_adjective, trailing_adjective, quantifier, function_call, role_name, value_constraint, literal, nested_clauses)
17
17
  end
18
18
 
19
19
  def value # Sometimes we just want the full term name
@@ -48,6 +48,7 @@ module ActiveFacts
48
48
  # These methods are semantic predicates; if they return false this parse rule will fail.
49
49
  class Context
50
50
  attr_reader :term, :global_term
51
+ attr_reader :terms
51
52
 
52
53
  def initialize(parser)
53
54
  @parser = parser
@@ -150,7 +151,7 @@ module ActiveFacts
150
151
  # Index the name by all prefixes
151
152
  def index_name(index, name, value = true)
152
153
  added = false
153
- words = name.scan(/\w+/)
154
+ words = name.split(/\s+/)
154
155
  words.inject("") do |n, w|
155
156
  # Index all prefixes up to the full term
156
157
  n = n.empty? ? w : "#{n} #{w}"
@@ -220,7 +221,10 @@ module ActiveFacts
220
221
  nodes = []
221
222
  begin
222
223
  node = parse(InputProxy.new(input, context, self), :index => @index)
223
- return nil unless node
224
+ unless node
225
+ raise failure_reason unless @index == input.size
226
+ return nil # No input, or no more input
227
+ end
224
228
  if @block
225
229
  @block.call(node)
226
230
  else
@@ -4,6 +4,7 @@
4
4
  #
5
5
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
6
  #
7
+ require 'activefacts/vocabulary'
7
8
  require 'activefacts/persistence'
8
9
 
9
10
  module ActiveFacts
@@ -18,7 +19,7 @@ module ActiveFacts
18
19
  # * paths Show the references paths through which each column was defined
19
20
  # * no_identifier Don't show the identified_by columns for an EntityType
20
21
 
21
- class ABSORPTION
22
+ class Absorption
22
23
  def initialize(vocabulary, *options) #:nodoc:
23
24
  @vocabulary = vocabulary
24
25
  @vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
@@ -28,6 +29,7 @@ module ActiveFacts
28
29
  end
29
30
 
30
31
  def generate(out = $>) #:nodoc:
32
+ @out = out
31
33
  no_absorption = 0
32
34
  single_absorption_vts = 0
33
35
  single_absorption_ets = 0
@@ -45,7 +47,7 @@ module ActiveFacts
45
47
  pk = indices.select(&:is_primary)[0]
46
48
  indices = indices.clone
47
49
  indices.delete pk
48
- puts "#{object_type.name}: #{
50
+ @out.puts "#{object_type.name}: #{
49
51
  # "[#{object_type.indices.size} indices] "
50
52
  # } #{
51
53
  object_type.columns.sort_by do |column|
@@ -55,7 +57,7 @@ module ActiveFacts
55
57
  [pk && pk.columns.include?(column) ? "*" : nil] +
56
58
  (0...indices.size).select{|i| indices[i].columns.include?(column)}.map{|i| (i+1).to_i }
57
59
  index_nrs.compact!
58
- (@paths ? column.references.map{|r| r.to_names}.flatten : column.name(nil)) * "." +
60
+ (@paths ? column.references.map{|r| r.to_names}.flatten : column.name(nil)) * '.' +
59
61
  (index_nrs.empty? ? "" : "["+index_nrs*""+"]")
60
62
  end*", "
61
63
  }"
@@ -65,3 +67,4 @@ module ActiveFacts
65
67
  end
66
68
  end
67
69
 
70
+ ActiveFacts::Registry.generator('absorption', ActiveFacts::Generate::Absorption)
@@ -5,14 +5,15 @@
5
5
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
6
  #
7
7
  require 'activefacts/vocabulary'
8
- require 'activefacts/generate/ordered'
8
+ require 'activefacts/registry'
9
+ require 'activefacts/generate/helpers/ordered'
9
10
 
10
11
  module ActiveFacts
11
12
  module Generate #:nodoc:
12
13
  # Generate CQL for an ActiveFacts vocabulary.
13
14
  # Invoke as
14
15
  # afgen --cql <file>.cql
15
- class CQL < OrderedDumper
16
+ class CQL < Helpers::OrderedDumper
16
17
  private
17
18
  def vocabulary_start(vocabulary)
18
19
  puts "vocabulary #{vocabulary.name};\n\n"
@@ -26,35 +27,7 @@ module ActiveFacts
26
27
  end
27
28
 
28
29
  def unit_dump unit
29
- if !unit.ephemera_url
30
- if unit.coefficient
31
- # REVISIT: Use a smarter algorithm to switch to exponential form when there'd be lots of zeroes.
32
- print unit.coefficient.numerator.to_s('F')
33
- if d = unit.coefficient.denominator and d != 1
34
- print "/#{d}"
35
- end
36
- print ' '
37
- else
38
- print '1 '
39
- end
40
- end
41
-
42
- print(unit.
43
- all_derivation_as_derived_unit.
44
- # REVISIT: Sort base units
45
- # REVISIT: convert negative powers to division?
46
- map do |der|
47
- base = der.base_unit
48
- "#{base.name}#{der.exponent and der.exponent != 1 ? "^#{der.exponent}" : ''} "
49
- end*''
50
- )
51
- if o = unit.offset and o != 0
52
- print "+ #{o.to_s('F')} "
53
- end
54
- print "converts to #{unit.name}#{unit.plural_name ? '/'+unit.plural_name : ''}"
55
- print " approximately" if unit.coefficient and !unit.coefficient.is_precise
56
- print " ephemera #{unit.ephemera_url}" if unit.ephemera_url
57
- puts ";"
30
+ puts unit.as_cql
58
31
  end
59
32
 
60
33
  def units_end
@@ -76,7 +49,7 @@ module ActiveFacts
76
49
  o.all_role.size == 0 &&
77
50
  !o.is_independent &&
78
51
  !o.value_constraint &&
79
- o.all_context_note.size == 0 &&
52
+ o.all_context_note_as_relevant_concept.size == 0 &&
80
53
  o.all_instance.size == 0
81
54
  # No need to dump it if the only thing it does is be a supertype; it'll be created automatically
82
55
  # return if o.all_value_type_as_supertype.size == 0
@@ -104,28 +77,26 @@ module ActiveFacts
104
77
  # o.all_role.size != 0
105
78
  =end
106
79
 
107
- parameters =
108
- [ o.length != 0 || o.scale != 0 ? o.length : nil,
109
- o.scale != 0 ? o.scale : nil
110
- ].compact
111
- parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : ""
80
+ puts o.as_cql
81
+ end
112
82
 
113
- puts "#{o.name
114
- } #{
115
- (o.is_independent ? '[independent] ' : '')
116
- }is written as #{
117
- (o.supertype || o).name
118
- }#{
119
- parameters
120
- }#{
121
- o.unit && " "+o.unit.name
122
- }#{
123
- o.value_constraint && " "+o.value_constraint.describe
124
- };"
83
+ def entity_type_dump(o)
84
+ @object_types_dumped[o] = true
85
+ pi = o.preferred_identifier
86
+
87
+ supers = o.supertypes
88
+ if (supers.size > 0)
89
+ # Ignore identification by a supertype:
90
+ pi = nil if pi && pi.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) }
91
+ subtype_dump(o, supers, pi)
92
+ else
93
+ non_subtype_dump(o, pi)
94
+ end
95
+ @constraints_used[pi] = true
125
96
  end
126
97
 
127
98
  def append_ring_to_reading(reading, ring)
128
- reading << " [#{(ring.ring_type.scan(/[A-Z][a-z]*/)*", ").downcase}]"
99
+ reading << " [#{(ring.ring_type.scan(/StronglyIntransitive|[A-Z][a-z]*/)*", ").downcase}]"
129
100
  end
130
101
 
131
102
  def mapping_pragma(entity_type, ignore_independence = false)
@@ -245,6 +216,9 @@ module ActiveFacts
245
216
  end
246
217
  (entity_type.is_independent ? ' independent' : '') +
247
218
  " identified by its #{value_residual}#{constraint_text}#{mapping_pragma(entity_type, true)}" +
219
+ entity_type.all_context_note_as_relevant_concept.map do |cn|
220
+ cn.verbalise
221
+ end.join("\n") +
248
222
  (fact_readings.size > 0 ? " where\n\t" : "") +
249
223
  fact_readings*",\n\t"
250
224
  end
@@ -279,6 +253,9 @@ module ActiveFacts
279
253
  (entity_type.is_independent ? ' independent' : '') +
280
254
  " identified by #{ irn*" and " }" +
281
255
  mapping_pragma(entity_type, true) +
256
+ entity_type.all_context_note_as_relevant_concept.map do |cn|
257
+ cn.verbalise
258
+ end.join("\n") +
282
259
  " where\n\t"+identifying_fact_text
283
260
  end
284
261
 
@@ -367,14 +344,14 @@ module ActiveFacts
367
344
  verbaliser = ActiveFacts::Metamodel::Verbaliser.new
368
345
  verbaliser.alternate_readings fact_type.all_reading
369
346
  pr = fact_type.preferred_reading
370
- if (pr.role_sequence.all_role_ref.to_a[0].join_role)
347
+ if (pr.role_sequence.all_role_ref.to_a[0].play)
371
348
  verbaliser.prepare_role_sequence pr.role_sequence
372
349
  end
373
350
  verbaliser.create_subscripts(:rolenames)
374
351
 
375
352
  print(fact_readings_with_constraints(verbaliser, fact_type)*",\n\t")
376
- if (pr.role_sequence.all_role_ref.to_a[0].join_role)
377
- print ":\n\t"+verbaliser.verbalise_over_role_sequence(pr.role_sequence)
353
+ if (pr.role_sequence.all_role_ref.to_a[0].play)
354
+ print " where\n\t"+verbaliser.verbalise_over_role_sequence(pr.role_sequence)
378
355
  end
379
356
  puts(';')
380
357
  end
@@ -408,13 +385,13 @@ module ActiveFacts
408
385
  # having no exact match, but having instead exactly one role of the same player in the readings.
409
386
 
410
387
  verbaliser = ActiveFacts::Metamodel::Verbaliser.new
411
- # For a mandatory constraint (min_frequency == 1, max == nil or 1) any subtyping join is over the proximate role player
412
- # For all other presence constraints any subtyping join is over the counterpart player
388
+ # For a mandatory constraint (min_frequency == 1, max == nil or 1) any subtyping step is over the proximate role player
389
+ # For all other presence constraints any subtyping step is over the counterpart player
413
390
  role_proximity = c.min_frequency == 1 && [nil, 1].include?(c.max_frequency) ? :proximate : :counterpart
414
391
  if role_proximity == :proximate
415
- verbaliser.role_refs_are_subtype_joined(c.role_sequence)
392
+ verbaliser.role_refs_have_subtype_steps(c.role_sequence)
416
393
  else
417
- join_over, joined_roles = ActiveFacts::Metamodel.join_roles_over(c.role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
394
+ join_over, joined_roles = ActiveFacts::Metamodel.plays_over(c.role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
418
395
  verbaliser.roles_have_same_player(joined_roles) if join_over
419
396
  end
420
397
 
@@ -433,7 +410,7 @@ module ActiveFacts
433
410
  pl = (min&&min>1)||(max&&max>1) ? 's' : ''
434
411
  puts \
435
412
  "each #{players.size > 1 ? "combination " : ""}#{players*", "} occurs #{c.frequency} time#{pl} in\n\t"+
436
- "#{expanded_readings*",\n\t"};"
413
+ "#{Array(expanded_readings)*",\n\t"};"
437
414
  end
438
415
  end
439
416
 
@@ -445,14 +422,14 @@ module ActiveFacts
445
422
 
446
423
  # Tell the verbaliser all we know, so it can figure out which players to subscript:
447
424
  players = []
448
- debug :subscript, "Preparing join across projected roles in set comparison constraint" do
425
+ debug :subscript, "Preparing query across projected roles in set comparison constraint" do
449
426
  transposed_role_refs.each do |role_refs|
450
- verbaliser.role_refs_are_subtype_joined role_refs
451
- join_over, = ActiveFacts::Metamodel.join_roles_over(role_refs.map{|rr| rr.role})
427
+ verbaliser.role_refs_have_subtype_steps role_refs
428
+ join_over, = ActiveFacts::Metamodel.plays_over(role_refs.map{|rr| rr.role})
452
429
  players << join_over
453
430
  end
454
431
  end
455
- debug :subscript, "Preparing join between roles in set comparison constraint" do
432
+ debug :subscript, "Preparing query between roles in set comparison constraint" do
456
433
  role_sequences.each do |role_sequence|
457
434
  debug :subscript, "role sequence is #{role_sequence.describe}" do
458
435
  verbaliser.prepare_role_sequence role_sequence
@@ -461,8 +438,8 @@ module ActiveFacts
461
438
  end
462
439
  verbaliser.create_subscripts :normal
463
440
 
464
- if role_sequences.detect{|scr| scr.all_role_ref.detect{|rr| rr.join_role}}
465
- # This set constraint has an explicit join. Verbalise it.
441
+ if role_sequences.detect{|scr| scr.all_role_ref.detect{|rr| rr.play}}
442
+ # This set constraint has an explicit query. Verbalise it.
466
443
 
467
444
  readings_list = role_sequences.
468
445
  map do |rs|
@@ -480,7 +457,7 @@ module ActiveFacts
480
457
  # Internal check: We must have located the players here
481
458
  if i = players.index(nil)
482
459
  rrs = transposed_role_refs[i]
483
- raise "Internal error detecting constrained object types in join involving #{rrs.map{|rr| rr.role.fact_type.default_reading}.uniq*', '}"
460
+ raise "Internal error detecting constrained object types in query involving #{rrs.map{|rr| rr.role.fact_type.default_reading}.uniq*', '}"
484
461
  end
485
462
 
486
463
  # Loose binding will apply only to the constrained roles, not to all roles. Not handled here.
@@ -499,7 +476,7 @@ module ActiveFacts
499
476
  return
500
477
  end
501
478
 
502
- # A constrained role may involve a subtyping join. We substitute the name of the supertype for all occurrences.
479
+ # A constrained role may involve a subtyping step. We substitute the name of the supertype for all occurrences.
503
480
  players = transposed_role_refs.map{|role_refs| common_supertype(role_refs.map{|rr| rr.role.object_type})}
504
481
  raise "Constraint must cover matching roles" if players.compact.size < players.size
505
482
 
@@ -507,15 +484,15 @@ module ActiveFacts
507
484
  map do |scr|
508
485
  # verbaliser.verbalise_over_role_sequence(scr.role_sequence)
509
486
  # REVISIT: verbalise_over_role_sequence cannot do what we need here, because of the
510
- # possibility of subtyping joins in the constrained roles across the different scr's
487
+ # possibility of subtyping steps in the constrained roles across the different scr's
511
488
  # The following code uses "players" and "constrained_roles" to create substitutions.
512
- # These should instead be passed to the verbaliser (one join node per index, role_refs for each).
489
+ # These should instead be passed to the verbaliser (one variable per index, role_refs for each).
513
490
  fact_types_processed = {}
514
491
  constrained_roles = scr.role_sequence.all_role_ref_in_order.map{|rr| rr.role}
515
- join_over, joined_roles = *Metamodel.join_roles_over(constrained_roles)
492
+ join_over, joined_roles = *Metamodel.plays_over(constrained_roles)
516
493
  constrained_roles.map do |constrained_role|
517
494
  fact_type = constrained_role.fact_type
518
- next nil if fact_types_processed[fact_type] # Don't emit the same fact type twice (in case of objectification join)
495
+ next nil if fact_types_processed[fact_type] # Don't emit the same fact type twice (in case of objectification step)
519
496
  fact_types_processed[fact_type] = true
520
497
  reading = fact_type.reading_preferably_starting_with_role(constrained_role)
521
498
  expand_constrained(verbaliser, reading, constrained_roles, players)
@@ -540,7 +517,7 @@ module ActiveFacts
540
517
  transposed_role_refs = [c.subset_role_sequence, c.superset_role_sequence].map{|rs| rs.all_role_ref_in_order.to_a}.transpose
541
518
 
542
519
  verbaliser = ActiveFacts::Metamodel::Verbaliser.new
543
- transposed_role_refs.each { |role_refs| verbaliser.role_refs_are_subtype_joined role_refs }
520
+ transposed_role_refs.each { |role_refs| verbaliser.role_refs_have_subtype_steps role_refs }
544
521
  verbaliser.prepare_role_sequence c.subset_role_sequence
545
522
  verbaliser.prepare_role_sequence c.superset_role_sequence
546
523
  verbaliser.create_subscripts :normal
@@ -558,18 +535,18 @@ module ActiveFacts
558
535
  end
559
536
 
560
537
  def constraint_dump(c)
561
- case c
562
- when ActiveFacts::Metamodel::PresenceConstraint
563
- dump_presence_constraint(c)
564
- when ActiveFacts::Metamodel::RingConstraint
565
- dump_ring_constraint(c)
566
- when ActiveFacts::Metamodel::SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
567
- dump_set_comparison_constraint(c)
568
- when ActiveFacts::Metamodel::SubsetConstraint
569
- dump_subset_constraint(c)
570
- else
571
- "#{c.class.basename} #{c.name}: unhandled constraint type"
572
- end
538
+ case c
539
+ when ActiveFacts::Metamodel::PresenceConstraint
540
+ dump_presence_constraint(c)
541
+ when ActiveFacts::Metamodel::RingConstraint
542
+ dump_ring_constraint(c)
543
+ when ActiveFacts::Metamodel::SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
544
+ dump_set_comparison_constraint(c)
545
+ when ActiveFacts::Metamodel::SubsetConstraint
546
+ dump_subset_constraint(c)
547
+ else
548
+ "#{c.class.basename} #{c.name}: unhandled constraint type"
549
+ end
573
550
  end
574
551
 
575
552
  # Find the common supertype of these object_types.
@@ -630,6 +607,7 @@ module ActiveFacts
630
607
  end
631
608
 
632
609
  expanded = verbaliser.expand_reading(reading, frequency_constraints, define_role_names, value_constraints)
610
+ expanded = "it is not the case that "+expanded if (reading.is_negative)
633
611
 
634
612
  if (ft_rings = @ring_constraints_by_fact[reading.fact_type]) &&
635
613
  (ring = ft_rings.detect{|rc| !@constraints_used[rc]})
@@ -651,9 +629,87 @@ module ActiveFacts
651
629
  end
652
630
  frequency_constraints = [] unless frequency_constraints.detect{|fc| fc[0] != "some" }
653
631
 
654
- verbaliser.expand_reading(reading, frequency_constraints)
632
+ expanded = verbaliser.expand_reading(reading, frequency_constraints)
633
+ expanded = "it is not the case that "+expanded if (reading.is_negative)
634
+ expanded
655
635
  end
656
636
 
657
637
  end
658
638
  end
639
+
640
+ module Metamodel
641
+ class ValueType
642
+ def as_cql
643
+ parameters =
644
+ [ length != 0 || scale != 0 ? length : nil,
645
+ scale != 0 ? scale : nil
646
+ ].compact
647
+ parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : ""
648
+
649
+ "#{name
650
+ } #{
651
+ (is_independent ? '[independent] ' : '')
652
+ }is written as #{
653
+ (supertype || self).name
654
+ }#{
655
+ parameters
656
+ }#{
657
+ unit && " "+unit.name
658
+ }#{
659
+ all_context_note_as_relevant_concept.map do |cn|
660
+ cn.verbalise
661
+ end.join("\n")
662
+ }#{
663
+ value_constraint && " "+value_constraint.describe
664
+ };"
665
+ end
666
+ end
667
+
668
+ class Unit
669
+ def as_cql
670
+ if !ephemera_url
671
+ if coefficient
672
+ # REVISIT: Use a smarter algorithm to switch to exponential form when there'd be lots of zeroes.
673
+ coefficient.numerator.to_s('F') +
674
+
675
+ if d = coefficient.denominator and d != 1
676
+ "/#{d}"
677
+ else
678
+ ''
679
+ end +
680
+
681
+ ' '
682
+ else
683
+ '1 '
684
+ end
685
+ else
686
+ ''
687
+ end +
688
+
689
+ all_derivation_as_derived_unit.
690
+ # REVISIT: Sort base units
691
+ # REVISIT: convert negative powers to division?
692
+ map do |der|
693
+ base = der.base_unit
694
+ "#{base.name}#{der.exponent and der.exponent != 1 ? "^#{der.exponent}" : ''} "
695
+ end*'' +
696
+
697
+ if o = offset and o != 0
698
+ "+ #{o.to_s('F')} "
699
+ else
700
+ ''
701
+ end +
702
+
703
+ "converts to #{name}#{plural_name ? '/'+plural_name : ''}" +
704
+
705
+ (coefficient && !coefficient.is_precise ? ' approximately' : '') +
706
+
707
+ (ephemera_url ? " ephemera #{ephemera_url}" : '') +
708
+
709
+ ';'
710
+ end
711
+ end
712
+ end
659
713
  end
714
+
715
+ ActiveFacts::Registry.generator('cql', ActiveFacts::Generate::CQL)