activefacts 0.7.1 → 0.7.2

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.
@@ -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
@@ -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
- JoinPath is where
169
- RoleRef has Ordinal (as JoinStep) path;
170
- JoinPath traverses at most one Concept,
171
- Concept is traversed by JoinPath;
172
- JoinPath has one input-Role;
173
- JoinPath has one output-Role;
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
- # * dependent Show Concepts that are not tables as well
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
- # Don't dump imported (base) ValueTypes:
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
- return unless concept.is_table || @dependent
49
-
50
- print "#{concept.name}"
51
- print " (#{concept.tentative ? "tentatively " : ""}#{concept.is_table ? "in" : ""}dependent)" if @dependent
52
-
53
- if !@no_identifier && concept.is_a?(EntityType)
54
- print " is identified by:\n\t#{
55
- "REVISIT" # concept.references_from.to_s
56
- }"
57
- end
58
- print "\n"
59
-
60
- unless @no_columns
61
- puts "#{ concept.references_from.map do |role_ref|
62
- "\t#{role_ref.column_name(".")}\n"
63
- end*"" }"
64
- end
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