activefacts-generators 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +30 -0
- data/Rakefile +6 -0
- data/activefacts-generators.gemspec +26 -0
- data/lib/activefacts/dependency_analyser.rb +182 -0
- data/lib/activefacts/generators/absorption.rb +71 -0
- data/lib/activefacts/generators/composition.rb +119 -0
- data/lib/activefacts/generators/cql.rb +715 -0
- data/lib/activefacts/generators/diagrams/json.rb +340 -0
- data/lib/activefacts/generators/help.rb +64 -0
- data/lib/activefacts/generators/helpers/inject.rb +16 -0
- data/lib/activefacts/generators/helpers/oo.rb +162 -0
- data/lib/activefacts/generators/helpers/ordered.rb +605 -0
- data/lib/activefacts/generators/helpers/rails.rb +57 -0
- data/lib/activefacts/generators/html/glossary.rb +462 -0
- data/lib/activefacts/generators/metadata/json.rb +204 -0
- data/lib/activefacts/generators/null.rb +32 -0
- data/lib/activefacts/generators/rails/models.rb +247 -0
- data/lib/activefacts/generators/rails/schema.rb +217 -0
- data/lib/activefacts/generators/ruby.rb +134 -0
- data/lib/activefacts/generators/sql/mysql.rb +281 -0
- data/lib/activefacts/generators/sql/server.rb +274 -0
- data/lib/activefacts/generators/stats.rb +70 -0
- data/lib/activefacts/generators/text.rb +29 -0
- data/lib/activefacts/generators/traits/datavault.rb +241 -0
- data/lib/activefacts/generators/traits/oo.rb +73 -0
- data/lib/activefacts/generators/traits/ordered.rb +33 -0
- data/lib/activefacts/generators/traits/ruby.rb +210 -0
- data/lib/activefacts/generators/transform/datavault.rb +303 -0
- data/lib/activefacts/generators/transform/surrogate.rb +215 -0
- data/lib/activefacts/registry.rb +11 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1e81fd6c874b5767f07dcb7d2f508f1be29c693
|
4
|
+
data.tar.gz: 497cfd7d1abc379b424acdb3e61b4c2b19de4589
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59851db95c25eba970ebb3da980838a2394a0de66d1fc8a24c004e002defbf87641f627e9be3e70f4abfa23e40ae02ce52cfe266f80cca47c2e40d1289cc1e99
|
7
|
+
data.tar.gz: f08d307b0a39c6dab0a6b267da237d04a45b3d820163da87fe90e9fe168971e128b232285dff8cd7e3664a17850d029203f33827194e7d598fad6ca5c7884527
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--format documentation
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in activefacts-generators.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
if ENV['PWD'] =~ %r{\A/Users/cjh/work/activefacts}
|
7
|
+
gem 'activefacts-api', path: '/Users/cjh/work/activefacts/api'
|
8
|
+
gem 'activefacts-metamodel', path: '/Users/cjh/work/activefacts/metamodel'
|
9
|
+
# gem 'activefacts-metamodel', git: 'git://github.com/cjheath/activefacts-metamodel.git'
|
10
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Clifford Heath
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Activefacts::Generators
|
2
|
+
|
3
|
+
Code generators for the ActiveFacts fact modeling suite, including the Constellation Query Language
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'activefacts-generators'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
After installation, the generators appear as options in the ActiveFacts generator <i>afgen</i>. Use
|
20
|
+
|
21
|
+
$ afgen --help
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/cjheath/activefacts-generators.
|
26
|
+
|
27
|
+
## License
|
28
|
+
|
29
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
30
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "activefacts-generators"
|
7
|
+
spec.version = "1.7.1"
|
8
|
+
spec.authors = ["Clifford Heath"]
|
9
|
+
spec.email = ["clifford.heath@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Code Generators for the ActiveFacts suite}
|
12
|
+
spec.description = %q{Code generators for the ActiveFacts Fact Modeling suite, including the Constellation Query Language}
|
13
|
+
spec.homepage = "http://github.com/cjheath/activefacts-generators"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.10.a"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
spec.add_development_dependency "rspec"
|
22
|
+
|
23
|
+
spec.add_runtime_dependency(%q<activefacts-metamodel>, [">= 1.7.0", "~> 1.7"])
|
24
|
+
spec.add_runtime_dependency(%q<activefacts-rmap>, [">= 1.7.0", "~> 1.7"])
|
25
|
+
spec.add_runtime_dependency(%q<activesupport>)
|
26
|
+
end
|
@@ -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,71 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Generators.
|
3
|
+
# Absorption generator.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
require 'activefacts/metamodel'
|
8
|
+
require 'activefacts/rmap'
|
9
|
+
require 'activefacts/registry'
|
10
|
+
|
11
|
+
module ActiveFacts
|
12
|
+
module Generators
|
13
|
+
# Emit the absorption (Relational summary) for vocabulary.
|
14
|
+
# Not currently working, it relies on the old relational composition code.
|
15
|
+
# Invoke as
|
16
|
+
# afgen --absorption[=options] <file>.cql"
|
17
|
+
# Options are comma or space separated:
|
18
|
+
# * no_columns Don't emit the columns
|
19
|
+
# * all Show ObjectTypes that are not tables as well
|
20
|
+
# * paths Show the references paths through which each column was defined
|
21
|
+
# * no_identifier Don't show the identified_by columns for an EntityType
|
22
|
+
|
23
|
+
class Absorption
|
24
|
+
def initialize(vocabulary, *options) #:nodoc:
|
25
|
+
@vocabulary = vocabulary
|
26
|
+
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
27
|
+
@no_columns = options.include? "no_columns"
|
28
|
+
@paths = options.include? "paths"
|
29
|
+
@no_identifier = options.include? "no_identifier"
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate(out = $>) #:nodoc:
|
33
|
+
@out = out
|
34
|
+
no_absorption = 0
|
35
|
+
single_absorption_vts = 0
|
36
|
+
single_absorption_ets = 0
|
37
|
+
multi_absorption_vts = 0
|
38
|
+
multi_absorption_ets = 0
|
39
|
+
@vocabulary.tables
|
40
|
+
@vocabulary.all_object_type.sort_by{|c| c.name}.each do |o|
|
41
|
+
next if !o.is_table
|
42
|
+
show(o)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def show object_type #:nodoc:
|
47
|
+
indices = object_type.indices
|
48
|
+
pk = indices.select(&:is_primary)[0]
|
49
|
+
indices = indices.clone
|
50
|
+
indices.delete pk
|
51
|
+
@out.puts "#{object_type.name}: #{
|
52
|
+
# "[#{object_type.indices.size} indices] "
|
53
|
+
# } #{
|
54
|
+
object_type.columns.sort_by do |column|
|
55
|
+
column.name(nil)
|
56
|
+
end.map do |column|
|
57
|
+
index_nrs =
|
58
|
+
[pk && pk.columns.include?(column) ? "*" : nil] +
|
59
|
+
(0...indices.size).select{|i| indices[i].columns.include?(column)}.map{|i| (i+1).to_i }
|
60
|
+
index_nrs.compact!
|
61
|
+
(@paths ? column.references.map{|r| r.to_names}.flatten : column.name(nil)) * '.' +
|
62
|
+
(index_nrs.empty? ? "" : "["+index_nrs*""+"]")
|
63
|
+
end*", "
|
64
|
+
}"
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
ActiveFacts::Registry.generator('absorption', ActiveFacts::Generators::Absorption)
|
@@ -0,0 +1,119 @@
|
|
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/metamodel'
|
8
|
+
require 'activefacts/generators/helpers/inject'
|
9
|
+
require 'activefacts/rmap'
|
10
|
+
require 'activefacts/generators/traits/ruby'
|
11
|
+
require 'activefacts/registry'
|
12
|
+
|
13
|
+
module ActiveFacts
|
14
|
+
module Generators
|
15
|
+
# afgen --composition[=options] <file>.cql
|
16
|
+
# Options are comma or space separated:
|
17
|
+
class Composition #:nodoc:
|
18
|
+
private
|
19
|
+
include RMap
|
20
|
+
|
21
|
+
def initialize(vocabulary, *options)
|
22
|
+
@vocabulary = vocabulary
|
23
|
+
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
24
|
+
@underscore = options.include?("underscore") ? "_" : ""
|
25
|
+
end
|
26
|
+
|
27
|
+
def puts s
|
28
|
+
@out.puts s
|
29
|
+
end
|
30
|
+
|
31
|
+
public
|
32
|
+
def generate(out = $>) #:nodoc:
|
33
|
+
@out = out
|
34
|
+
|
35
|
+
tables_emitted = {}
|
36
|
+
|
37
|
+
puts "require '#{@vocabulary.name}'"
|
38
|
+
puts "require 'activefacts/composition'"
|
39
|
+
puts "\n#{@vocabulary.name}_ER = ActiveFacts::Composition.new(#{@vocabulary.name}) do"
|
40
|
+
@vocabulary.tables.each do |table|
|
41
|
+
puts " composite :\"#{table.name.gsub(' ',@underscore)}\" do"
|
42
|
+
|
43
|
+
pk = table.identifier_columns
|
44
|
+
identity_column = pk[0] if pk[0].is_auto_assigned
|
45
|
+
|
46
|
+
fk_refs = table.references_from.select{|ref| ref.is_simple_reference }
|
47
|
+
fk_columns = table.columns.select do |column|
|
48
|
+
column.references[0].is_simple_reference
|
49
|
+
end
|
50
|
+
|
51
|
+
columns =
|
52
|
+
table.columns.map do |column|
|
53
|
+
[column, column.references.map{|r| r.to_names }]
|
54
|
+
end.sort_by do |column, refnames|
|
55
|
+
refnames
|
56
|
+
end
|
57
|
+
previous_flattening = []
|
58
|
+
ref_prefix = []
|
59
|
+
columns.each do |column, refnames|
|
60
|
+
ref_prefix = column.references[0...previous_flattening.size]
|
61
|
+
# Pop back. Not a succinct algorithm, but easy to check
|
62
|
+
while previous_flattening.size > ref_prefix.size
|
63
|
+
previous_flattening.pop
|
64
|
+
puts ' '+' '*previous_flattening.size+"end\n"
|
65
|
+
end
|
66
|
+
while ref_prefix.size > 0 and previous_flattening != ref_prefix
|
67
|
+
previous_flattening.pop
|
68
|
+
ref_prefix.pop
|
69
|
+
puts ' '+' '*previous_flattening.size+"end\n"
|
70
|
+
end
|
71
|
+
loop do
|
72
|
+
ref = column.references[ref_prefix.size]
|
73
|
+
if ref.is_self_value
|
74
|
+
# REVISIT: I think these should be 'insert :value, :as => "XYZ"'
|
75
|
+
role_name = "value".snakecase
|
76
|
+
reading = "Intrinsic value of #{role_name}"
|
77
|
+
elsif ref.is_to_objectified_fact
|
78
|
+
# REVISIT: It's ugly to have to handle these special cases here
|
79
|
+
role_name = ref.to.name.words.snakecase
|
80
|
+
reading = ref.from_role.link_fact_type.default_reading
|
81
|
+
else
|
82
|
+
if ref.is_unary && ref.is_from_objectified_fact && ref != column.references.last
|
83
|
+
# Use the name of the objectification on the path to other absorbed fact types:
|
84
|
+
role_name = ref.to_role.fact_type.entity_type.name.words.snakecase
|
85
|
+
else
|
86
|
+
role_name = ref.to_role.preferred_role_name
|
87
|
+
end
|
88
|
+
# puts ">>>>> #{ref.inspect}: #{role_name} <<<<<<"
|
89
|
+
reading = ref.fact_type.default_reading
|
90
|
+
end
|
91
|
+
if ref == column.references.last
|
92
|
+
# REVISIT: Avoid the "as" here when the value is implied by the role_name:
|
93
|
+
puts ' '+' '*ref_prefix.size+"nest :#{role_name}, :as => \"#{column.name}\"\t\t# #{reading}"
|
94
|
+
break
|
95
|
+
else
|
96
|
+
puts ' '+' '*ref_prefix.size+"flatten :#{role_name} do\t\t# #{reading}"
|
97
|
+
ref_prefix.push ref
|
98
|
+
end
|
99
|
+
end
|
100
|
+
previous_flattening = ref_prefix
|
101
|
+
end
|
102
|
+
|
103
|
+
while previous_flattening.size > 0
|
104
|
+
previous_flattening.pop
|
105
|
+
puts ' '+' '*previous_flattening.size+"end\n"
|
106
|
+
end
|
107
|
+
puts " end\n\n"
|
108
|
+
|
109
|
+
tables_emitted[table] = true
|
110
|
+
|
111
|
+
end
|
112
|
+
puts "end\n"
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
ActiveFacts::Registry.generator('composition', ActiveFacts::Generators::Composition)
|