activefacts 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +83 -0
  3. data/README.rdoc +81 -0
  4. data/Rakefile +41 -0
  5. data/bin/afgen +46 -0
  6. data/bin/cql +52 -0
  7. data/examples/CQL/Address.cql +46 -0
  8. data/examples/CQL/Blog.cql +54 -0
  9. data/examples/CQL/CompanyDirectorEmployee.cql +51 -0
  10. data/examples/CQL/Death.cql +16 -0
  11. data/examples/CQL/Genealogy.cql +95 -0
  12. data/examples/CQL/Marriage.cql +18 -0
  13. data/examples/CQL/Metamodel.cql +238 -0
  14. data/examples/CQL/MultiInheritance.cql +19 -0
  15. data/examples/CQL/OilSupply.cql +47 -0
  16. data/examples/CQL/Orienteering.cql +108 -0
  17. data/examples/CQL/PersonPlaysGame.cql +17 -0
  18. data/examples/CQL/SchoolActivities.cql +31 -0
  19. data/examples/CQL/SimplestUnary.cql +12 -0
  20. data/examples/CQL/SubtypePI.cql +32 -0
  21. data/examples/CQL/Warehousing.cql +99 -0
  22. data/examples/CQL/WindowInRoomInBldg.cql +22 -0
  23. data/lib/activefacts.rb +10 -0
  24. data/lib/activefacts/api.rb +25 -0
  25. data/lib/activefacts/api/concept.rb +384 -0
  26. data/lib/activefacts/api/constellation.rb +106 -0
  27. data/lib/activefacts/api/entity.rb +239 -0
  28. data/lib/activefacts/api/instance.rb +54 -0
  29. data/lib/activefacts/api/numeric.rb +158 -0
  30. data/lib/activefacts/api/role.rb +94 -0
  31. data/lib/activefacts/api/standard_types.rb +67 -0
  32. data/lib/activefacts/api/support.rb +59 -0
  33. data/lib/activefacts/api/value.rb +122 -0
  34. data/lib/activefacts/api/vocabulary.rb +120 -0
  35. data/lib/activefacts/cql.rb +31 -0
  36. data/lib/activefacts/cql/CQLParser.treetop +104 -0
  37. data/lib/activefacts/cql/Concepts.treetop +112 -0
  38. data/lib/activefacts/cql/DataTypes.treetop +66 -0
  39. data/lib/activefacts/cql/Expressions.treetop +113 -0
  40. data/lib/activefacts/cql/FactTypes.treetop +185 -0
  41. data/lib/activefacts/cql/Language/English.treetop +92 -0
  42. data/lib/activefacts/cql/LexicalRules.treetop +169 -0
  43. data/lib/activefacts/cql/Rakefile +6 -0
  44. data/lib/activefacts/cql/parser.rb +88 -0
  45. data/lib/activefacts/generate/absorption.rb +87 -0
  46. data/lib/activefacts/generate/cql.rb +441 -0
  47. data/lib/activefacts/generate/cql/html.rb +397 -0
  48. data/lib/activefacts/generate/null.rb +19 -0
  49. data/lib/activefacts/generate/ordered.rb +557 -0
  50. data/lib/activefacts/generate/ruby.rb +326 -0
  51. data/lib/activefacts/generate/sql/server.rb +164 -0
  52. data/lib/activefacts/generate/text.rb +21 -0
  53. data/lib/activefacts/input/cql.rb +1268 -0
  54. data/lib/activefacts/input/orm.rb +926 -0
  55. data/lib/activefacts/persistence.rb +1 -0
  56. data/lib/activefacts/persistence/composition.rb +653 -0
  57. data/lib/activefacts/support.rb +51 -0
  58. data/lib/activefacts/version.rb +3 -0
  59. data/lib/activefacts/vocabulary.rb +6 -0
  60. data/lib/activefacts/vocabulary/extensions.rb +343 -0
  61. data/lib/activefacts/vocabulary/metamodel.rb +303 -0
  62. data/script/txt2html +71 -0
  63. data/spec/absorption_spec.rb +95 -0
  64. data/spec/api/autocounter.rb +82 -0
  65. data/spec/api/constellation.rb +130 -0
  66. data/spec/api/entity_type.rb +101 -0
  67. data/spec/api/instance.rb +428 -0
  68. data/spec/api/roles.rb +122 -0
  69. data/spec/api/value_type.rb +112 -0
  70. data/spec/api_spec.rb +14 -0
  71. data/spec/cql_cql_spec.rb +58 -0
  72. data/spec/cql_parse_spec.rb +31 -0
  73. data/spec/cql_ruby_spec.rb +60 -0
  74. data/spec/cql_sql_spec.rb +54 -0
  75. data/spec/cql_symbol_tables_spec.rb +259 -0
  76. data/spec/cql_unit_spec.rb +336 -0
  77. data/spec/cqldump_spec.rb +169 -0
  78. data/spec/norma_cql_spec.rb +48 -0
  79. data/spec/norma_ruby_spec.rb +50 -0
  80. data/spec/norma_sql_spec.rb +45 -0
  81. data/spec/norma_tables_spec.rb +94 -0
  82. data/spec/spec.opts +1 -0
  83. data/spec/spec_helper.rb +10 -0
  84. metadata +173 -0
@@ -0,0 +1,48 @@
1
+ #
2
+ # ActiveFacts tests: Parse all NORMA files and check the generated CQL.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'rubygems'
6
+ require 'stringio'
7
+ require 'activefacts/vocabulary'
8
+ require 'activefacts/support'
9
+ require 'activefacts/input/orm'
10
+ require 'activefacts/generate/cql'
11
+
12
+ include ActiveFacts
13
+
14
+ describe "Norma Loader" do
15
+ ORM_CQL_FAILURES = %w{
16
+ ServiceDirector
17
+ }
18
+ # Generate and return the CQL for the given vocabulary
19
+ def cql(vocabulary)
20
+ output = StringIO.new
21
+ @dumper = ActiveFacts::Generate::CQL.new(vocabulary.constellation)
22
+ @dumper.generate(output)
23
+ output.rewind
24
+ output.readlines
25
+ end
26
+
27
+ #Dir["examples/norma/Bl*.orm"].each do |orm_file|
28
+ #Dir["examples/norma/Meta*.orm"].each do |orm_file|
29
+ #Dir["examples/norma/[AC]*.orm"].each do |orm_file|
30
+ Dir["examples/norma/*.orm"].each do |orm_file|
31
+ expected_file = orm_file.sub(%r{/norma/(.*).orm\Z}, '/CQL/\1.cql')
32
+
33
+ actual_file = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'spec/actual/\1.cql')
34
+
35
+ it "should load #{orm_file} and dump CQL matching #{expected_file}" do
36
+ pending if ORM_CQL_FAILURES.include? File.basename(orm_file, ".orm")
37
+ vocabulary = ActiveFacts::Input::ORM.readfile(orm_file)
38
+
39
+ cql = cql(vocabulary)
40
+ # Save the actual file:
41
+ File.open(actual_file, "w") { |f| f.write cql*"" }
42
+
43
+ pending unless File.exists? expected_file
44
+ cql.should == File.open(expected_file) {|f| f.readlines}
45
+ File.delete(actual_file)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,50 @@
1
+ #
2
+ # ActiveFacts tests: Parse all CQL files and check the generated Ruby.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'rubygems'
6
+ require 'stringio'
7
+ require 'activefacts/vocabulary'
8
+ require 'activefacts/support'
9
+ require 'activefacts/input/orm'
10
+ require 'activefacts/generate/ruby'
11
+
12
+ include ActiveFacts
13
+
14
+ class String
15
+ def strip_comments()
16
+ c_comment = %r{/\*((?!\*/).)*\*/}m
17
+ gsub(c_comment, '').gsub(%r{\n\n+},"\n")
18
+ end
19
+ end
20
+
21
+ describe "NORMA Loader with Ruby output" do
22
+ # Generate and return the Ruby for the given vocabulary
23
+ def ruby(vocabulary)
24
+ output = StringIO.new
25
+ @dumper = ActiveFacts::Generate::RUBY.new(vocabulary.constellation)
26
+ @dumper.generate(output)
27
+ output.rewind
28
+ output.read
29
+ end
30
+
31
+ #Dir["examples/norma/Bl*.orm"].each do |orm_file|
32
+ #Dir["examples/norma/Meta*.orm"].each do |orm_file|
33
+ #Dir["examples/norma/[ACG]*.orm"].each do |orm_file|
34
+ Dir["examples/norma/*.orm"].each do |orm_file|
35
+ expected_file = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'examples/ruby/\1.rb')
36
+ actual_file = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'spec/actual/\1.rb')
37
+
38
+ it "should load #{orm_file} and dump Ruby matching #{expected_file}" do
39
+ vocabulary = ActiveFacts::Input::ORM.readfile(orm_file)
40
+
41
+ # Build and save the actual file:
42
+ ruby_text = ruby(vocabulary)
43
+ File.open(actual_file, "w") { |f| f.write ruby_text }
44
+
45
+ pending unless File.exists? expected_file
46
+ ruby_text.should == File.open(expected_file) {|f| f.read }
47
+ File.delete(actual_file) # It succeeded, we don't need the file.
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ #
2
+ # ActiveFacts tests: Parse all CQL files and check the generated Ruby.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'rubygems'
6
+ require 'stringio'
7
+ require 'activefacts/vocabulary'
8
+ require 'activefacts/support'
9
+ require 'activefacts/persistence'
10
+ require 'activefacts/input/orm'
11
+ require 'activefacts/generate/sql/server'
12
+
13
+ include ActiveFacts
14
+ include ActiveFacts::Metamodel
15
+
16
+ describe "NORMA Loader with SQL output" do
17
+ # Generate and return the SQL for the given vocabulary
18
+ def sql(vocabulary)
19
+ output = StringIO.new
20
+ @dumper = ActiveFacts::Generate::SQL::SERVER.new(vocabulary.constellation)
21
+ @dumper.generate(output)
22
+ output.rewind
23
+ output.read
24
+ end
25
+
26
+ #Dir["examples/norma/Bl*.orm"].each do |orm_file|
27
+ #Dir["examples/norma/Meta*.orm"].each do |orm_file|
28
+ #Dir["examples/norma/[ACG]*.orm"].each do |orm_file|
29
+ Dir["examples/norma/*.orm"].each do |orm_file|
30
+ expected_file = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'examples/SQL/\1.sql')
31
+ actual_file = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'spec/actual/\1.sql')
32
+
33
+ it "should load #{orm_file} and dump SQL matching #{expected_file}" do
34
+ vocabulary = ActiveFacts::Input::ORM.readfile(orm_file)
35
+
36
+ # Build and save the actual file:
37
+ sql_text = sql(vocabulary)
38
+ File.open(actual_file, "w") { |f| f.write sql_text }
39
+
40
+ pending unless File.exists? expected_file
41
+ sql_text.should == File.open(expected_file) {|f| f.read }
42
+ File.delete(actual_file) # It succeeded, we don't need the file.
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,94 @@
1
+ #
2
+ # ActiveFacts test:
3
+ #
4
+ # Parse all NORMA files, compute the composition (list of tables)
5
+ # and compare that with NORMA's output.
6
+ #
7
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
8
+ #
9
+ require 'rubygems'
10
+ require 'stringio'
11
+ require 'activefacts/vocabulary'
12
+ require 'activefacts/persistence'
13
+ require 'activefacts/support'
14
+ require 'activefacts/input/orm'
15
+
16
+ include ActiveFacts
17
+ include ActiveFacts::Metamodel
18
+
19
+ Exceptions = {
20
+ "Blog" => ["Author", "Comment", "Paragraph", "Post", "Topic"],
21
+ "DeathAsBinary" => ["Person"],
22
+ "Metamodel" => ["AllowedRange", "Constraint", "Correspondence", "Fact", "FactType", "Feature", "Instance", "JoinPath", "Reading", "Role", "RoleRef", "RoleSequence", "RoleValue", "SetComparisonRoles", "Unit", "UnitBasis", "ValueRestriction"],
23
+ "OilSupplyWithCosts" => ["AcceptableSubstitutes", "Month", "ProductionForecast", "RegionalDemand", "TransportRoute"],
24
+ "Orienteering" => ["Club", "Entry", "Event", "EventControl", "EventScoringMethod", "Map", "Person", "Punch", "PunchPlacement", "Series", "Visit"],
25
+ "Warehousing" => ["Bin", "DirectOrderMatch", "DispatchItem", "Party", "Product", "PurchaseOrder", "PurchaseOrderItem", "ReceivedItem", "SalesOrder", "SalesOrderItem", "TransferRequest", "Warehouse"]
26
+ }
27
+
28
+ describe "Relational Composition from NORMA" do
29
+ #Dir["examples/norma/B*.orm"].each do |orm_file|
30
+ #Dir["examples/norma/Ins*.orm"].each do |orm_file|
31
+ #Dir["examples/norma/W*.orm"].each do |orm_file|
32
+ Dir["examples/norma/*.orm"].each do |orm_file|
33
+ sql_tables = Exceptions[File.basename(orm_file, ".orm")]
34
+ if !sql_tables
35
+ sql_file_pattern = orm_file.sub(/\.orm\Z/, '*.sql')
36
+ sql_files = Dir[sql_file_pattern]
37
+ next unless sql_files.size > 0
38
+ end
39
+
40
+ it "should load #{orm_file} and compute #{
41
+ sql_tables ? "the expected list of tables" :
42
+ "a list of tables similar to those in #{sql_files[0]}"
43
+ }" do
44
+
45
+ vocabulary = ActiveFacts::Input::ORM.readfile(orm_file)
46
+
47
+ # Get the list of tables from NORMA's SQL:
48
+ sql_tables ||= File.open(sql_files[0]) do |f|
49
+ f.
50
+ readlines.
51
+ select do |l|
52
+ l =~ /CREATE TABLE/
53
+ end.
54
+ map do |l|
55
+ l.chomp.gsub(/.*CREATE TABLE\s+\W*(\w+\.)?"?(\w+)"?.*/, '\2')
56
+ end.
57
+ sort
58
+ end
59
+
60
+ # Get the list of tables from our composition:
61
+ composition = vocabulary.tables.map{|o| o.name }.sort
62
+
63
+ # Save the actual and expected composition to files
64
+ actual_tables = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'spec/actual/\1.tables')
65
+ File.open(actual_tables, "w") { |f| f.puts composition*"\n" }
66
+ norma_tables = orm_file.sub(%r{examples/norma/(.*).orm\Z}, 'spec/actual/\1.norma.tables')
67
+ File.open(norma_tables, "w") { |f| f.puts sql_tables*"\n" }
68
+
69
+ # Calculate the columns and column names; REVISIT: check the results
70
+ vocabulary.tables.each {|table| table.absorbed_roles }
71
+
72
+ if false && composition != sql_tables
73
+ #puts "="*20 + " reasons " + "="*20
74
+ # Show only the reasons for the differences:
75
+ #((composition+sql_tables).uniq-(composition&sql_tables)).
76
+ # Show the reasons for all entity types:
77
+ vocabulary.
78
+ all_feature.
79
+ select{|f| EntityType === f || f.independent }.
80
+ map{|f| f.name}.
81
+ sort.
82
+ each do |concept_name|
83
+ concept = vocabulary.constellation.Feature(concept_name, vocabulary)
84
+ puts "#{concept_name}:\n\t#{concept.dependency_reasons*"\n\t"}"
85
+ end
86
+ end
87
+
88
+ composition.should == sql_tables
89
+
90
+ File.delete(actual_tables)
91
+ File.delete(norma_tables)
92
+ end
93
+ end
94
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'activefacts'
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activefacts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - Clifford Heath
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-28 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: treetop
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: newgem
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.1.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: hoe
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.8.0
44
+ version:
45
+ description: ActiveFacts is a semantic modeling toolkit, comprising an implementation of the Constellation Query Language, the Constellation API, and code generators that receive CQL or ORM (Object Role Modeling files, from NORMA) to emit CQL, Ruby and SQL. Semantic modeling is a refinement of fact-based modeling techniques that draw on natural language verbalisation and formal logic. Fact based modeling is essentially the same as relational modeling in the sixth normal form. The tools provided here automatically condense that to third normal form for efficient storage. They also generate object models as a Ruby module which has an effective mapping to both the original semantic model and to the generated SQL. The result is a formal language that reads like plain English, and allows creation of relational and object models that are guaranteed equivalent, and much more stable in the face of schema evolution than SQL is.
46
+ email:
47
+ - cjh@dataconstellation.org
48
+ executables:
49
+ - afgen
50
+ - cql
51
+ extensions:
52
+ - lib/activefacts/cql/Rakefile
53
+ extra_rdoc_files:
54
+ - History.txt
55
+ - Manifest.txt
56
+ - README.rdoc
57
+ files:
58
+ - History.txt
59
+ - Manifest.txt
60
+ - README.rdoc
61
+ - Rakefile
62
+ - bin/afgen
63
+ - bin/cql
64
+ - examples/CQL/Address.cql
65
+ - examples/CQL/Blog.cql
66
+ - examples/CQL/CompanyDirectorEmployee.cql
67
+ - examples/CQL/Death.cql
68
+ - examples/CQL/Genealogy.cql
69
+ - examples/CQL/Marriage.cql
70
+ - examples/CQL/Metamodel.cql
71
+ - examples/CQL/MultiInheritance.cql
72
+ - examples/CQL/OilSupply.cql
73
+ - examples/CQL/Orienteering.cql
74
+ - examples/CQL/PersonPlaysGame.cql
75
+ - examples/CQL/SchoolActivities.cql
76
+ - examples/CQL/SimplestUnary.cql
77
+ - examples/CQL/SubtypePI.cql
78
+ - examples/CQL/Warehousing.cql
79
+ - examples/CQL/WindowInRoomInBldg.cql
80
+ - lib/activefacts.rb
81
+ - lib/activefacts/api.rb
82
+ - lib/activefacts/api/concept.rb
83
+ - lib/activefacts/api/constellation.rb
84
+ - lib/activefacts/api/entity.rb
85
+ - lib/activefacts/api/instance.rb
86
+ - lib/activefacts/api/numeric.rb
87
+ - lib/activefacts/api/role.rb
88
+ - lib/activefacts/api/standard_types.rb
89
+ - lib/activefacts/api/support.rb
90
+ - lib/activefacts/api/value.rb
91
+ - lib/activefacts/api/vocabulary.rb
92
+ - lib/activefacts/cql.rb
93
+ - lib/activefacts/cql/Rakefile
94
+ - lib/activefacts/cql/CQLParser.treetop
95
+ - lib/activefacts/cql/Concepts.treetop
96
+ - lib/activefacts/cql/DataTypes.treetop
97
+ - lib/activefacts/cql/Expressions.treetop
98
+ - lib/activefacts/cql/FactTypes.treetop
99
+ - lib/activefacts/cql/Language/English.treetop
100
+ - lib/activefacts/cql/LexicalRules.treetop
101
+ - lib/activefacts/cql/parser.rb
102
+ - lib/activefacts/generate/absorption.rb
103
+ - lib/activefacts/generate/cql.rb
104
+ - lib/activefacts/generate/cql/html.rb
105
+ - lib/activefacts/generate/null.rb
106
+ - lib/activefacts/generate/ordered.rb
107
+ - lib/activefacts/generate/ruby.rb
108
+ - lib/activefacts/generate/sql/server.rb
109
+ - lib/activefacts/generate/text.rb
110
+ - lib/activefacts/input/cql.rb
111
+ - lib/activefacts/input/orm.rb
112
+ - lib/activefacts/persistence.rb
113
+ - lib/activefacts/persistence/composition.rb
114
+ - lib/activefacts/support.rb
115
+ - lib/activefacts/version.rb
116
+ - lib/activefacts/vocabulary.rb
117
+ - lib/activefacts/vocabulary/extensions.rb
118
+ - lib/activefacts/vocabulary/metamodel.rb
119
+ - script/txt2html
120
+ - spec/absorption_spec.rb
121
+ - spec/api/autocounter.rb
122
+ - spec/api/constellation.rb
123
+ - spec/api/entity_type.rb
124
+ - spec/api/instance.rb
125
+ - spec/api/roles.rb
126
+ - spec/api/value_type.rb
127
+ - spec/api_spec.rb
128
+ - spec/cql_cql_spec.rb
129
+ - spec/cql_parse_spec.rb
130
+ - spec/cql_ruby_spec.rb
131
+ - spec/cql_sql_spec.rb
132
+ - spec/cql_symbol_tables_spec.rb
133
+ - spec/cql_unit_spec.rb
134
+ - spec/cqldump_spec.rb
135
+ - spec/norma_cql_spec.rb
136
+ - spec/norma_ruby_spec.rb
137
+ - spec/norma_sql_spec.rb
138
+ - spec/norma_tables_spec.rb
139
+ - spec/spec.opts
140
+ - spec/spec_helper.rb
141
+ has_rdoc: true
142
+ homepage: http://dataconstellation.com/ActiveFacts/
143
+ post_install_message: For more information on ActiveFacts, see http://dataconstellation.com/ActiveFacts
144
+ rdoc_options:
145
+ - -x
146
+ - lib/activefacts/cql/.*.rb
147
+ - -x
148
+ - lib/activefacts/vocabulary/.*.rb
149
+ - -x
150
+ - lib/activefacts/persistence/.*.rb
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: "0"
158
+ version:
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: "0"
164
+ version:
165
+ requirements: []
166
+
167
+ rubyforge_project: cjheath@rubyforge.org
168
+ rubygems_version: 1.2.0
169
+ signing_key:
170
+ specification_version: 2
171
+ summary: ActiveFacts is a semantic modeling toolkit, comprising an implementation of the Constellation Query Language, the Constellation API, and code generators that receive CQL or ORM (Object Role Modeling files, from NORMA) to emit CQL, Ruby and SQL
172
+ test_files: []
173
+