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
data/script/txt2html ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ load File.dirname(__FILE__) + "/../Rakefile"
4
+ require 'rubyforge'
5
+ require 'redcloth'
6
+ require 'syntax/convertors/html'
7
+ require 'erb'
8
+
9
+ download = "http://rubyforge.org/projects/#{$hoe.rubyforge_name}"
10
+ version = $hoe.version
11
+
12
+ def rubyforge_project_id
13
+ RubyForge.new.configure.autoconfig["group_ids"][$hoe.rubyforge_name]
14
+ end
15
+
16
+ class Fixnum
17
+ def ordinal
18
+ # teens
19
+ return 'th' if (10..19).include?(self % 100)
20
+ # others
21
+ case self % 10
22
+ when 1: return 'st'
23
+ when 2: return 'nd'
24
+ when 3: return 'rd'
25
+ else return 'th'
26
+ end
27
+ end
28
+ end
29
+
30
+ class Time
31
+ def pretty
32
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
33
+ end
34
+ end
35
+
36
+ def convert_syntax(syntax, source)
37
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
38
+ end
39
+
40
+ if ARGV.length >= 1
41
+ src, template = ARGV
42
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.html.erb')
43
+ else
44
+ puts("Usage: #{File.split($0).last} source.txt [template.html.erb] > output.html")
45
+ exit!
46
+ end
47
+
48
+ template = ERB.new(File.open(template).read)
49
+
50
+ title = nil
51
+ body = nil
52
+ File.open(src) do |fsrc|
53
+ title_text = fsrc.readline
54
+ body_text_template = fsrc.read
55
+ body_text = ERB.new(body_text_template).result(binding)
56
+ syntax_items = []
57
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
58
+ ident = syntax_items.length
59
+ element, syntax, source = $1, $2, $3
60
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
61
+ "syntax-temp-#{ident}"
62
+ }
63
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
64
+ body = RedCloth.new(body_text).to_html
65
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
66
+ end
67
+ stat = File.stat(src)
68
+ created = stat.ctime
69
+ modified = stat.mtime
70
+
71
+ $stdout << template.result(binding)
@@ -0,0 +1,95 @@
1
+ #
2
+ # ActiveFacts tests: Test the relational absorption by compiling CQL fragments.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'rubygems'
6
+ require 'treetop'
7
+ require 'activefacts/support'
8
+ require 'activefacts/api/support'
9
+ require 'activefacts/input/cql'
10
+ require 'activefacts/persistence'
11
+
12
+ describe "Absorption" do
13
+ Prologue = %Q{
14
+ vocabulary Test;
15
+ DateTime is defined as DateAndTime();
16
+ Month is defined as VariableLengthText(3);
17
+ Season is defined as VariableLengthText(6);
18
+ PartyID is defined as AutoCounter();
19
+ ClaimID is defined as AutoCounter();
20
+ }
21
+ Claim = %Q{
22
+ Claim is identified by ClaimID where
23
+ Claim has exactly one ClaimID,
24
+ ClaimID is of at most one Claim;
25
+ }
26
+ Incident = %Q{
27
+ Incident is identified by Claim where
28
+ Claim concerns at most one Incident,
29
+ Incident is of exactly one Claim;
30
+ }
31
+ Party = %Q{
32
+ Party is identified by PartyID where
33
+ Party has exactly one PartyID,
34
+ PartyID is of at most one Party;
35
+ }
36
+ Person = %Q{
37
+ Person is a kind of Party;
38
+ }
39
+
40
+ Tests = [
41
+ { :should => "inject a value column into the table for an independent ValueType",
42
+ :cql => %Q{
43
+ #{Prologue}
44
+ Month is in exactly one Season;
45
+ },
46
+ :tables => { "Month" => [ %w{Month Value}, "Season" ] }
47
+ },
48
+
49
+ { :should => "absorb a one-to-one along the identification path",
50
+ :cql => %Q{
51
+ #{Prologue} #{Claim} #{Incident}
52
+ Incident relates to loss on exactly one DateTime;
53
+ },
54
+ :tables => { "Claim" => ["ClaimID", %w{Incident DateTime}]}
55
+ },
56
+
57
+ { :should => "absorb an objectified binary with single-role UC",
58
+ :cql => %Q{
59
+ #{Prologue} #{Claim} #{Party} #{Person}
60
+ Lodgement is where
61
+ Claim was lodged by at most one Person;
62
+ Lodgement was made at at most one DateTime;
63
+ Person has exactly one birth-Date;
64
+ },
65
+ :tables => {
66
+ "Claim" => ["ClaimID", %w{Lodgement DateTime}, %w{Lodgement Person ID}],
67
+ "Party" => ["PartyID", %w{Person birth Date}]
68
+ }
69
+ },
70
+
71
+ ]
72
+
73
+ setup do
74
+ end
75
+
76
+ Tests.each do |test|
77
+ should = test[:should]
78
+ cql = test[:cql]
79
+ expected_tables = test[:tables]
80
+ it "should #{should}" do
81
+ @compiler = ActiveFacts::Input::CQL.new(cql, should)
82
+ @vocabulary = @compiler.read
83
+
84
+ # Ensure that the same tables were generated:
85
+ tables = @vocabulary.tables
86
+ tables.map(&:name).sort.should == expected_tables.keys.sort
87
+
88
+ # Ensure that the same column descriptions were generated:
89
+ tables.sort_by(&:name).each do |table|
90
+ column_descriptions = table.absorbed_roles.all_role_ref.map{|rr| rr.column_name(nil) }.sort
91
+ column_descriptions.should == expected_tables[table.name].map{|c| Array(c) }.sort
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,82 @@
1
+ #
2
+ # ActiveFacts tests: Value instances in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ describe "AutoCounter Value Type instances" do
6
+ setup do
7
+ Object.send :remove_const, :Mod if Object.const_defined?("Mod")
8
+ module Mod
9
+ class ThingId < AutoCounter
10
+ value_type
11
+ end
12
+ class Thing
13
+ identified_by :thing_id
14
+ has_one :thing_id
15
+ end
16
+ class Ordinal < Int
17
+ value_type
18
+ end
19
+ class ThingFacet
20
+ identified_by :thing, :ordinal
21
+ has_one :thing
22
+ has_one :ordinal
23
+ end
24
+ end
25
+ @constellation = ActiveFacts::API::Constellation.new(Mod)
26
+ @thing = Mod::Thing.new(:new)
27
+ @thing_id = Mod::ThingId.new
28
+ end
29
+
30
+ it "should respond to verbalise" do
31
+ @thing_id.respond_to?(:verbalise).should be_true
32
+ end
33
+
34
+ it "should verbalise correctly" do
35
+ @thing_id.verbalise.should =~ /ThingId 'new_[0-9]+'/
36
+ end
37
+
38
+ it "should respond to constellation" do
39
+ @thing_id.respond_to?(:constellation).should be_true
40
+ end
41
+
42
+ it "should respond to its roles" do
43
+ @thing_id.respond_to?(:all_thing).should be_true
44
+ end
45
+
46
+ it "should allow prevent invalid role assignment" do
47
+ lambda {
48
+ @thing.thing_id = "foo"
49
+ }.should raise_error
50
+ end
51
+
52
+ it "should allow its roles to be assigned" do
53
+ lambda {
54
+ @thing.thing_id = @thing_id
55
+ }.should_not raise_error
56
+ end
57
+
58
+ it "should allow an existing counter to be re-used" do
59
+ @new_thing = Mod::Thing.new(@thing_id)
60
+ @new_thing.thing_id.should == @thing_id
61
+ end
62
+
63
+ it "should return the ValueType in response to .class()" do
64
+ @thing_id.class.vocabulary.should == Mod
65
+ end
66
+
67
+ it "should not allow a counter to be cloned" do
68
+ lambda {
69
+ @thing_id.clone
70
+ }.should raise_error
71
+ end
72
+
73
+ it "should allow an existing counter-identified object to be re-used" do
74
+ thing = @constellation.Thing(:new)
75
+ facets = []
76
+ facets << @constellation.ThingFacet(thing, 0)
77
+ facets << @constellation.ThingFacet(thing, 1)
78
+ facets[0].thing.object_id.should == facets[1].thing.object_id
79
+ facets[0].thing.thing_id.object_id.should == facets[1].thing.thing_id.object_id
80
+ end
81
+
82
+ end
@@ -0,0 +1,130 @@
1
+ #
2
+ # ActiveFacts tests: Constellation instances in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require "ruby-debug"
6
+
7
+ require 'activefacts/api'
8
+
9
+ describe "A Constellation instance" do
10
+ setup do
11
+ Object.send :remove_const, :Mod if Object.const_defined?("Mod")
12
+ module Mod
13
+ @base_types = [
14
+ Int, Real, AutoCounter, String, Date, DateTime
15
+ ]
16
+
17
+ # Create a value type and a subtype of that value type for each base type:
18
+ @base_types.each do |base_type|
19
+ eval %Q{
20
+ class #{base_type.name}Value < #{base_type.name}
21
+ value_type
22
+ end
23
+
24
+ class #{base_type.name}SubValue < #{base_type.name}Value
25
+ # Note no new "value_type" is required here, it comes through inheritance
26
+ end
27
+ }
28
+ end
29
+
30
+ class Name < StringValue
31
+ value_type
32
+ #has_one :attr, Name
33
+ end
34
+
35
+ class LegalEntity
36
+ identified_by :name
37
+ has_one :name
38
+ end
39
+
40
+ class SurrogateId
41
+ identified_by :auto_counter_value
42
+ has_one :auto_counter_value
43
+ end
44
+
45
+ class Company < LegalEntity
46
+ supertypes SurrogateId
47
+ end
48
+
49
+ class Person < LegalEntity
50
+ identified_by :name, :family_name # REVISIT: want a way to role_alias :name, :given_name
51
+ supertypes SurrogateId
52
+
53
+ has_one :family_name, :Name
54
+ end
55
+ end
56
+ @constellation = ActiveFacts::API::Constellation.new(Mod)
57
+ end
58
+
59
+ it "should support fetching its vocabulary" do
60
+ @constellation.vocabulary.should == Mod
61
+ end
62
+
63
+ # it "should support fetching its query" do
64
+ # pending
65
+ # @constellation.query.should == Mod
66
+ # end
67
+
68
+ it "should support methods to construct instances of any concept" do
69
+ name = foo = acme = fred_fly = nil
70
+ lambda {
71
+ name = @constellation.Name("foo")
72
+ foo = @constellation.LegalEntity("foo")
73
+ acme = @constellation.Company("Acme, Inc")
74
+ fred_fly = @constellation.Person("fred", "fly")
75
+ }.should_not raise_error
76
+ name.class.should == Mod::Name
77
+ name.constellation.should == @constellation
78
+ foo.class.should == Mod::LegalEntity
79
+ foo.constellation.should == @constellation
80
+ acme.class.should == Mod::Company
81
+ acme.constellation.should == @constellation
82
+ fred_fly.class.should == Mod::Person
83
+ fred_fly.constellation.should == @constellation
84
+ end
85
+
86
+ it "should re-use instances constructed the same way" do
87
+ name1 = @constellation.Name("foo")
88
+ foo1 = @constellation.LegalEntity("foo")
89
+ acme1 = @constellation.Company("Acme, Inc")
90
+ fred_fly1 = @constellation.Person("fred", "fly")
91
+
92
+ name2 = @constellation.Name("foo")
93
+ foo2 = @constellation.LegalEntity("foo")
94
+ acme2 = @constellation.Company("Acme, Inc")
95
+ fred_fly2 = @constellation.Person("fred", "fly")
96
+
97
+ name1.object_id.should == name2.object_id
98
+ foo1.object_id.should == foo2.object_id
99
+ acme1.object_id.should == acme2.object_id
100
+ fred_fly1.object_id.should == fred_fly2.object_id
101
+ end
102
+
103
+ it "should index value instances, including by its superclasses" do
104
+ baz = @constellation.Name("baz")
105
+ @constellation.Name.keys.sort.should == ["baz"]
106
+
107
+ @constellation.StringValue.keys.sort.should == ["baz"]
108
+ end
109
+
110
+ it "should index entity instances, including by its superclass and secondary supertypes" do
111
+ name = "Acme, Inc"
112
+ fred = "Fred"
113
+ fly = "Fly"
114
+ acme = @constellation.Company name, :auto_counter_value => :new
115
+ fred_fly = @constellation.Person fred, fly, :auto_counter_value => :new
116
+
117
+ # REVISIT: This should be illegal:
118
+ #fred_fly.auto_counter_value = :new
119
+
120
+ @constellation.Person.keys.sort.should == [[fred, fly]]
121
+ @constellation.Company.keys.sort.should == [[name]]
122
+
123
+ @constellation.LegalEntity.keys.sort.should be_include([name])
124
+ @constellation.LegalEntity.keys.sort.should be_include([fred])
125
+
126
+ @constellation.SurrogateId.values.should be_include(acme)
127
+ @constellation.SurrogateId.values.should be_include(fred_fly)
128
+ end
129
+
130
+ end
@@ -0,0 +1,101 @@
1
+ #
2
+ # ActiveFacts tests: Entity classes in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ describe "Entity Type class definitions" do
6
+ setup do
7
+ Object.send :remove_const, :Mod if Object.const_defined?("Mod")
8
+ module Mod
9
+ class Name < String
10
+ value_type
11
+ end
12
+ class LegalEntity
13
+ end
14
+ class Person < LegalEntity
15
+ identified_by :name
16
+ has_one :name, Name
17
+ end
18
+ end
19
+ end
20
+
21
+ it "should respond_to verbalise" do
22
+ Mod::Person.respond_to?(:verbalise).should be_true
23
+ end
24
+
25
+ it "should not pollute the superclass" do
26
+ Mod::LegalEntity.respond_to?(:verbalise).should_not be_true
27
+ Class.respond_to?(:verbalise).should_not be_true
28
+ end
29
+
30
+ it "should return a string from verbalise" do
31
+ v = Mod::Person.verbalise
32
+ v.should_not be_nil
33
+ v.should_not =~ /REVISIT/
34
+ end
35
+
36
+ it "should respond_to vocabulary" do
37
+ Mod::Person.respond_to?(:vocabulary).should be_true
38
+ end
39
+
40
+ it "should return the parent module as the vocabulary" do
41
+ vocabulary = Mod::Person.vocabulary
42
+ vocabulary.should == Mod
43
+ end
44
+
45
+ it "should return a vocabulary that knows about this concept" do
46
+ vocabulary = Mod::Person.vocabulary
47
+ vocabulary.respond_to?(:concept).should be_true
48
+ vocabulary.concept.has_key?("Person").should be_true
49
+ end
50
+
51
+ it "should respond to roles()" do
52
+ Mod::Person.respond_to?(:roles).should be_true
53
+ end
54
+
55
+ it "should contain only the added role definition" do
56
+ Mod::Person.roles.size.should == 1
57
+ end
58
+
59
+ it "should return the role definition" do
60
+ # Check the role definition may be accessed by passing an index:
61
+ Mod::Person.roles(0).should be_nil
62
+
63
+ role = Mod::Person.roles(:name)
64
+ role.should_not be_nil
65
+
66
+ role = Mod::Person.roles("name")
67
+ role.should_not be_nil
68
+
69
+ # Check the role definition may be accessed by indexing the returned hash:
70
+ role = Mod::Person.roles[:name]
71
+ role.should_not be_nil
72
+
73
+ # Check the role definition array by .include?
74
+ Mod::Person.roles.include?(:name).should be_true
75
+ end
76
+
77
+ it "should fail on a ValueClass" do
78
+ lambda{
79
+ class SomeClass < String
80
+ identified_by
81
+ end
82
+ }.should raise_error
83
+ end
84
+
85
+ it "should return the identifying roles" do
86
+ Mod::Person.identifying_roles.should == [:name]
87
+ end
88
+
89
+ it "should prevent a role name from matching a concept that exists unless that concept is the player" do
90
+ lambda {
91
+ module Mod
92
+ class LegalEntity
93
+ end
94
+ class Bad
95
+ identified_by :name
96
+ has_one :name, LegalEntity
97
+ end
98
+ end
99
+ }.should raise_error
100
+ end
101
+ end