activefacts 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +4 -0
- data/examples/CQL/Metamodel.cql +11 -7
- data/lib/activefacts/api/concept.rb +10 -0
- data/lib/activefacts/api/entity.rb +2 -0
- data/lib/activefacts/api/instance_index.rb +71 -0
- data/lib/activefacts/api/role_proxy.rb +70 -0
- data/lib/activefacts/api/role_values.rb +117 -0
- data/lib/activefacts/generate/absorption.rb +20 -30
- data/lib/activefacts/generate/null.rb +1 -0
- data/lib/activefacts/generate/ruby.rb +12 -28
- data/lib/activefacts/generate/sql/server.rb +8 -22
- data/lib/activefacts/generate/text.rb +1 -0
- data/lib/activefacts/input/cql.rb +30 -32
- data/lib/activefacts/input/orm.rb +11 -12
- data/lib/activefacts/persistence.rb +5 -0
- data/lib/activefacts/persistence/columns.rb +28 -21
- data/lib/activefacts/persistence/concept.rb +73 -0
- data/lib/activefacts/persistence/foreignkey.rb +17 -1
- data/lib/activefacts/persistence/index.rb +21 -10
- data/lib/activefacts/persistence/reference.rb +6 -6
- data/lib/activefacts/persistence/tables.rb +8 -5
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +17 -7
- data/lib/activefacts/vocabulary/metamodel.rb +22 -17
- data/spec/absorption_spec.rb +1 -1
- data/spec/cql_symbol_tables_spec.rb +1 -1
- data/spec/cqldump_spec.rb +8 -8
- data/spec/norma_tables_spec.rb +1 -1
- metadata +7 -3
data/Manifest.txt
CHANGED
@@ -26,8 +26,11 @@ lib/activefacts/api/concept.rb
|
|
26
26
|
lib/activefacts/api/constellation.rb
|
27
27
|
lib/activefacts/api/entity.rb
|
28
28
|
lib/activefacts/api/instance.rb
|
29
|
+
lib/activefacts/api/instance_index.rb
|
29
30
|
lib/activefacts/api/numeric.rb
|
30
31
|
lib/activefacts/api/role.rb
|
32
|
+
lib/activefacts/api/role_proxy.rb
|
33
|
+
lib/activefacts/api/role_values.rb
|
31
34
|
lib/activefacts/api/standard_types.rb
|
32
35
|
lib/activefacts/api/support.rb
|
33
36
|
lib/activefacts/api/value.rb
|
@@ -55,6 +58,7 @@ lib/activefacts/input/cql.rb
|
|
55
58
|
lib/activefacts/input/orm.rb
|
56
59
|
lib/activefacts/persistence.rb
|
57
60
|
lib/activefacts/persistence/columns.rb
|
61
|
+
lib/activefacts/persistence/concept.rb
|
58
62
|
lib/activefacts/persistence/foreignkey.rb
|
59
63
|
lib/activefacts/persistence/index.rb
|
60
64
|
lib/activefacts/persistence/reference.rb
|
data/examples/CQL/Metamodel.cql
CHANGED
@@ -16,6 +16,7 @@ Length is defined as UnsignedInteger(32);
|
|
16
16
|
Name is defined as VariableLengthText(64);
|
17
17
|
Numerator is defined as Decimal();
|
18
18
|
Ordinal is defined as UnsignedSmallInteger(32);
|
19
|
+
Pronoun is defined as VariableLengthText() restricted to {'feminine', 'masculine', 'personal'};
|
19
20
|
RingType is defined as VariableLengthText();
|
20
21
|
RoleSequenceId is defined as AutoCounter();
|
21
22
|
Scale is defined as UnsignedInteger(32);
|
@@ -142,8 +143,8 @@ SetExclusionConstraint is mandatory;
|
|
142
143
|
Alias is a kind of Feature;
|
143
144
|
|
144
145
|
Concept is a kind of Feature;
|
146
|
+
Concept uses at most one Pronoun;
|
145
147
|
Concept is independent;
|
146
|
-
Concept is personal;
|
147
148
|
Instance is of one Concept;
|
148
149
|
Role is where
|
149
150
|
FactType has Ordinal role played by Concept,
|
@@ -165,12 +166,12 @@ RoleRef is where
|
|
165
166
|
Role has Ordinal place in RoleSequence;
|
166
167
|
RoleRef has at most one leading-Adjective;
|
167
168
|
RoleRef has at most one trailing-Adjective;
|
168
|
-
|
169
|
-
RoleRef has Ordinal (as JoinStep)
|
170
|
-
|
171
|
-
Concept is traversed by
|
172
|
-
|
173
|
-
|
169
|
+
Join is where
|
170
|
+
RoleRef has Ordinal (as JoinStep) join;
|
171
|
+
Join traverses at most one Concept,
|
172
|
+
Concept is traversed by Join;
|
173
|
+
Join has at most one input-Role;
|
174
|
+
Join has at most one output-Role;
|
174
175
|
|
175
176
|
EntityType is a kind of Concept;
|
176
177
|
EntityType nests at most one FactType,
|
@@ -227,6 +228,9 @@ each combination EntityType, TypeInheritance occurs at most one time in
|
|
227
228
|
TypeInheritance provides identification;
|
228
229
|
each FactType occurs at least one time in
|
229
230
|
FactType has Ordinal role played by Concept;
|
231
|
+
each Join occurs at least one time in
|
232
|
+
Join has input-Role,
|
233
|
+
Join has output-Role;
|
230
234
|
each PresenceConstraint occurs at least one time in
|
231
235
|
PresenceConstraint has min-Frequency,
|
232
236
|
PresenceConstraint has max-Frequency,
|
@@ -121,6 +121,10 @@ module ActiveFacts
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
+
def subtypes
|
125
|
+
@subtypes ||= []
|
126
|
+
end
|
127
|
+
|
124
128
|
# Every new role added or inherited comes through here:
|
125
129
|
def realise_role(role) #:nodoc:
|
126
130
|
#puts "Realising role #{role.counterpart_concept.basename rescue role.counterpart_concept}.#{role.name} in #{basename}"
|
@@ -137,6 +141,10 @@ module ActiveFacts
|
|
137
141
|
|
138
142
|
# REVISIT: Use method_missing to catch all_some_role_as_other_role_and_third_role, to sort_by those roles?
|
139
143
|
|
144
|
+
def is_a? klass
|
145
|
+
super || supertypes_transitive.include?(klass)
|
146
|
+
end
|
147
|
+
|
140
148
|
private
|
141
149
|
|
142
150
|
def realise_supertypes(concept, all_supertypes = nil)
|
@@ -146,6 +154,7 @@ module ActiveFacts
|
|
146
154
|
s.each {|t|
|
147
155
|
next if all_supertypes.include? t
|
148
156
|
realise_supertypes(t, all_supertypes)
|
157
|
+
t.subtypes << self
|
149
158
|
all_supertypes << t
|
150
159
|
}
|
151
160
|
#puts "Realising roles of #{concept.basename} in #{basename}"
|
@@ -174,6 +183,7 @@ module ActiveFacts
|
|
174
183
|
else
|
175
184
|
target.roles[related_role_name] = role.counterpart = Role.new(target, definer, role, related_role_name, false, false)
|
176
185
|
end
|
186
|
+
role.counterpart_concept = target
|
177
187
|
#puts "Realising role pair #{definer.basename}.#{role_name} <-> #{target.basename}.#{related_role_name}"
|
178
188
|
realise_role(role)
|
179
189
|
target.realise_role(role.counterpart)
|
@@ -219,6 +219,8 @@ module ActiveFacts
|
|
219
219
|
|
220
220
|
def inherited(other) #:nodoc:
|
221
221
|
other.identified_by *identifying_role_names
|
222
|
+
subtypes << other unless subtypes.include? other
|
223
|
+
#puts "#{self.name} inherited by #{other.name}"
|
222
224
|
vocabulary.add_concept(other)
|
223
225
|
end
|
224
226
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# InstanceIndex class
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
module ActiveFacts
|
8
|
+
module API
|
9
|
+
#
|
10
|
+
# Each Constellation maintains an InstanceIndex for each Concept in its Vocabulary.
|
11
|
+
# The InstanceIndex object is returned when you call @constellation.Concept with no
|
12
|
+
# arguments (where Concept is the concept name you're interested in)
|
13
|
+
#
|
14
|
+
class InstanceIndex
|
15
|
+
def []=(key, value) #:nodoc:
|
16
|
+
raise "Adding RoleProxy to InstanceIndex" if value && RoleProxy === value
|
17
|
+
h[key] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](*args)
|
21
|
+
a = args
|
22
|
+
#a = naked(args)
|
23
|
+
# p "vvvv",
|
24
|
+
# args,
|
25
|
+
# a,
|
26
|
+
# keys.map{|k| v=super(k); (RoleProxy === k ? "*" : "")+k.to_s+"=>"+(RoleProxy === v ? "*" : "")+v.to_s}*",",
|
27
|
+
# "^^^^"
|
28
|
+
h[*a]
|
29
|
+
#super(*a)
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
h.size
|
34
|
+
end
|
35
|
+
|
36
|
+
def map &b
|
37
|
+
h.map &b
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return an array of all the instances of this concept
|
41
|
+
def values
|
42
|
+
h.values
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return an array of the identifying role values arrays for all the instances of this concept
|
46
|
+
def keys
|
47
|
+
h.keys
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete_if(&b) #:nodoc:
|
51
|
+
h.delete_if &b
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def h
|
56
|
+
@hash ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def naked(o)
|
60
|
+
case o
|
61
|
+
when Array
|
62
|
+
o.map{|e| naked(e) }
|
63
|
+
when RoleProxy
|
64
|
+
o.__getobj__
|
65
|
+
else
|
66
|
+
o
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# RoleProxy class, still somewhat experimental
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
require 'delegate'
|
8
|
+
|
9
|
+
module ActiveFacts
|
10
|
+
module API
|
11
|
+
#
|
12
|
+
# When you use the accessor method created by has_one, one_to_one, or maybe, you get a RoleProxy for the actual value.
|
13
|
+
# This behaves almost exactly as the value, but it knows through which role you fetched it.
|
14
|
+
# That will allow it to verbalise itself using the correct reading for that role.
|
15
|
+
#
|
16
|
+
# Don't use "SomeClass === role_value" to test the type, use "role_value.is_a?(SomeClass)" instead.
|
17
|
+
#
|
18
|
+
# In future, retrieving a value by indexing into a RoleValues array will do the same thing.
|
19
|
+
#
|
20
|
+
class RoleProxy < SimpleDelegator
|
21
|
+
def initialize(role, o = nil) #:nodoc:
|
22
|
+
@role = role # REVISIT: Use this to implement verbalise()
|
23
|
+
__setobj__(o)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(m, *a, &b) #:nodoc:
|
27
|
+
begin
|
28
|
+
super # Delegate first
|
29
|
+
rescue NoMethodError => e
|
30
|
+
__getobj__.method_missing(m, *a, &b)
|
31
|
+
rescue => e
|
32
|
+
raise
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def class #:nodoc:
|
37
|
+
__getobj__.class
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_a? klass #:nodoc:
|
41
|
+
__getobj__.is_a? klass
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s #:nodoc:
|
45
|
+
__getobj__.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def object_id #:nodoc:
|
49
|
+
__getobj__.object_id
|
50
|
+
end
|
51
|
+
|
52
|
+
# REVISIT: Should Proxies hash and eql? the same as their wards?
|
53
|
+
def hash #:nodoc:
|
54
|
+
__getobj__.hash ^ self.class.hash
|
55
|
+
end
|
56
|
+
|
57
|
+
def eql?(o) #:nodoc:
|
58
|
+
self.class == o.class and __getobj__.eql?(o)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ==(o) #:nodoc:
|
62
|
+
__getobj__.==(o)
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect #:nodoc:
|
66
|
+
"Proxy:#{__getobj__.inspect}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# RoleValues, manages the set of instances involved in a many_to_one relationship.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
# There are two implementations here, one using an array and one using a hash.
|
8
|
+
# The hash one has problems with keys being changed during object deletion, so
|
9
|
+
# cannot be used yet; a fix is upcoming and will improve performance of large sets.
|
10
|
+
#
|
11
|
+
module ActiveFacts
|
12
|
+
module API
|
13
|
+
|
14
|
+
class RoleValues #:nodoc:
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
#=begin
|
18
|
+
def initialize
|
19
|
+
@a = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def each &b
|
23
|
+
# REVISIT: Provide a configuration variable to enable this heckling during testing:
|
24
|
+
#@a.sort_by{rand}.each &b
|
25
|
+
@a.each &b
|
26
|
+
end
|
27
|
+
|
28
|
+
def size
|
29
|
+
@a.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def empty?
|
33
|
+
@a.size == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def +(a)
|
37
|
+
@a.+(a.is_a?(RoleValues) ? Array(a) : a)
|
38
|
+
end
|
39
|
+
|
40
|
+
def -(a)
|
41
|
+
@a - a
|
42
|
+
end
|
43
|
+
|
44
|
+
def single
|
45
|
+
@a.size > 1 ? nil : @a[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(old, value)
|
49
|
+
@a.delete(old) if old
|
50
|
+
@a << value if value
|
51
|
+
raise "Adding RoleProxy to RoleValues collection" if value && RoleProxy === value
|
52
|
+
end
|
53
|
+
|
54
|
+
def verbalise
|
55
|
+
"["+@a.to_a.map{|e| e.verbalise}*", "+"]"
|
56
|
+
end
|
57
|
+
#=end
|
58
|
+
|
59
|
+
=begin
|
60
|
+
def initialize
|
61
|
+
@h = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def each &b
|
65
|
+
@h.keys.each &b
|
66
|
+
end
|
67
|
+
|
68
|
+
def size
|
69
|
+
@h.size
|
70
|
+
end
|
71
|
+
|
72
|
+
def empty?
|
73
|
+
@h.size == 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def +(a)
|
77
|
+
@h.keys.+(a.is_a?(RoleValues) ? Array(a) : a)
|
78
|
+
end
|
79
|
+
|
80
|
+
def -(a)
|
81
|
+
@h.keys - a
|
82
|
+
end
|
83
|
+
|
84
|
+
def single
|
85
|
+
@h.size > 1 ? nil : @h.keys[0]
|
86
|
+
end
|
87
|
+
|
88
|
+
def update(old, value)
|
89
|
+
if old
|
90
|
+
unless @h.delete(old)
|
91
|
+
@h.each { |k, v|
|
92
|
+
next if k != old
|
93
|
+
puts "#{@h.object_id}: Didn't delete #{k.verbalise} (hash=#{k.hash}) matching #{old.verbalise} (hash=#{old.hash})"
|
94
|
+
puts "They are #{k.eql?(old) ? "" : "not "}eql?"
|
95
|
+
found = @h[k]
|
96
|
+
puts "found #{found.inspect}" if found
|
97
|
+
debugger
|
98
|
+
x = k.eql?(old)
|
99
|
+
y = old.eql?(k)
|
100
|
+
p y
|
101
|
+
}
|
102
|
+
raise "failed to delete #{old.verbalise}, have #{map{|e| e.verbalise}.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
puts "#{@h.object_id}: Adding #{value.inspect}" if value && (value.name == 'Meetingisboardmeeting' rescue false)
|
106
|
+
@h[value] = true if value
|
107
|
+
end
|
108
|
+
|
109
|
+
def verbalise
|
110
|
+
"["+@h.keys.map{|e| e.verbalise}*", "+"]"
|
111
|
+
end
|
112
|
+
=end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -14,7 +14,7 @@ module ActiveFacts
|
|
14
14
|
# afgen --absorption[=options] <file>.cql"
|
15
15
|
# Options are comma or space separated:
|
16
16
|
# * no_columns Don't emit the columns
|
17
|
-
# *
|
17
|
+
# * all Show Concepts that are not tables as well
|
18
18
|
# * paths Show the references paths through which each column was defined
|
19
19
|
# * no_identifier Don't show the identified_by columns for an EntityType
|
20
20
|
|
@@ -25,7 +25,6 @@ module ActiveFacts
|
|
25
25
|
@vocabulary = vocabulary
|
26
26
|
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
27
27
|
@no_columns = options.include? "no_columns"
|
28
|
-
@dependent = options.include? "dependent"
|
29
28
|
@paths = options.include? "paths"
|
30
29
|
@no_identifier = options.include? "no_identifier"
|
31
30
|
end
|
@@ -38,39 +37,30 @@ module ActiveFacts
|
|
38
37
|
multi_absorption_ets = 0
|
39
38
|
@vocabulary.tables
|
40
39
|
@vocabulary.all_feature.sort_by{|c| c.name}.each do |o|
|
41
|
-
|
42
|
-
next if ValueType === o && !o.supertype
|
40
|
+
next if !o.is_table
|
43
41
|
show(o)
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
45
|
def show concept #:nodoc:
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if (@paths)
|
67
|
-
ap = concept.absorption_paths
|
68
|
-
puts "#{ ap.map {|role|
|
69
|
-
prr = role.preferred_reference.describe
|
70
|
-
player = role.fact_type.entity_type == concept ? role.concept : (role.fact_type.all_role-[role])[0].concept
|
71
|
-
"\tcan absorb #{prr != role.concept.name ? "(via #{prr}) " : "" }into #{player.name}\n"
|
72
|
-
}*"" }"
|
73
|
-
end
|
46
|
+
indices = concept.indices
|
47
|
+
pk = indices.select(&:is_primary)[0]
|
48
|
+
indices = indices.clone
|
49
|
+
indices.delete pk
|
50
|
+
puts "#{concept.name}: #{
|
51
|
+
# "[#{concept.indices.size} indices] "
|
52
|
+
# } #{
|
53
|
+
concept.columns.sort_by do |column|
|
54
|
+
column.name(nil)
|
55
|
+
end.map do |column|
|
56
|
+
index_nrs =
|
57
|
+
[pk && pk.columns.include?(column) ? "*" : nil] +
|
58
|
+
(0...indices.size).select{|i| indices[i].columns.include?(column)}.map{|i| (i+1).to_i }
|
59
|
+
index_nrs.compact!
|
60
|
+
(@paths ? column.references.map{|r| r.to_names}.flatten : column.name(nil)) * "." +
|
61
|
+
(index_nrs.empty? ? "" : "["+index_nrs*""+"]")
|
62
|
+
end*", "
|
63
|
+
}"
|
74
64
|
|
75
65
|
end
|
76
66
|
end
|