activefacts 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +0 -3
- data/Rakefile +7 -5
- data/bin/afgen +5 -2
- data/bin/cql +3 -2
- data/examples/CQL/Genealogy.cql +3 -3
- data/examples/CQL/Metamodel.cql +10 -7
- data/examples/CQL/MultiInheritance.cql +2 -1
- data/examples/CQL/OilSupply.cql +4 -4
- data/examples/CQL/Orienteering.cql +2 -2
- data/lib/activefacts.rb +2 -1
- data/lib/activefacts/api.rb +21 -2
- data/lib/activefacts/api/concept.rb +52 -39
- data/lib/activefacts/api/constellation.rb +8 -6
- data/lib/activefacts/api/entity.rb +41 -37
- data/lib/activefacts/api/instance.rb +5 -3
- data/lib/activefacts/api/numeric.rb +28 -21
- data/lib/activefacts/api/role.rb +29 -43
- data/lib/activefacts/api/standard_types.rb +8 -3
- data/lib/activefacts/api/support.rb +4 -4
- data/lib/activefacts/api/value.rb +9 -3
- data/lib/activefacts/api/vocabulary.rb +17 -7
- data/lib/activefacts/cql.rb +10 -7
- data/lib/activefacts/cql/CQLParser.treetop +6 -0
- data/lib/activefacts/cql/Concepts.treetop +32 -26
- data/lib/activefacts/cql/DataTypes.treetop +6 -0
- data/lib/activefacts/cql/Expressions.treetop +6 -0
- data/lib/activefacts/cql/FactTypes.treetop +6 -0
- data/lib/activefacts/cql/Language/English.treetop +9 -3
- data/lib/activefacts/cql/LexicalRules.treetop +6 -0
- data/lib/activefacts/cql/Rakefile +8 -0
- data/lib/activefacts/cql/parser.rb +4 -2
- data/lib/activefacts/generate/absorption.rb +20 -28
- data/lib/activefacts/generate/cql.rb +28 -16
- data/lib/activefacts/generate/cql/html.rb +327 -321
- data/lib/activefacts/generate/null.rb +7 -3
- data/lib/activefacts/generate/oo.rb +19 -15
- data/lib/activefacts/generate/ordered.rb +457 -461
- data/lib/activefacts/generate/ruby.rb +12 -4
- data/lib/activefacts/generate/sql/server.rb +42 -10
- data/lib/activefacts/generate/text.rb +7 -3
- data/lib/activefacts/input/cql.rb +55 -28
- data/lib/activefacts/input/orm.rb +32 -22
- data/lib/activefacts/persistence.rb +5 -0
- data/lib/activefacts/persistence/columns.rb +66 -32
- data/lib/activefacts/persistence/foreignkey.rb +29 -5
- data/lib/activefacts/persistence/index.rb +57 -25
- data/lib/activefacts/persistence/reference.rb +65 -30
- data/lib/activefacts/persistence/tables.rb +28 -17
- data/lib/activefacts/support.rb +8 -0
- data/lib/activefacts/version.rb +7 -1
- data/lib/activefacts/vocabulary.rb +4 -2
- data/lib/activefacts/vocabulary/extensions.rb +12 -10
- data/lib/activefacts/vocabulary/metamodel.rb +24 -23
- data/spec/api/autocounter.rb +2 -2
- data/spec/api/entity_type.rb +2 -2
- data/spec/api/instance.rb +61 -30
- data/spec/api/roles.rb +9 -9
- data/spec/cql_parse_spec.rb +1 -0
- data/spec/norma_tables_spec.rb +3 -3
- metadata +8 -4
@@ -1,8 +1,11 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# Standard types extensions.
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
# These extensions add ActiveFacts Concept and Instance behaviour into base Ruby value classes,
|
8
|
+
# and allow any Class to become an Entity.
|
6
9
|
#
|
7
10
|
require 'date'
|
8
11
|
|
@@ -37,6 +40,8 @@ class NilClass #:nodoc:
|
|
37
40
|
end
|
38
41
|
|
39
42
|
class Class
|
43
|
+
# Make this Class into a Concept and if necessary its module into a Vocabulary.
|
44
|
+
# The parameters are the names (Symbols) of the identifying roles.
|
40
45
|
def identified_by *args
|
41
46
|
raise "not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
|
42
47
|
include ActiveFacts::API::Entity
|
@@ -1,8 +1,8 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts Runtime API.
|
3
|
+
# Various additions or patches to Ruby built-in classes, and some global support methods
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
6
|
#
|
7
7
|
|
8
8
|
class Symbol #:nodoc:
|
@@ -21,7 +21,7 @@ class String #:nodoc:
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def snakecase
|
24
|
-
gsub(/([
|
24
|
+
gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# Value module (mixins for ValueType classes and instances)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
6
|
#
|
5
7
|
# The methods of this module are added to Value type classes.
|
6
8
|
#
|
@@ -13,6 +15,7 @@ module ActiveFacts
|
|
13
15
|
|
14
16
|
# Value instance methods:
|
15
17
|
def initialize(*args) #:nodoc:
|
18
|
+
args[0] = args[0].__getobj__ if RoleProxy === args[0]
|
16
19
|
super(args)
|
17
20
|
end
|
18
21
|
|
@@ -59,7 +62,10 @@ module ActiveFacts
|
|
59
62
|
def identifying_role_values(*args) #:nodoc:
|
60
63
|
# If the single arg is the correct class or a subclass, use it directly
|
61
64
|
#puts "#{basename}.identifying_role_values#{args.inspect}"
|
62
|
-
|
65
|
+
if (args.size == 1 and (arg = args[0]).is_a?(self.class)) # No secondary supertypes allowed for value types
|
66
|
+
arg = arg.__getobj__ if RoleProxy === arg
|
67
|
+
return arg
|
68
|
+
end
|
63
69
|
new(*args)
|
64
70
|
end
|
65
71
|
|
@@ -1,15 +1,21 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# Vocabulary module (mixin for any Module that contains classes having Concept mixed in)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
6
|
#
|
5
7
|
# The methods of this module are extended into any module that contains
|
6
8
|
# a Concept class (Entity type or Value type).
|
7
9
|
#
|
8
10
|
module ActiveFacts
|
9
11
|
module API
|
12
|
+
# Vocabulary is a mixin that adds methods to any Module which has any Concept classes (ValueType or EntityType).
|
13
|
+
# A Vocabulary knows all the Concept classes including forward-referenced ones,
|
14
|
+
# and can resolve the forward references when the class is finally defined.
|
15
|
+
# Construction of a Constellation requires a Vocabuary as argument.
|
10
16
|
module Vocabulary
|
11
17
|
# With a parameter, look up a concept class by name.
|
12
|
-
# Without, return the hash of all concepts in this vocabulary
|
18
|
+
# Without, return the hash (keyed by the class' basename) of all concepts in this vocabulary
|
13
19
|
def concept(name = nil)
|
14
20
|
@concept ||= {}
|
15
21
|
return @concept unless name
|
@@ -61,6 +67,7 @@ module ActiveFacts
|
|
61
67
|
}*"\n\t"
|
62
68
|
end
|
63
69
|
|
70
|
+
=begin
|
64
71
|
# Create or find an instance of klass in constellation using value to identify it
|
65
72
|
def adopt(klass, constellation, value) #:nodoc:
|
66
73
|
puts "Adopting #{ value.verbalise rescue value.class.to_s+' '+value.inspect} as #{klass} into constellation #{constellation.object_id}"
|
@@ -68,20 +75,21 @@ module ActiveFacts
|
|
68
75
|
path = "unknown"
|
69
76
|
# Create a value instance we can hack if the value isn't already in this constellation
|
70
77
|
if (c = constellation)
|
71
|
-
if klass
|
78
|
+
if value.is_a?(klass) # Right class?
|
79
|
+
value = value.__getobj__ if RoleProxy === value
|
72
80
|
vc = value.constellation rescue nil
|
73
81
|
if (c == vc) # Right constellation?
|
74
82
|
# Already right class, in the right constellation
|
75
83
|
path = "right constellation, right class, just use it"
|
76
84
|
else
|
77
85
|
# We need a new object from our constellation, so copy the value.
|
78
|
-
if klass.respond_to?(:
|
86
|
+
if klass.respond_to?(:identifying_role_names)
|
79
87
|
# Make a new entity having only the identifying roles set.
|
80
88
|
# Someone will complain that this is wrong, and all functional role values should also
|
81
89
|
# be cloned, and I'm listening... but not there yet. Why just those?
|
82
90
|
cloned = c.send(
|
83
91
|
:"#{klass.basename}",
|
84
|
-
*klass.
|
92
|
+
*klass.identifying_role_names.map{|role| value.send(role) }
|
85
93
|
)
|
86
94
|
path = "wrong constellation, right class, cloned entity"
|
87
95
|
else
|
@@ -99,7 +107,8 @@ module ActiveFacts
|
|
99
107
|
end
|
100
108
|
else
|
101
109
|
# This object's not in a constellation
|
102
|
-
if klass
|
110
|
+
if value.is_a?(klass) # Right class?
|
111
|
+
value = value.__getobj__ if RoleProxy === value
|
103
112
|
if vc = value.constellation rescue nil
|
104
113
|
raise "REVISIT: Assigning to #{self.class.basename}.#{role_name} with constellation=#{c.inspect}: Can't dis-associate object from its constellation #{vc.object_id} yet"
|
105
114
|
end
|
@@ -114,6 +123,7 @@ module ActiveFacts
|
|
114
123
|
# print "#{path}"; puts ", adopted as #{value.verbalise rescue value.inspect}"
|
115
124
|
value
|
116
125
|
end
|
126
|
+
=end
|
117
127
|
|
118
128
|
end
|
119
129
|
end
|
data/lib/activefacts/cql.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts CQL loader.
|
3
|
+
# Use Polyglot to patch things so you can *require* a CQL file and have it define a Ruby module.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
6
|
#
|
5
7
|
require 'rubygems'
|
6
8
|
require 'polyglot'
|
@@ -9,12 +11,13 @@ require 'activefacts/input/cql'
|
|
9
11
|
require 'activefacts/generate/ruby'
|
10
12
|
|
11
13
|
module ActiveFacts
|
12
|
-
#
|
14
|
+
# This class has a load method for Polyglot to tell it how to _require_ a CQL file.
|
15
|
+
# The CQL file is parsed to a vocabulary constellation, which is generated
|
16
|
+
# to Ruby code and eval'd, making the generated classes available.
|
17
|
+
# To make this Loader available, simply
|
18
|
+
# require 'activefacts/cql'
|
13
19
|
class CQLLoader
|
14
|
-
|
15
|
-
# The CQL file is parsed to a vocabulary constellation, which is generated
|
16
|
-
# to Ruby code and eval'd, making the generated classes available.
|
17
|
-
def self.load(file)
|
20
|
+
def self.load(file) #:nodoc:
|
18
21
|
debug "Loading #{file}" do
|
19
22
|
vocabulary = ActiveFacts::Input::CQL.readfile(file)
|
20
23
|
|
@@ -1,3 +1,9 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Parse rules relating to Concept definitions.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
1
7
|
module ActiveFacts
|
2
8
|
module CQL
|
3
9
|
grammar Concepts
|
@@ -22,8 +28,8 @@ module ActiveFacts
|
|
22
28
|
|
23
29
|
rule basetype
|
24
30
|
is s
|
25
|
-
|
26
|
-
|
31
|
+
# independency?
|
32
|
+
identification
|
27
33
|
{ def supers; [] end
|
28
34
|
def identifier; identification.value; end
|
29
35
|
}
|
@@ -31,8 +37,8 @@ module ActiveFacts
|
|
31
37
|
|
32
38
|
rule subtype
|
33
39
|
subtype_prefix
|
34
|
-
|
35
|
-
|
40
|
+
# independency?
|
41
|
+
supertype_list ident:identification?
|
36
42
|
{
|
37
43
|
def supers; supertype_list.value; end
|
38
44
|
def identifier; ident.empty? ? nil : ident.value; end
|
@@ -41,7 +47,7 @@ module ActiveFacts
|
|
41
47
|
|
42
48
|
# REVISIT: This doesn't work, and I don't know why.
|
43
49
|
rule independency
|
44
|
-
|
50
|
+
('independent' S / 'separate' S / 'partitioned' S)
|
45
51
|
end
|
46
52
|
|
47
53
|
rule supertype_list
|
@@ -54,12 +60,12 @@ module ActiveFacts
|
|
54
60
|
end
|
55
61
|
|
56
62
|
rule identification
|
57
|
-
|
58
|
-
identified_by its s id s
|
59
|
-
|
60
|
-
|
63
|
+
# REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-"
|
64
|
+
identified_by its s id s # Reference Mode
|
65
|
+
{ def value; {:mode => id.text_value }; end }
|
66
|
+
/
|
61
67
|
identified_by role_list
|
62
|
-
|
68
|
+
{ def value; {:roles => role_list.roles }; end }
|
63
69
|
end
|
64
70
|
|
65
71
|
# Identified by roles... also used for constraints, beware
|
@@ -75,24 +81,24 @@ module ActiveFacts
|
|
75
81
|
# We can't tell which word is an adjective and which is a concept here.
|
76
82
|
# The concept might be forward-referenced, but not if adjectives are used.
|
77
83
|
# REVISIT: This accepts double-adjective expressions (three words) but they don't work elsewhere
|
78
|
-
|
79
|
-
|
80
|
-
|
84
|
+
rule lead_adj
|
85
|
+
role_player_id '-'
|
86
|
+
end
|
81
87
|
|
82
|
-
|
83
|
-
|
84
|
-
|
88
|
+
rule trail_adj
|
89
|
+
'-' role_player_id
|
90
|
+
end
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
rule role_player
|
93
|
+
l:lead_adj? tail:(s role_player_id)+ s t:trail_adj?
|
94
|
+
{
|
95
|
+
def value
|
96
|
+
(l.empty? ? [] : [l.role_player_id.text_value]) +
|
97
|
+
tail.elements.map{|e| e.role_player_id.text_value } +
|
98
|
+
(t.empty? ? [] : [t.role_player_id.text_value])
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
96
102
|
|
97
103
|
rule role_player_id
|
98
104
|
!(role_list_sep / quantifier) id
|
@@ -1,3 +1,9 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Parse rules the English syntax of CQL.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
1
7
|
module ActiveFacts
|
2
8
|
module CQL
|
3
9
|
grammar Language
|
@@ -17,9 +23,9 @@ module ActiveFacts
|
|
17
23
|
rule quantifier
|
18
24
|
each s { def value; [1, nil]; end }
|
19
25
|
/ some s { def value; nil; end }
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
# REVISIT: "Some" means that any prior occurrence of this role player is to be ignored; this is a new occurrence
|
27
|
+
# "that" on the other hand means that this role player was *previously* designated using "some".
|
28
|
+
# These distinctions are lost here
|
23
29
|
/ that s { def value; nil; end }
|
24
30
|
/ one s { def value; [1, 1]; end }
|
25
31
|
/ no s { def value; [0, 0]; end }
|
@@ -1,3 +1,11 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# A Rakefile to run Treetop when the ActiveFacts gem is installed.
|
4
|
+
# This isn't mandatory but makes it much faster to start the parser.
|
5
|
+
# Delete the generated files during parser development.
|
6
|
+
#
|
7
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
8
|
+
#
|
1
9
|
task :default do
|
2
10
|
pattern = File.dirname(__FILE__) + '**/*.treetop'
|
3
11
|
files = Dir[pattern]
|
@@ -1,6 +1,8 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# The parser turns CQL strings into abstract syntax trees ready for semantic analysis.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
6
|
#
|
5
7
|
require 'rubygems'
|
6
8
|
require 'treetop'
|
@@ -1,17 +1,27 @@
|
|
1
1
|
#
|
2
|
-
#
|
2
|
+
# ActiveFacts Generators.
|
3
|
+
# Absorption generator.
|
3
4
|
#
|
4
|
-
# Copyright (c)
|
5
|
-
# Author: Clifford Heath.
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
6
|
#
|
7
7
|
require 'activefacts/persistence'
|
8
8
|
|
9
9
|
module ActiveFacts
|
10
10
|
module Generate
|
11
|
+
# Emit the absorption (Relational summary) for vocabulary.
|
12
|
+
# Not currently working, it relies on the old relational composition code.
|
13
|
+
# Invoke as
|
14
|
+
# afgen --absorption[=options] <file>.cql"
|
15
|
+
# Options are comma or space separated:
|
16
|
+
# * no_columns Don't emit the columns
|
17
|
+
# * dependent Show Concepts that are not tables as well
|
18
|
+
# * paths Show the references paths through which each column was defined
|
19
|
+
# * no_identifier Don't show the identified_by columns for an EntityType
|
20
|
+
|
11
21
|
class ABSORPTION
|
12
22
|
include Metamodel
|
13
23
|
|
14
|
-
def initialize(vocabulary, *options)
|
24
|
+
def initialize(vocabulary, *options) #:nodoc:
|
15
25
|
@vocabulary = vocabulary
|
16
26
|
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
17
27
|
@no_columns = options.include? "no_columns"
|
@@ -20,7 +30,7 @@ module ActiveFacts
|
|
20
30
|
@no_identifier = options.include? "no_identifier"
|
21
31
|
end
|
22
32
|
|
23
|
-
def generate(out = $>)
|
33
|
+
def generate(out = $>) #:nodoc:
|
24
34
|
no_absorption = 0
|
25
35
|
single_absorption_vts = 0
|
26
36
|
single_absorption_ets = 0
|
@@ -31,42 +41,24 @@ module ActiveFacts
|
|
31
41
|
# Don't dump imported (base) ValueTypes:
|
32
42
|
next if ValueType === o && !o.supertype
|
33
43
|
show(o)
|
34
|
-
|
35
|
-
case o.absorption_paths.size
|
36
|
-
when 0; no_absorption += 1
|
37
|
-
when 1
|
38
|
-
if ValueType === o
|
39
|
-
single_absorption_vts += 1
|
40
|
-
else
|
41
|
-
single_absorption_ets += 1
|
42
|
-
end
|
43
|
-
else
|
44
|
-
if ValueType === o
|
45
|
-
multi_absorption_vts += 1
|
46
|
-
else
|
47
|
-
multi_absorption_ets += 1
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
44
|
end
|
52
|
-
puts "#{no_absorption} concepts have no absorption paths, #{single_absorption_vts}/#{single_absorption_ets} value/entity types have only one path, #{multi_absorption_vts}/#{multi_absorption_ets} have more than one"
|
53
45
|
end
|
54
46
|
|
55
|
-
def show concept
|
56
|
-
return unless concept.
|
47
|
+
def show concept #:nodoc:
|
48
|
+
return unless concept.is_table || @dependent
|
57
49
|
|
58
50
|
print "#{concept.name}"
|
59
|
-
print " (#{concept.tentative ? "tentatively " : ""}#{concept.
|
51
|
+
print " (#{concept.tentative ? "tentatively " : ""}#{concept.is_table ? "in" : ""}dependent)" if @dependent
|
60
52
|
|
61
53
|
if !@no_identifier && concept.is_a?(EntityType)
|
62
54
|
print " is identified by:\n\t#{
|
63
|
-
concept.
|
55
|
+
"REVISIT" # concept.references_from.to_s
|
64
56
|
}"
|
65
57
|
end
|
66
58
|
print "\n"
|
67
59
|
|
68
60
|
unless @no_columns
|
69
|
-
puts "#{ concept.
|
61
|
+
puts "#{ concept.references_from.map do |role_ref|
|
70
62
|
"\t#{role_ref.column_name(".")}\n"
|
71
63
|
end*"" }"
|
72
64
|
end
|