activefacts 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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