activefacts 1.3.0 → 1.5.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: 4e9276d073a9b3444de848bf033b76f373784a51
4
- data.tar.gz: 0ab5602a9298cc84ce688168a3635bd7f4d976a8
3
+ metadata.gz: f4c7be7ca9e116d36232a6e9c0bcf28f0f4a63eb
4
+ data.tar.gz: a3c13e8e437aa028760eff29cd05df07b0017a91
5
5
  SHA512:
6
- metadata.gz: a984577de5c0aa667d5975f70b35c76f2bb4ab65555fc6a19ca0b7a8bae635cd77fb6d3fbac3959f9f9d76bc53c81846214940ea5109d3e58c6ae5b06197c6c5
7
- data.tar.gz: 169adf1025e3c1bb46fa58fd45e79d05c2eb45e4ffb069c25bec1c10b786f3dddca32c97f54556353bd48fb9580e2becc279f5a11185373857af4dff64694635
6
+ metadata.gz: 49878c205424a5534e811af19bf09ebe0e3c8aefbd3127a1ee0695d3a5c77eec2beb74715455ff32d7bde613e8a1eff8241dc35ba77a79915770e1ee68c60ba0
7
+ data.tar.gz: 4396ee06e34a48236e858274010356123f66847686a04763b4b4992283ae30f8dd6b0d96e7086803339a97f9ffbc2c158d2563a8840a288c330623f11680de8b
data/Manifest.txt CHANGED
@@ -71,10 +71,13 @@ lib/activefacts/cql/compiler/shared.rb
71
71
  lib/activefacts/cql/compiler/value_type.rb
72
72
  lib/activefacts/cql/nodes.rb
73
73
  lib/activefacts/cql/parser.rb
74
+ lib/activefacts/dependency_analyser.rb
74
75
  lib/activefacts/generate/absorption.rb
76
+ lib/activefacts/generate/composition.rb
75
77
  lib/activefacts/generate/cql.rb
76
78
  lib/activefacts/generate/dm.rb
77
79
  lib/activefacts/generate/help.rb
80
+ lib/activefacts/generate/helpers/inject.rb
78
81
  lib/activefacts/generate/helpers/oo.rb
79
82
  lib/activefacts/generate/helpers/ordered.rb
80
83
  lib/activefacts/generate/helpers/rails.rb
@@ -85,9 +88,14 @@ lib/activefacts/generate/records.rb
85
88
  lib/activefacts/generate/ruby.rb
86
89
  lib/activefacts/generate/sql/mysql.rb
87
90
  lib/activefacts/generate/sql/server.rb
91
+ lib/activefacts/generate/stats.rb
88
92
  lib/activefacts/generate/rails/schema.rb
89
93
  lib/activefacts/generate/rails/models.rb
90
94
  lib/activefacts/generate/text.rb
95
+ lib/activefacts/generate/topics.rb
96
+ lib/activefacts/generate/traits/oo.rb
97
+ lib/activefacts/generate/traits/ordered.rb
98
+ lib/activefacts/generate/traits/ruby.rb
91
99
  lib/activefacts/generate/transform/surrogate.rb
92
100
  lib/activefacts/generate/version.rb
93
101
  lib/activefacts/input/cql.rb
@@ -107,6 +115,7 @@ lib/activefacts/vocabulary.rb
107
115
  lib/activefacts/vocabulary/extensions.rb
108
116
  lib/activefacts/vocabulary/metamodel.rb
109
117
  lib/activefacts/vocabulary/verbaliser.rb
118
+ lib/activefacts/vocabulary/query_evaluator.rb
110
119
  script/txt2html
111
120
  spec/cql/comparison_spec.rb
112
121
  spec/cql/contractions_spec.rb
@@ -31,6 +31,7 @@ Rotation Setting is written as String restricted to {'left', 'right'};
31
31
  Scale is written as Unsigned Integer(32);
32
32
  Subscript is written as Unsigned Integer(16);
33
33
  Text is written as String(256);
34
+ Topic Name is written as Name;
34
35
  Transaction Phase is written as String restricted to {'assert', 'commit'};
35
36
  X is written as Signed Integer(32);
36
37
  Y is written as Signed Integer(32);
@@ -165,6 +166,10 @@ Subset Constraint is a kind of Set Constraint;
165
166
  Subset Constraint covers one subset-Role Sequence;
166
167
  Subset Constraint covers one superset-Role Sequence;
167
168
 
169
+ Topic is identified by its Name;
170
+ Concept belongs to at most one Topic,
171
+ Topic contains Concept;
172
+
168
173
  Unit is identified by Concept where
169
174
  Unit is an instance of one Concept;
170
175
  Ephemera URL provides Unit coefficient,
@@ -56,6 +56,15 @@ module ActiveFacts
56
56
  extend language_module
57
57
  end
58
58
 
59
+ # Mark any new Concepts as belonging to this topic
60
+ def topic_flood
61
+ @constellation.Concept.each do |key, concept|
62
+ next if concept.topic
63
+ trace :topic, "Colouring #{concept.describe} with #{@topic.topic_name}"
64
+ concept.topic = @topic
65
+ end
66
+ end
67
+
59
68
  def compile input
60
69
  include_language
61
70
 
@@ -75,7 +84,14 @@ module ActiveFacts
75
84
  ast.vocabulary = @vocabulary
76
85
  value = compile_definition ast
77
86
  trace :definition, "Compiled to #{value.is_a?(Array) ? value.map{|v| v.verbalise}*', ' : value.verbalise}" if value
78
- @vocabulary = value if ast.is_a?(Compiler::Vocabulary)
87
+ if value.is_a?(ActiveFacts::Metamodel::Topic)
88
+ topic_flood if @topic
89
+ @topic = value
90
+ elsif ast.is_a?(Compiler::Vocabulary)
91
+ topic_flood if @topic
92
+ @vocabulary = value
93
+ @topic = @constellation.Topic(@vocabulary.name)
94
+ end
79
95
  rescue => e
80
96
  # Augment the exception message, but preserve the backtrace
81
97
  start_line = @string.line_of(node.interval.first)
@@ -86,6 +102,7 @@ module ActiveFacts
86
102
  raise ne
87
103
  end
88
104
  end
105
+ topic_flood if @topic
89
106
  end
90
107
  raise failure_reason unless ok
91
108
  vocabulary
@@ -96,12 +113,18 @@ module ActiveFacts
96
113
  saved_block = @block
97
114
  saved_string = @string
98
115
  saved_input_length = @input_length
116
+ saved_topic = @topic
99
117
  old_filename = @filename
100
118
  @filename = File.dirname(old_filename)+'/'+file+'.cql'
101
119
 
102
120
  # REVISIT: Save and use another @vocabulary for this file?
103
121
  File.open(@filename) do |f|
104
- ok = parse_all(f.read, nil, &@block)
122
+ topic_flood if @topic
123
+ @topic = @constellation.Topic(File.basename(@filename, '.cql'))
124
+ trace :import, "Importing #{@filename} as #{@topic.topic_name}" do
125
+ ok = parse_all(f.read, nil, &@block)
126
+ end
127
+ @topic = saved_topic
105
128
  end
106
129
 
107
130
  rescue => e
@@ -47,8 +47,16 @@ module ActiveFacts
47
47
 
48
48
  class Constraint < Definition
49
49
  def initialize context_note, enforcement, clauses_lists = []
50
- if context_note.is_a?(Treetop::Runtime::SyntaxNode)
50
+ if context_note.is_a?(Treetop::Runtime::SyntaxNode) && !context_note.empty?
51
51
  context_note = context_note.empty? ? nil : context_note.ast
52
+ else
53
+ context_note = nil # Perhaps a context note got attached to one of the clauses. Steal it.
54
+ clauses_lists.detect do |clauses_list|
55
+ if c = clauses_list.last.context_note
56
+ context_note = c
57
+ clauses_list.last.context_note = nil
58
+ end
59
+ end
52
60
  end
53
61
  @context_note = context_note
54
62
  @enforcement = enforcement
@@ -323,6 +323,7 @@ module ActiveFacts
323
323
  :max_frequency => 1,
324
324
  :is_preferred_identifier => true # (prefer || !!@fact_type.entity_type)
325
325
  )
326
+ pc.concept.topic = @fact_type.concept.topic
326
327
  trace :constraint, "Made new fact type implicit PC GUID=#{pc.concept.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
327
328
  elsif pc
328
329
  trace :constraint, "Will rely on existing UC GUID=#{pc.concept.guid} #{pc.name} to be used as PI over #{rs.describe}"
@@ -116,7 +116,11 @@ module ActiveFacts
116
116
  end
117
117
 
118
118
  def compile
119
- @constellation.Vocabulary @name
119
+ if @constellation.Vocabulary.size > 0
120
+ @constellation.Topic @name
121
+ else
122
+ @constellation.Vocabulary @name
123
+ end
120
124
  end
121
125
 
122
126
  def to_s
@@ -0,0 +1,182 @@
1
+ module ActiveFacts
2
+ class DependencyAnalyser
3
+ def initialize enumerable, &block
4
+ @enumerable = enumerable
5
+ analyse_precursors &block
6
+ end
7
+
8
+ def analyse_precursors &block
9
+ @precursors = {}
10
+ @enumerable.each do |item|
11
+ @precursors[item] = block.call(item)
12
+ end
13
+ end
14
+
15
+ def analyse_precursors_transitive
16
+ all_precursors = proc do |item|
17
+ p = @precursors[item]
18
+ all =
19
+ p + p.map do |precursor|
20
+ p.include?(precursor) ? [] : all_precursors.call(precursor)
21
+ end.flatten
22
+ all.uniq
23
+ end
24
+
25
+ @precursors_transitive = {}
26
+ @enumerable.each do |item|
27
+ @precursors_transitive[item] = all_precursors.call(item)
28
+ end
29
+ end
30
+
31
+ def analyse_followers
32
+ @followers = Hash.new{|h, k| h[k] = [] }
33
+ @enumerable.each do |item|
34
+ @precursors[item].each do |precursor|
35
+ @followers[precursor] << item
36
+ end
37
+ end
38
+ end
39
+
40
+ def analyse_chasers
41
+ analyse_precursors_transitive unless @precursors_transitive
42
+ analyse_followers unless @followers
43
+
44
+ # A follower is an object with us as a precursor, that has no new precursors of its own
45
+ @chasers = {}
46
+ @enumerable.each do |item|
47
+ @chasers[item] =
48
+ @enumerable.select do |follower|
49
+ @precursors[follower].include?(item) and
50
+ (@precursors_transitive[follower] - @precursors_transitive[item] - [item]).size == 0
51
+ end
52
+ end
53
+ end
54
+
55
+ def tsort &block
56
+ analyse_precursors unless @precursors
57
+ emitted = {}
58
+ pass = 0
59
+ until emitted.size == @enumerable.size
60
+ next_items = []
61
+ blocked =
62
+ @enumerable.inject({}) do |hash, item|
63
+ next hash if emitted[item]
64
+ blockers = item.precursors.select{|precursor| !emitted[precursor]}
65
+ if blockers.size > 0
66
+ hash[item] = blockers
67
+ else
68
+ next_items << item
69
+ end
70
+ hash
71
+ end
72
+ return blocked if next_items.size == 0 # Cannot make progress
73
+ # puts "PASS #{pass += 1}"
74
+ next_items.each do |item|
75
+ block.call(item)
76
+ emitted[item] = true
77
+ end
78
+ end
79
+ nil
80
+ end
81
+
82
+ def each &b
83
+ if block_given?
84
+ @enumerable.each { |item| yield item}
85
+ else
86
+ @enumerable
87
+ end
88
+ end
89
+
90
+ def precursors item = nil, &b
91
+ analyse_precursors unless @precursors
92
+ if item
93
+ if block_given?
94
+ Array(@precursors[item]).each { |precursor| yield precursor, item }
95
+ else
96
+ Array(@precursors[item])
97
+ end
98
+ else
99
+ @enumerable.each do |item|
100
+ precursors(item, &b)
101
+ end
102
+ end
103
+ end
104
+
105
+ def precursors_transitive item, &b
106
+ analyse_precursors_transitive unless @precursors_transitive
107
+ if item
108
+ if block_given?
109
+ Array(@precursors_transitive[item]).each { |precursor| yield precursor, item }
110
+ else
111
+ Array(@precursors_transitive[item])
112
+ end
113
+ else
114
+ @enumerable.each do |item|
115
+ precursors_transitive(item, &b)
116
+ end
117
+ end
118
+ end
119
+
120
+ def followers item = nil, &b
121
+ analyse_followers unless @followers
122
+ if item
123
+ if block_given?
124
+ Array(@followers[item]).each { |follower| yield follower, item }
125
+ else
126
+ Array(@followers[item])
127
+ end
128
+ else
129
+ @enumerable.each do |item|
130
+ followers(item, &b)
131
+ end
132
+ end
133
+ end
134
+
135
+ def chasers item, &b
136
+ analyse_chasers unless @chasers
137
+ if item
138
+ if block_given?
139
+ Array(@chasers[item]).each { |follower| yield follower, item }
140
+ else
141
+ Array(@chasers[item])
142
+ end
143
+ else
144
+ @enumerable.each do |item|
145
+ follower(item, &b)
146
+ end
147
+ end
148
+ end
149
+
150
+ # Compute the page rank of the objects
151
+ # If used, the block shold return the starting weight
152
+ def page_rank damping = 0.85, &weight
153
+ weight ||= proc {|item| 1.0}
154
+
155
+ @total = 0
156
+ @rank = {}
157
+ @enumerable.each do |item|
158
+ @total +=
159
+ (@rank[item] = weight.call(item) * 1.0)
160
+ end
161
+ # Normalize:
162
+ @enumerable.each do |item|
163
+ @rank[item] /= @total
164
+ end
165
+
166
+ 50.times do |iteration|
167
+ @enumerable.each do |item|
168
+ links = (precursors(item) + followers(item)).uniq
169
+ linked_rank = links.map do |l|
170
+ onward_links = (precursors(l) + followers(l)).uniq || @enumerable.size
171
+ @rank[l] / onward_links.size
172
+ end.inject(&:+) || 0
173
+ @rank[item] = (1.0-damping) + damping*linked_rank
174
+ end
175
+ end
176
+
177
+ @rank
178
+ end
179
+
180
+ end
181
+ end
182
+
@@ -0,0 +1,118 @@
1
+ #
2
+ # ActiveFacts Generators.
3
+ # Generate a Relational Composition (for activefacts/composition).
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
7
+ require 'activefacts/vocabulary'
8
+ require 'activefacts/generate/helpers/inject'
9
+ require 'activefacts/persistence'
10
+ require 'activefacts/generate/traits/ruby'
11
+
12
+ module ActiveFacts
13
+ module Generate
14
+ # afgen --composition[=options] <file>.cql
15
+ # Options are comma or space separated:
16
+ class Composition #:nodoc:
17
+ private
18
+ include Persistence
19
+
20
+ def initialize(vocabulary, *options)
21
+ @vocabulary = vocabulary
22
+ @vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
23
+ @underscore = options.include?("underscore") ? "_" : ""
24
+ end
25
+
26
+ def puts s
27
+ @out.puts s
28
+ end
29
+
30
+ public
31
+ def generate(out = $>) #:nodoc:
32
+ @out = out
33
+
34
+ tables_emitted = {}
35
+
36
+ puts "require '#{@vocabulary.name}'"
37
+ puts "require 'activefacts/composition'"
38
+ puts "\n#{@vocabulary.name}_ER = ActiveFacts::Composition.new(#{@vocabulary.name}) do"
39
+ @vocabulary.tables.each do |table|
40
+ puts " composite :\"#{table.name.gsub(' ',@underscore)}\" do"
41
+
42
+ pk = table.identifier_columns
43
+ identity_column = pk[0] if pk[0].is_auto_assigned
44
+
45
+ fk_refs = table.references_from.select{|ref| ref.is_simple_reference }
46
+ fk_columns = table.columns.select do |column|
47
+ column.references[0].is_simple_reference
48
+ end
49
+
50
+ columns =
51
+ table.columns.map do |column|
52
+ [column, column.references.map{|r| r.to_names }]
53
+ end.sort_by do |column, refnames|
54
+ refnames
55
+ end
56
+ previous_flattening = []
57
+ ref_prefix = []
58
+ columns.each do |column, refnames|
59
+ ref_prefix = column.references[0...previous_flattening.size]
60
+ # Pop back. Not a succinct algorithm, but easy to check
61
+ while previous_flattening.size > ref_prefix.size
62
+ previous_flattening.pop
63
+ puts ' '+' '*previous_flattening.size+"end\n"
64
+ end
65
+ while ref_prefix.size > 0 and previous_flattening != ref_prefix
66
+ previous_flattening.pop
67
+ ref_prefix.pop
68
+ puts ' '+' '*previous_flattening.size+"end\n"
69
+ end
70
+ loop do
71
+ ref = column.references[ref_prefix.size]
72
+ if ref.is_self_value
73
+ # REVISIT: I think these should be 'insert :value, :as => "XYZ"'
74
+ role_name = "value".snakecase
75
+ reading = "Intrinsic value of #{role_name}"
76
+ elsif ref.is_to_objectified_fact
77
+ # REVISIT: It's ugly to have to handle these special cases here
78
+ role_name = ref.to.name.words.snakecase
79
+ reading = ref.from_role.link_fact_type.default_reading
80
+ else
81
+ if ref.is_unary && ref.is_from_objectified_fact && ref != column.references.last
82
+ # Use the name of the objectification on the path to other absorbed fact types:
83
+ role_name = ref.to_role.fact_type.entity_type.name.words.snakecase
84
+ else
85
+ role_name = ref.to_role.preferred_role_name
86
+ end
87
+ # puts ">>>>> #{ref.inspect}: #{role_name} <<<<<<"
88
+ reading = ref.fact_type.default_reading
89
+ end
90
+ if ref == column.references.last
91
+ # REVISIT: Avoid the "as" here when the value is implied by the role_name:
92
+ puts ' '+' '*ref_prefix.size+"nest :#{role_name}, :as => \"#{column.name}\"\t\t# #{reading}"
93
+ break
94
+ else
95
+ puts ' '+' '*ref_prefix.size+"flatten :#{role_name} do\t\t# #{reading}"
96
+ ref_prefix.push ref
97
+ end
98
+ end
99
+ previous_flattening = ref_prefix
100
+ end
101
+
102
+ while previous_flattening.size > 0
103
+ previous_flattening.pop
104
+ puts ' '+' '*previous_flattening.size+"end\n"
105
+ end
106
+ puts " end\n\n"
107
+
108
+ tables_emitted[table] = true
109
+
110
+ end
111
+ puts "end\n"
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+
118
+ ActiveFacts::Registry.generator('composition', ActiveFacts::Generate::Composition)