activefacts-rmap 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf33b3400b5c538ad43e3db107ca862a29f5b1f5
4
- data.tar.gz: 3eb0d4cfa5be71a688a8a41cb138601fd76f6e3d
3
+ metadata.gz: 41a6ef1c77d0e5d8085c6564a4cdf9e4715094ba
4
+ data.tar.gz: 59989842387ccc9f7bc701cd9be100279ef8af92
5
5
  SHA512:
6
- metadata.gz: d5358ddf562d53888ce4b9213a85e845d43b6b1947692bb8b2661b9ed30214b7c9fc2617ca589aaf674fde0715f584fdc8cba27ed4b1ece4ad68ddce60bd275a
7
- data.tar.gz: 1c6170b95730f6b6c0ad08249953ea9dd2c63ea78aedabbb240b6190289d7f27d5074d11280eba6418c248e20f1f90b088ba2f75722941e55d444027f2181208
6
+ metadata.gz: c14280f747f47d489475e8499c1b1fb40f6dabfd593c424e7494f62bb39677dc04c2f2022443c39595a51439be01c501634ad07312040b453f98f0fc29e6083a
7
+ data.tar.gz: b82c172e59dbad9b14078be5a2410d96ca8fac4e2e6777e01f63ce1e3ea592a71f9f6a901d7ae6ff7311f8ecf987c45de080834f928e47ea8b6ffed45173c39a
data/Gemfile CHANGED
@@ -2,7 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}
6
- $stderr.puts "Using work area gems for #{File.basename(File.dirname(__FILE__))} from activefacts-rmap"
7
- gem 'activefacts-metamodel', path: '/Users/cjh/work/activefacts/metamodel'
5
+ this_file = File.absolute_path(__FILE__)
6
+ if this_file =~ %r{\A#{ENV['HOME']}}i
7
+ dir = File.dirname(File.dirname(this_file))
8
+ $stderr.puts "Using work area gems in #{dir} from activefacts-rmap"
9
+ gem 'activefacts-metamodel', path: dir+'/metamodel'
8
10
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_development_dependency "bundler", ">= 1.10", "~> 1.10.6"
20
+ spec.add_development_dependency "bundler", ">= 1.10"
21
21
  spec.add_development_dependency "rake", "~> 10.0"
22
22
  spec.add_development_dependency "rspec", "~> 3.3"
23
23
 
@@ -68,69 +68,73 @@ module ActiveFacts
68
68
 
69
69
  def self.name(refs, separator = "")
70
70
  last_names = []
71
- names = refs.
72
- inject([]) do |a, ref|
73
-
74
- # Skip any object after the first which is identified by this reference
75
- if ref != refs[0] and
76
- !ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) and
77
- ref.to and
78
- ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
79
- (role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
80
- role_ref.role == ref.from_role
81
- trace :columns, "Skipping #{ref}, identifies non-initial object"
82
- next a
83
- end
71
+ name_array = nil
72
+ trace :columns, "Building column name from #{refs.inspect}" do
73
+ names = refs.
74
+ inject([]) do |a, ref|
75
+
76
+ # Skip any object after the first which is identified by this reference
77
+ if ref != refs[0] and
78
+ !ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) and
79
+ ref.to and
80
+ ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
81
+ (role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
82
+ role_ref.role == ref.from_role
83
+ trace :columns, "Skipping #{ref}, identifies non-initial object"
84
+ next a
85
+ end
84
86
 
85
- names = ref.to_names(ref != refs.last)
87
+ names = ref.to_names(ref != refs.last)
86
88
 
87
- # When traversing type inheritances, keep the subtype name, not the supertype names as well:
88
- if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
89
- if ref.to != ref.fact_type.subtype # Did we already have the subtype?
90
- trace :columns, "Skipping supertype #{ref}"
91
- next a
89
+ # When traversing type inheritances, keep the subtype name, not the supertype names as well:
90
+ if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
91
+ if ref.to != ref.fact_type.subtype # Did we already have the subtype?
92
+ trace :columns, "Skipping supertype #{ref}"
93
+ next a
94
+ end
95
+ trace :columns, "Eliding supertype in #{ref}"
96
+ last_names.size.times { a.pop } # Remove the last names added
97
+ elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
98
+ # When Xyz is followed by XyzID, truncate that to just ID
99
+ trace :columns, "truncating repeated #{last_names.last} in #{names[0]}"
100
+ names[0] = names[0][last_names.last.size..-1]
101
+ names.shift if names[0] == ''
102
+ elsif last_names.last == names[0]
103
+ # Same, but where an underscore split up the words
104
+ trace :columns, "truncating repeated name in #{names.inspect}"
105
+ names.shift
92
106
  end
93
- trace :columns, "Eliding supertype in #{ref}"
94
- last_names.size.times { a.pop } # Remove the last names added
95
- elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
96
- # When Xyz is followed by XyzID, truncate that to just ID
97
- trace :columns, "truncating repeated #{last_names.last} in #{names[0]}"
98
- names[0] = names[0][last_names.last.size..-1]
99
- names.shift if names[0] == ''
100
- elsif last_names.last == names[0]
101
- # Same, but where an underscore split up the words
102
- trace :columns, "truncating repeated name in #{names.inspect}"
103
- names.shift
104
- end
105
107
 
106
- # If the reference is to the single identifying role of the object_type making the reference,
107
- # strip the object_type name from the start of the reference role
108
- if a.size > 0 and
109
- (et = ref.from).is_a?(ActiveFacts::Metamodel::EntityType) and
110
- # This instead of the next 2 would apply to all identifying roles, but breaks some examples:
111
- # (role_ref = et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == ref.to_role}) and
112
- (role_ref = et.preferred_identifier.role_sequence.all_role_ref.single) and
113
- role_ref.role == ref.to_role and
114
- names[0][0...et.name.size].downcase == et.name.downcase
115
-
116
- trace :columns, "truncating transitive identifying role #{names.inspect}"
117
- names[0] = names[0][et.name.size..-1]
118
- names.shift if names[0] == ""
119
- end
108
+ # If the reference is to the single identifying role of the object_type making the reference,
109
+ # strip the object_type name from the start of the reference role
110
+ if a.size > 0 and
111
+ (et = ref.from).is_a?(ActiveFacts::Metamodel::EntityType) and
112
+ # This instead of the next 2 would apply to all identifying roles, but breaks some examples:
113
+ # (role_ref = et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == ref.to_role}) and
114
+ (role_ref = et.preferred_identifier.role_sequence.all_role_ref.single) and
115
+ role_ref.role == ref.to_role and
116
+ names[0][0...et.name.size].downcase == et.name.downcase
117
+
118
+ trace :columns, "truncating transitive identifying role #{names.inspect}"
119
+ names[0] = names[0][et.name.size..-1]
120
+ names.shift if names[0] == ""
121
+ end
120
122
 
121
- last_names = names
123
+ last_names = names
122
124
 
123
- a += names
124
- a
125
- end.elide_repeated_subsequences { |a, b|
126
- if a.is_a?(Array)
127
- a.map{|e| e.downcase} == b.map{|e| e.downcase}
128
- else
129
- a.downcase == b.downcase
130
- end
131
- }
125
+ a += names
126
+ a
127
+ end.elide_repeated_subsequences { |a, b|
128
+ if a.is_a?(Array)
129
+ a.map{|e| e.downcase} == b.map{|e| e.downcase}
130
+ else
131
+ a.downcase == b.downcase
132
+ end
133
+ }
132
134
 
133
- name_array = names.map{|n| n.sub(/^[a-z]/){|s| s.upcase}}
135
+ name_array = names.map{|n| n.sub(/^[a-z]/){|s| s.upcase}}
136
+ trace :columns, "column name is #{name_array*'.'}"
137
+ end
134
138
  separator ? name_array * separator : name_array
135
139
  end
136
140
 
@@ -163,21 +167,21 @@ module ActiveFacts
163
167
  end
164
168
 
165
169
  vt = references[-1].is_self_value ? references[-1].from : references[-1].to
166
- begin
170
+ begin
167
171
  params[:length] ||= vt.length if vt.length.to_i != 0
168
172
  params[:scale] ||= vt.scale if vt.scale.to_i != 0
169
173
  constraints << vt.value_constraint if vt.value_constraint
170
- last_vt = vt
174
+ last_vt = vt
171
175
  vt = vt.supertype
172
176
  end while vt
173
- params[:underlying_type] = last_vt
177
+ params[:underlying_type] = last_vt
174
178
  return [last_vt.name, params, constraints]
175
179
  end
176
180
 
177
181
  # The comment is the readings from the References expressed as a series of steps (not a full verbalisation)
178
182
  def comment
179
183
  @references.map do |ref|
180
- ref.verbalised_path
184
+ ref.verbalised_path
181
185
  end.compact * " and "
182
186
  end
183
187
 
@@ -192,34 +196,34 @@ module ActiveFacts
192
196
  cols =
193
197
  if is_unary
194
198
  kind = "unary "
195
- objectified_unary_columns =
196
- ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
199
+ objectified_unary_columns =
200
+ ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
197
201
 
198
202
  =begin
199
- # This code omits the unary if it's objectified and that plays a mandatory role
200
- first_mandatory_column = nil
201
- if (@to && @to.fact_type)
202
- trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do
203
- first_mandatory_column =
204
- objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary
205
- trace :unary_col, "checking column #{col.name}" do
206
- !col.references.detect do |ref|
207
- trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}"
208
- !ref.is_mandatory
209
- end
210
- end
211
- end
212
- if is_from_objectified_fact && first_mandatory_column
213
- trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory"
214
- end
215
- end
216
- end
217
-
218
- (is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified
203
+ # This code omits the unary if it's objectified and that plays a mandatory role
204
+ first_mandatory_column = nil
205
+ if (@to && @to.fact_type)
206
+ trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do
207
+ first_mandatory_column =
208
+ objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary
209
+ trace :unary_col, "checking column #{col.name}" do
210
+ !col.references.detect do |ref|
211
+ trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}"
212
+ !ref.is_mandatory
213
+ end
214
+ end
215
+ end
216
+ if is_from_objectified_fact && first_mandatory_column
217
+ trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory"
218
+ end
219
+ end
220
+ end
221
+
222
+ (is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified
219
223
  =end
220
224
 
221
- [Column.new()] + # The unary itself
222
- objectified_unary_columns
225
+ [Column.new()] + # The unary itself
226
+ objectified_unary_columns
223
227
  elsif is_self_value
224
228
  kind = "self-role "
225
229
  [Column.new()]
@@ -258,7 +262,7 @@ module ActiveFacts
258
262
  end
259
263
 
260
264
  def wipe_columns
261
- @columns = nil
265
+ @columns = nil
262
266
  end
263
267
  end
264
268
 
@@ -327,7 +331,7 @@ module ActiveFacts
327
331
  def identifier_columns
328
332
  trace :columns, "Identifier Columns for #{name}" do
329
333
  if absorbed_via and
330
- # If this is a subtype that has its own identification, use that.
334
+ # If this is a subtype that has its own identification, use that instead
331
335
  (all_type_inheritance_as_subtype.size == 0 ||
332
336
  all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification })
333
337
  return absorbed_via.from.identifier_columns
@@ -28,62 +28,62 @@ module ActiveFacts
28
28
  end
29
29
 
30
30
  def describe
31
- "foreign key from #{from.name}(#{from_columns.map{|c| c.name}*', '}) to #{to.name}(#{to_columns.map{|c| c.name}*', '})"
31
+ "foreign key from #{from.name}(#{from_columns.map{|c| c.name}*', '}) to #{to.name}(#{to_columns.map{|c| c.name}*', '})"
32
32
  end
33
33
 
34
34
  def verbalised_path reverse = false
35
- # REVISIT: This should be a proper join path verbalisation:
36
- refs = reverse ? references.reverse : references
37
- refs.map do |r|
38
- r.verbalised_path reverse
39
- end * ' and '
35
+ # REVISIT: This should be a proper join path verbalisation:
36
+ refs = reverse ? references.reverse : references
37
+ refs.map do |r|
38
+ r.verbalised_path reverse
39
+ end * ' and '
40
40
  end
41
41
 
42
42
  # Which references are absorbed into the "from" table?
43
43
  def precursor_references
44
- fk_jump = @references.detect(&:fk_jump)
45
- jump_index = @references.index(fk_jump)
46
- @references[0, jump_index]
44
+ fk_jump = @references.detect(&:fk_jump)
45
+ jump_index = @references.index(fk_jump)
46
+ @references[0, jump_index]
47
47
  end
48
48
 
49
49
  # Which references are absorbed into the "to" table?
50
50
  def following_references
51
- fk_jump = @references.detect(&:fk_jump)
52
- jump_index = @references.index(fk_jump)
53
- fk_jump != @references.last ? @references[jump_index+1..-1] : []
51
+ fk_jump = @references.detect(&:fk_jump)
52
+ jump_index = @references.index(fk_jump)
53
+ fk_jump != @references.last ? @references[jump_index+1..-1] : []
54
54
  end
55
55
 
56
56
  def jump_reference
57
- @references.detect(&:fk_jump)
57
+ @references.detect(&:fk_jump)
58
58
  end
59
59
 
60
60
  def to_name
61
- p = precursor_references
62
- f = following_references
63
- j = jump_reference
61
+ p = precursor_references
62
+ f = following_references
63
+ j = jump_reference
64
64
 
65
- @references.last.to_names +
66
- (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
65
+ @references.last.to_names +
66
+ (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
67
67
  end
68
68
 
69
69
  # The from_name is the role name of the table with the FK, viewed from the other end
70
70
  # When there are no precursor_references or following_references, it's the jump_reference.from_names
71
71
  # REVISIT: I'm still working out what to do with precursor_references and following_references
72
72
  def from_name
73
- p = precursor_references
74
- f = following_references
75
- j = jump_reference
73
+ p = precursor_references
74
+ f = following_references
75
+ j = jump_reference
76
76
 
77
- # pluralise unless j.is_one_to_one
77
+ # pluralise unless j.is_one_to_one
78
78
 
79
- # REVISIT: references[0].from_names is where the FK lives; but the object of interest may be an absorbed subclass which we should use here instead:
80
- # REVISIT: Should crunch superclasses in subtype traversals
81
- # REVISIT: Need to add "_as_rolename" where rolename is not to.name
79
+ # REVISIT: references[0].from_names is where the FK lives; but the object of interest may be an absorbed subclass which we should use here instead:
80
+ # REVISIT: Should crunch superclasses in subtype traversals
81
+ # REVISIT: Need to add "_as_rolename" where rolename is not to.name
82
82
 
83
- [
84
- @references[0].from_names,
85
- (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
86
- ]
83
+ [
84
+ @references[0].from_names,
85
+ (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
86
+ ]
87
87
  end
88
88
 
89
89
  end
@@ -103,83 +103,83 @@ module ActiveFacts
103
103
  # REVISIT: Disabled, as this should never happen.
104
104
  # next array if ref.to.absorbed_via != ref.fact_type
105
105
  end
106
- ref.fk_jump = true
106
+ ref.fk_jump = true
107
107
  array << [ref]
108
108
  elsif ref.is_absorbing or (ref.to && !ref.to.is_table)
109
- trace :fk, "getting fks absorbed into #{name} via #{ref}" do
110
- ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
111
- array << aref.insert(0, ref)
112
- end
113
- end
109
+ trace :fk, "getting fks absorbed into #{name} via #{ref}" do
110
+ ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
111
+ array << aref.insert(0, ref)
112
+ end
113
+ end
114
114
  end
115
115
  array
116
116
  end
117
117
  end
118
118
 
119
119
  def foreign_keys_to
120
- @foreign_keys_to ||= []
120
+ @foreign_keys_to ||= []
121
121
  end
122
122
 
123
123
  # Return an array of all the foreign keys from this table
124
124
  def foreign_keys
125
125
 
126
126
  # Get the ForeignKey object for each absorbed reference path
127
- @foreign_keys ||=
128
- begin
129
- fk_ref_paths = all_absorbed_foreign_key_reference_path
130
- fk_ref_paths.map do |fk_ref_path|
131
- trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
132
-
133
- from_columns = (columns||all_columns({})).select{|column|
134
- column.references[0...fk_ref_path.size] == fk_ref_path
135
- }
136
- trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
137
-
138
- # Figure out absorption on the target end:
139
- to = fk_ref_path.last.to
140
- if to.absorbed_via
141
- trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
142
- while (r = to.absorbed_via)
143
- m = r.reversed
144
- trace :fk, "#{m.reading}"
145
- fk_ref_path << m
146
- to = m.from == to ? m.to : m.from
147
- end
148
- trace :fk, "Absorption ends at #{to.name}"
149
- end
150
- end
151
-
152
- # REVISIT: This test may no longer be necessary
153
- raise "REVISIT: #{fk_ref_path.inspect} is bad" unless to and to.columns
154
-
155
- # REVISIT: This fails for absorbed subtypes having their own identification.
156
- # Check the CompanyDirectorEmployee model for example, EmployeeManagerNr -> Person (should reference EmployeeNr)
157
- # Need to use the absorbed identifier_columns of the subtype,
158
- # not the columns of the supertype that absorbs it.
159
- # But in general, that isn't going to work because in most DBMS
160
- # there's no suitable uniquen index on the subtype's identifier_columns
161
-
162
- to_columns = fk_ref_path[-1].to.identifier_columns
163
-
164
- # Put the column pairs in the correct order. They MUST be in the order they appear in the primary key
165
- froms, tos = from_columns.zip(to_columns).sort_by { |pair|
166
- to_columns.index(pair[1])
167
- }.transpose
168
-
169
- fk = ActiveFacts::RMap::ForeignKey.new(self, to, fk_ref_path, froms, tos)
170
- to.foreign_keys_to << fk
171
- fk
172
- end
173
- end.
174
- sort_by do |fk|
175
- # Put the foreign keys in a defined order:
176
- # debugger if !fk.to_columns || fk.to_columns.include?(nil) || !fk.from_columns || fk.from_columns.include?(nil)
177
- [ fk.to.name,
178
- fk.to_columns.map{|col| col.name(nil).sort},
179
- fk.from_columns.map{|col| col.name(nil).sort}
180
- ]
181
- end
182
- end
127
+ @foreign_keys ||=
128
+ begin
129
+ fk_ref_paths = all_absorbed_foreign_key_reference_path
130
+ fk_ref_paths.map do |fk_ref_path|
131
+ trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
132
+
133
+ from_columns = (columns||all_columns({})).select{|column|
134
+ column.references[0...fk_ref_path.size] == fk_ref_path
135
+ }
136
+ trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
137
+
138
+ # Figure out absorption on the target end:
139
+ to = fk_ref_path.last.to
140
+ if to.absorbed_via
141
+ trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
142
+ while (r = to.absorbed_via)
143
+ m = r.reversed
144
+ trace :fk, "#{m.reading}"
145
+ fk_ref_path << m
146
+ to = m.from == to ? m.to : m.from
147
+ end
148
+ trace :fk, "Absorption ends at #{to.name}"
149
+ end
150
+ end
151
+
152
+ # REVISIT: This test may no longer be necessary
153
+ raise "REVISIT: #{fk_ref_path.inspect} is bad" unless to and to.columns
154
+
155
+ # REVISIT: This fails for absorbed subtypes having their own identification.
156
+ # Check the CompanyDirectorEmployee model for example, EmployeeManagerNr -> Person (should reference EmployeeNr)
157
+ # Need to use the absorbed identifier_columns of the subtype,
158
+ # not the columns of the supertype that absorbs it.
159
+ # But in general, that isn't going to work because in most DBMS
160
+ # there's no suitable uniquen index on the subtype's identifier_columns
161
+
162
+ to_columns = fk_ref_path[-1].to.identifier_columns
163
+
164
+ # Put the column pairs in the correct order. They MUST be in the order they appear in the primary key
165
+ froms, tos = from_columns.zip(to_columns).sort_by { |pair|
166
+ to_columns.index(pair[1])
167
+ }.transpose
168
+
169
+ fk = ActiveFacts::RMap::ForeignKey.new(self, to, fk_ref_path, froms, tos)
170
+ to.foreign_keys_to << fk
171
+ fk
172
+ end
173
+ end.
174
+ sort_by do |fk|
175
+ # Put the foreign keys in a defined order:
176
+ # debugger if !fk.to_columns || fk.to_columns.include?(nil) || !fk.from_columns || fk.from_columns.include?(nil)
177
+ [ fk.to.name,
178
+ fk.to_columns.map{|col| col.name(nil).sort},
179
+ fk.from_columns.map{|col| col.name(nil).sort}
180
+ ]
181
+ end
182
+ end
183
183
 
184
184
  end
185
185
  end
@@ -69,13 +69,13 @@ module ActiveFacts
69
69
  end
70
70
 
71
71
  def to_s #:nodoc:
72
- if @uniqueness_constraint
73
- name = @uniqueness_constraint.name
74
- preferred = @uniqueness_constraint.is_preferred_identifier ? " (preferred)" : ""
75
- else
76
- name = "#{@on.name}IsUnique"
77
- preferred = !@on.injected_surrogate_role ? " (preferred)" : ""
78
- end
72
+ if @uniqueness_constraint
73
+ name = @uniqueness_constraint.name
74
+ preferred = @uniqueness_constraint.is_preferred_identifier ? " (preferred)" : ""
75
+ else
76
+ name = "#{@on.name}IsUnique"
77
+ preferred = !@on.injected_surrogate_role ? " (preferred)" : ""
78
+ end
79
79
  colnames = @columns.map(&:name)*", "
80
80
  "Index #{name} on #{@on.name} over #{@over.name}(#{colnames})#{preferred}"
81
81
  end
@@ -85,26 +85,26 @@ module ActiveFacts
85
85
  module Metamodel #:nodoc:
86
86
  class EntityType
87
87
  def self_index
88
- nil
88
+ nil
89
89
  end
90
90
  end
91
91
 
92
92
  class ValueType
93
93
  def self_index
94
- ActiveFacts::RMap::Index.new(
95
- nil, # The implied uniqueness constraint is not created
96
- self, # ValueType being indexed
97
- self, # Absorbed object being indexed
98
- columns.select{|c| c.references[0].is_self_value},
99
- injected_surrogate_role ? false : true
100
- )
94
+ ActiveFacts::RMap::Index.new(
95
+ nil, # The implied uniqueness constraint is not created
96
+ self, # ValueType being indexed
97
+ self, # Absorbed object being indexed
98
+ columns.select{|c| c.references[0].is_self_value},
99
+ injected_surrogate_role ? false : true
100
+ )
101
101
  end
102
102
  end
103
103
 
104
104
  class ObjectType
105
105
  # An array of each Index for this table
106
106
  def indices
107
- @indices || populate_indices
107
+ @indices || populate_indices
108
108
  end
109
109
 
110
110
  def clear_indices #:nodoc:
@@ -146,7 +146,7 @@ module ActiveFacts
146
146
  # trace :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name('.')}*", "}"
147
147
  ref.to_role.all_role_ref.each do |role_ref|
148
148
  all_pcs = role_ref.role_sequence.all_presence_constraint
149
- # puts "pcs over #{ref_path.map{|r| r.to_names}.flatten*'.'}: #{role_ref.role_sequence.all_presence_constraint.map(&:describe)*"; "}" if all_pcs.size > 0
149
+ # puts "pcs over #{ref_path.map{|r| r.to_names}.flatten*'.'}: #{role_ref.role_sequence.all_presence_constraint.map(&:describe)*"; "}" if all_pcs.size > 0
150
150
  pcs = all_pcs.
151
151
  reject do |pc|
152
152
  !pc.max_frequency or # No maximum freq; cannot be a uniqueness constraint
@@ -179,12 +179,12 @@ module ActiveFacts
179
179
  over = columns[0].references[absorption_level].from
180
180
 
181
181
  # Absorption through a one-to-one forms a UC that we don't need to enforce using an index:
182
- if over != self and
182
+ if over != self and
183
183
  over.absorbed_via == columns[0].references[absorption_level-1] and
184
184
  (rr = uc.role_sequence.all_role_ref.single) and
185
185
  over.absorbed_via.fact_type.all_role.include?(rr.role)
186
- next nil
187
- end
186
+ next nil
187
+ end
188
188
 
189
189
  index = ActiveFacts::RMap::Index.new(
190
190
  uc,
@@ -202,9 +202,9 @@ module ActiveFacts
202
202
  index.columns.map(&:name)+['', index.over.name]
203
203
  end
204
204
  end
205
- si = self_index
206
- @indices.unshift(si) if si
207
- @indices
205
+ si = self_index
206
+ @indices.unshift(si) if si
207
+ @indices
208
208
  end
209
209
 
210
210
  end
@@ -74,9 +74,9 @@ module ActiveFacts
74
74
 
75
75
  # Is this Reference covered by a mandatory constraint (implicitly or explicitly)
76
76
  def is_mandatory
77
- !is_unary &&
78
- (!@from_role || # All phantom roles of fact types are mandatory
79
- @from_role.is_mandatory)
77
+ !is_unary &&
78
+ (!@from_role || # All phantom roles of fact types are mandatory
79
+ @from_role.is_mandatory)
80
80
  end
81
81
 
82
82
  # Is this Reference from a unary Role?
@@ -177,6 +177,7 @@ module ActiveFacts
177
177
  # Flip the reference
178
178
  @to, @from = @from, @to
179
179
  @to_role, @from_role = @from_role, @to_role
180
+ trace :references, "Mirror #{self.inspect} absorbs #{@to.name}" if @to.absorbed_via == self
180
181
  self
181
182
  end
182
183
 
@@ -212,23 +213,23 @@ module ActiveFacts
212
213
  end
213
214
 
214
215
  def verbalised_path reverse = false
215
- return "#{from.name} Value" if is_self_value
216
- objectified = fact_type.entity_type
217
- f = # Switch to the Link Fact Type if we're traversing an objectification
218
- (to_role && to_role.link_fact_type) ||
219
- (from_role && from_role.link_fact_type) ||
220
- fact_type
221
-
222
- start_role =
223
- if objectified
224
- target = reverse ? to : from
225
- [to_role, from_role, f.all_role[0]].compact.detect{|role| role.object_type == target}
226
- else
227
- reverse ? to_role : from_role
228
- end
229
- reading = f.reading_preferably_starting_with_role(start_role)
230
- (is_mandatory || is_unary ? '' : 'maybe ') +
231
- reading.expand
216
+ return "#{from.name} Value" if is_self_value
217
+ objectified = fact_type.entity_type
218
+ f = # Switch to the Link Fact Type if we're traversing an objectification
219
+ (to_role && to_role.link_fact_type) ||
220
+ (from_role && from_role.link_fact_type) ||
221
+ fact_type
222
+
223
+ start_role =
224
+ if objectified
225
+ target = reverse ? to : from
226
+ [to_role, from_role, f.all_role_in_order[0]].compact.detect{|role| role.object_type == target}
227
+ else
228
+ reverse ? to_role : from_role
229
+ end
230
+ reading = f.reading_preferably_starting_with_role(start_role)
231
+ (is_mandatory || is_unary ? '' : 'maybe ') +
232
+ reading.expand
232
233
  end
233
234
 
234
235
  def inspect #:nodoc:
@@ -294,11 +295,8 @@ module ActiveFacts
294
295
  all_role.each do |role|
295
296
  # It's possible that this role is in an implicit or derived fact type. Skip it if so.
296
297
  next if role.fact_type.is_a?(LinkFactType) or
297
- # REVISIT: dafuq? Is this looking for a constraint over a derivation? This looks wrong.
298
- role.fact_type.preferred_reading.role_sequence.all_role_ref.to_a[0].play or
299
- # This is not yet actually set, and wouldn't handle constraint derivations anyhow:
300
- role.variable_as_projection
301
-
298
+ # REVISIT: dafuq? Is this looking for a constraint over a derivation? This looks wrong.
299
+ role.fact_type.preferred_reading.role_sequence.all_role_ref.to_a[0].play
302
300
  populate_reference role
303
301
  end
304
302
  end
@@ -324,13 +322,13 @@ module ActiveFacts
324
322
  when :supertype # A subtype absorbs a reference to its supertype when separate, or all when partitioned
325
323
  # REVISIT: Or when partitioned
326
324
  raise "Internal error, expected TypeInheritance" unless role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
327
- counterpart_role = (role.fact_type.all_role.to_a-[role])[0]
325
+ counterpart_role = (role.fact_type.all_role.to_a-[role])[0]
328
326
  if role.fact_type.assimilation or counterpart_role.object_type.is_separate
329
327
  trace :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
330
328
  else
331
329
  r = ActiveFacts::RMap::Reference.new(self, role)
332
330
  r.to.absorbed_via = r
333
- trace :references, "supertype #{name} absorbs subtype #{r.to.name}"
331
+ trace :references, "Supertype #{name} absorbs subtype #{r.to.name}"
334
332
  r.tabulate
335
333
  end
336
334
 
@@ -384,7 +382,7 @@ module ActiveFacts
384
382
  r.tabulate
385
383
  end
386
384
  else
387
- # REVISIT: Should we implicitly objectify this fact type here and add a spanning UC?
385
+ # REVISIT: Should we implicitly objectify this fact type here and add a spanning UC?
388
386
  raise "Role #{role.object_type.name} in '#{role.fact_type.default_reading}' lacks a uniqueness constraint"
389
387
  end
390
388
  end
@@ -45,16 +45,6 @@ module ActiveFacts
45
45
 
46
46
  @is_table
47
47
  end
48
-
49
- # Is this ValueType auto-assigned either at assert or on first save to the database?
50
- def is_auto_assigned
51
- type = self
52
- while type
53
- return true if type.name =~ /^Auto/ || type.transaction_phase
54
- type = type.supertype
55
- end
56
- false
57
- end
58
48
  end
59
49
 
60
50
  class EntityType < DomainObjectType
@@ -150,7 +140,7 @@ module ActiveFacts
150
140
  (rr = c.role_sequence.all_role_ref.single) and
151
141
  rr.role == self
152
142
  end
153
- # REVISIT: check mapping pragmas, e.g. by to_1.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
143
+ # REVISIT: check mapping pragmas, e.g. by to_1.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
154
144
 
155
145
  if fact_type.entity_type
156
146
  # This is a role in an objectified fact type
@@ -259,10 +249,10 @@ module ActiveFacts
259
249
  pi_ref = nil
260
250
  if pi_roles.size == 1 and
261
251
  object_type.references_to.detect do |ref|
262
- if ref.from_role == first_pi_role and ref.from.is_a?(EntityType) # and ref.is_mandatory # REVISIT
263
- pi_ref = ref
264
- end
265
- end
252
+ if ref.from_role == first_pi_role and ref.from.is_a?(EntityType) # and ref.is_mandatory # REVISIT
253
+ pi_ref = ref
254
+ end
255
+ end
266
256
 
267
257
  trace :absorption, "#{object_type.name} is fully absorbed along its sole reference path into entity type #{pi_ref.from.name}"
268
258
  object_type.definitely_not_table
@@ -275,16 +265,16 @@ module ActiveFacts
275
265
  pi_roles.include?(ref.to_role)
276
266
  }
277
267
  trace :absorption, "#{object_type.name} has #{non_identifying_refs_from.size} non-identifying functional roles" do
278
- non_identifying_refs_from.each do |ref|
279
- trace :absorption, "#{ref.inspect}"
280
- end
281
- end
268
+ non_identifying_refs_from.each do |ref|
269
+ trace :absorption, "#{ref.inspect}"
270
+ end
271
+ end
282
272
 
283
- trace :absorption, "#{object_type.name} has #{object_type.references_to.size} references to it:" do
284
- object_type.references_to.each do |ref|
285
- trace :absorption, ref.inspect
286
- end
287
- end if object_type.references_to.size > 1
273
+ trace :absorption, "#{object_type.name} has #{object_type.references_to.size} references to it:" do
274
+ object_type.references_to.each do |ref|
275
+ trace :absorption, ref.inspect
276
+ end
277
+ end if object_type.references_to.size > 1
288
278
 
289
279
  if object_type.references_to.size > 1 and
290
280
  non_identifying_refs_from.size > 0
@@ -298,7 +288,7 @@ module ActiveFacts
298
288
  non_identifying_refs_from.reject do |ref|
299
289
  !ref.to or ref.to.absorbed_via == ref
300
290
  end +
301
- object_type.references_to
291
+ object_type.references_to
302
292
  ).reject do |ref|
303
293
  next true if !ref.to.is_table or !ref.is_one_to_one
304
294
 
@@ -314,12 +304,12 @@ module ActiveFacts
314
304
  # If this object can be fully absorbed, do that (might require flipping some references)
315
305
  if absorption_paths.size > 0
316
306
  trace :absorption, "#{object_type.name} is fully absorbed through #{absorption_paths.inspect}" do
317
- absorption_paths.each do |ref|
318
- flip = object_type == ref.from
319
- ref.flip if flip
320
- trace :absorption, "#{object_type.name} is FULLY ABSORBED via {ref}#{flip ? ' (flipped)' : ''}"
321
- end
322
- end
307
+ absorption_paths.each do |ref|
308
+ flip = object_type == ref.from
309
+ ref.flip if flip
310
+ trace :absorption, "#{object_type.name} is FULLY ABSORBED via {ref}#{flip ? ' (flipped)' : ''}"
311
+ end
312
+ end
323
313
  object_type.definitely_not_table
324
314
  next object_type
325
315
  end
@@ -1,5 +1,5 @@
1
1
  module Activefacts
2
2
  module RMap
3
- VERSION = "1.8.1"
3
+ VERSION = "1.8.2"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts-rmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clifford Heath
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-04 00:00:00.000000000 Z
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.10'
20
- - - "~>"
21
- - !ruby/object:Gem::Version
22
- version: 1.10.6
23
20
  type: :development
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '1.10'
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: 1.10.6
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rake
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -121,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
115
  version: '0'
122
116
  requirements: []
123
117
  rubyforge_project:
124
- rubygems_version: 2.2.2
118
+ rubygems_version: 2.4.5
125
119
  signing_key:
126
120
  specification_version: 4
127
121
  summary: Relational mapping for ActiveFacts