updateable_views_inheritance 1.4.2 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,5 @@
1
1
  require 'active_record/connection_adapters/postgresql/utils'
2
2
 
3
-
4
3
  module ActiveRecord #:nodoc:
5
4
  module ConnectionAdapters #:nodoc:
6
5
  module PostgreSQL
@@ -9,30 +8,38 @@ module ActiveRecord #:nodoc:
9
8
  # Options:
10
9
  # [:parent]
11
10
  # parent relation
12
- # [:child_table_name]
11
+ # [:table]
13
12
  # default is <tt>"#{child_view}_data"</tt>
13
+ # [:skip_creating_child_table]
14
+ # use together with :table option
14
15
  def create_child(child_view, options)
15
16
  raise 'Please call me with a parent, for example: create_child(:steam_locomotives, :parent => :locomotives)' unless options[:parent]
16
17
 
17
- unqualified_child_view_name = Utils.extract_schema_qualified_name(child_view).identifier
18
-
19
18
  parent_relation = options[:parent].to_s
20
- if is_view?(parent_relation) # interpreted as inheritance chain deeper than two levels
21
- parent_table = query("SELECT child_relation FROM updateable_views_inheritance WHERE child_aggregate_view = #{quote(parent_relation)}")[0][0]
22
- else
23
- parent_table = parent_relation
24
- end
19
+ parent_table = if is_view?(parent_relation) # interpreted as inheritance chain deeper than two levels
20
+ query(<<~SQL)[0][0]
21
+ SELECT child_relation
22
+ FROM updateable_views_inheritance
23
+ WHERE child_aggregate_view = #{quote(parent_relation)}
24
+ SQL
25
+ else
26
+ parent_relation
27
+ end
25
28
 
26
29
  child_table = options[:table] || quote_table_name("#{child_view}_data")
27
- child_table_pk = "#{unqualified_child_view_name.singularize}_id"
28
30
 
29
- create_table(child_table, :id => false) do |t|
30
- t.integer child_table_pk, :null => false
31
- yield t
31
+ unless options.key?(:skip_creating_child_table)
32
+ unqualified_child_view_name = Utils.extract_schema_qualified_name(child_view).identifier
33
+ child_table_pk = "#{unqualified_child_view_name.singularize}_id"
34
+
35
+ create_table(child_table, :id => false) do |t|
36
+ t.integer child_table_pk, :null => false
37
+ yield t
38
+ end
39
+ execute "ALTER TABLE #{child_table} ADD PRIMARY KEY (#{child_table_pk})"
40
+ execute "ALTER TABLE #{child_table} ADD FOREIGN KEY (#{child_table_pk})
41
+ REFERENCES #{parent_table} ON DELETE CASCADE ON UPDATE CASCADE"
32
42
  end
33
- execute "ALTER TABLE #{child_table} ADD PRIMARY KEY (#{child_table_pk})"
34
- execute "ALTER TABLE #{child_table} ADD FOREIGN KEY (#{child_table_pk})
35
- REFERENCES #{parent_table} ON DELETE CASCADE ON UPDATE CASCADE"
36
43
 
37
44
  create_child_view(parent_relation, child_view, child_table)
38
45
  end
@@ -84,9 +91,9 @@ module ActiveRecord #:nodoc:
84
91
  end
85
92
  if pk
86
93
  if sequence
87
- select_value <<-end_sql, 'Reset sequence'
94
+ select_value <<-SQL, 'Reset sequence'
88
95
  SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
89
- end_sql
96
+ SQL
90
97
  else
91
98
  @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
92
99
  end
@@ -102,7 +109,7 @@ module ActiveRecord #:nodoc:
102
109
  # Returns a relation's primary key and belonging sequence. If +relation+ is a table the result is its PK and sequence.
103
110
  # When it is a view, PK and sequence of the table at the root of the inheritance chain are returned.
104
111
  def pk_and_sequence_for(relation)
105
- result = query(<<-end_sql, 'PK')[0]
112
+ result = query(<<-SQL, 'PK')[0]
106
113
  SELECT attr.attname
107
114
  FROM pg_attribute attr,
108
115
  pg_constraint cons
@@ -110,7 +117,7 @@ module ActiveRecord #:nodoc:
110
117
  AND cons.conrelid = '#{relation}'::regclass
111
118
  AND cons.contype = 'p'
112
119
  AND attr.attnum = ANY(cons.conkey)
113
- end_sql
120
+ SQL
114
121
  if result.nil? or result.empty?
115
122
  parent = parent_table(relation)
116
123
  pk_and_sequence_for(parent) if parent
@@ -130,7 +137,7 @@ module ActiveRecord #:nodoc:
130
137
  # Return the list of all views in the schema search path.
131
138
  def views(name=nil)
132
139
  schemas = schema_search_path.split(/,\s*/).map { |p| quote(p) }.join(',')
133
- query(<<-SQL, name).map { |row| row[0] }
140
+ query(<<~SQL, name).map { |row| row[0] }
134
141
  SELECT viewname
135
142
  FROM pg_views
136
143
  WHERE schemaname IN (#{schemas})
@@ -139,7 +146,7 @@ module ActiveRecord #:nodoc:
139
146
 
140
147
  # Checks whether relation +name+ is a view.
141
148
  def is_view?(name)
142
- result = query(<<-SQL, name).map { |row| row[0] }
149
+ result = query(<<~SQL, name).map { |row| row[0] }
143
150
  SELECT viewname
144
151
  FROM pg_views
145
152
  WHERE viewname = '#{name}'
@@ -149,11 +156,11 @@ module ActiveRecord #:nodoc:
149
156
 
150
157
  # Recursively delete +parent_relation+ (if it is a view) and the children views the depend on it.
151
158
  def remove_parent_and_children_views(parent_relation)
152
- children_views = query(<<-end_sql).map{|row| row[0]}
159
+ children_views = query(<<-SQL).map{|row| row[0]}
153
160
  SELECT child_aggregate_view
154
161
  FROM updateable_views_inheritance
155
162
  WHERE parent_relation = '#{parent_relation}'
156
- end_sql
163
+ SQL
157
164
  children_views.each do |cv|
158
165
  remove_parent_and_children_views(cv)
159
166
  # drop the view only if it wasn't dropped beforehand in recursive call from other method.
@@ -172,18 +179,18 @@ module ActiveRecord #:nodoc:
172
179
  def rebuild_parent_and_children_views(parent_relation)
173
180
  # Current implementation is not very efficient - it can drop and recreate one and the same view in the bottom of the hierarchy many times.
174
181
  remove_parent_and_children_views(parent_relation)
175
- children = query(<<-end_sql)
182
+ children = query(<<-SQL)
176
183
  SELECT parent_relation, child_aggregate_view, child_relation
177
184
  FROM updateable_views_inheritance
178
185
  WHERE parent_relation = '#{parent_relation}'
179
- end_sql
186
+ SQL
180
187
 
181
188
  #if the parent is in the middle of the inheritance chain, it's a view that should be rebuilt as well
182
- parent = query(<<-end_sql)[0]
189
+ parent = query(<<-SQL)[0]
183
190
  SELECT parent_relation, child_aggregate_view, child_relation
184
191
  FROM updateable_views_inheritance
185
192
  WHERE child_aggregate_view = '#{parent_relation}'
186
- end_sql
193
+ SQL
187
194
  create_child_view(parent[0], parent[1], parent[2]) if (parent && !parent.empty?)
188
195
 
189
196
  children.each do |child|
@@ -220,11 +227,11 @@ module ActiveRecord #:nodoc:
220
227
  quoted_inheritance_column = quote_column_name(parent_klass_name.inheritance_column)
221
228
  queries = relations.map{|rel| generate_single_table_inheritanche_union_clause(rel, sorted_column_names, conflict_column_names, columns_hash, quoted_inheritance_column)}
222
229
  unioin_clauses = queries.join("\n UNION ")
223
- execute <<-end_sql
230
+ execute <<-SQL
224
231
  CREATE VIEW #{sti_aggregate_view} AS (
225
232
  #{unioin_clauses}
226
233
  )
227
- end_sql
234
+ SQL
228
235
  end
229
236
 
230
237
  # Recreates the Single_Table_Inheritanche-like aggregate view +sti_aggregate_view+
@@ -234,7 +241,7 @@ module ActiveRecord #:nodoc:
234
241
  create_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view)
235
242
  end
236
243
 
237
- # Overriden - it must return false, otherwise deleting fixtures won't work
244
+ # Overriden - it solargraph-must return false, otherwise deleting fixtures won't work
238
245
  def supports_disable_referential_integrity?
239
246
  false
240
247
  end
@@ -247,194 +254,201 @@ module ActiveRecord #:nodoc:
247
254
  module Tutuf #:nodoc:
248
255
  class ClassTableReflection
249
256
  class << self
250
- # Returns all models' class objects that are ActiveRecord::Base descendants
251
- def all_db_klasses
252
- return @@klasses if defined?(@@klasses)
253
- @@klasses = []
254
- # load model classes so that inheritance_column is set correctly where defined
255
- model_filenames.collect{|m| load "#{Rails.root}/app/models/#{m}";m.match(%r{([^/]+?)\.rb$})[1].camelize.constantize }.each do |klass|
256
- @@klasses << klass if klass < ActiveRecord::Base
257
- end
258
- @@klasses.uniq
257
+ # Returns all models' class objects that are ActiveRecord::Base descendants
258
+ def all_db_klasses
259
+ return @@klasses if defined?(@@klasses)
260
+
261
+ @@klasses = []
262
+ # load model classes so that inheritance_column is set correctly where defined
263
+ model_filenames.collect{|m| load "#{Rails.root}/app/models/#{m}";m.match(%r{([^/]+?)\.rb$})[1].camelize.constantize }.each do |klass|
264
+ @@klasses << klass if klass < ActiveRecord::Base
259
265
  end
266
+ @@klasses.uniq
267
+ end
260
268
 
261
- # Returns the class object for +table_name+
262
- def get_klass_for_table(table_name)
263
- klass_for_tables()[table_name.to_s]
264
- end
269
+ # Returns the class object for +table_name+
270
+ def get_klass_for_table(table_name)
271
+ klass_for_tables()[table_name.to_s]
272
+ end
265
273
 
266
- # Returns hash with tables and thier corresponding class.
267
- # {table_name1 => ClassName1, ...}
268
- def klass_for_tables
269
- return @@tables_klasses if defined?(@@tables_klasses)
270
- @@tables_klasses = {}
271
- all_db_klasses.each do |klass|
272
- @@tables_klasses[klass.table_name] = klass if klass.respond_to?(:table_name)
273
- end
274
- @@tables_klasses
275
- end
274
+ # Returns hash with tables and thier corresponding class.
275
+ # {table_name1 => ClassName1, ...}
276
+ def klass_for_tables
277
+ return @@tables_klasses if defined?(@@tables_klasses)
276
278
 
277
- # Returns filenames for models in the current Rails application
278
- def model_filenames
279
- Dir.chdir("#{Rails.root}/app/models"){ Dir["**/*.rb"] }
279
+ @@tables_klasses = {}
280
+ all_db_klasses.each do |klass|
281
+ @@tables_klasses[klass.table_name] = klass if klass.respond_to?(:table_name)
280
282
  end
283
+ @@tables_klasses
284
+ end
285
+
286
+ # Returns filenames for models in the current Rails application
287
+ def model_filenames
288
+ Dir.chdir("#{Rails.root}/app/models"){ Dir["**/*.rb"] }
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ # Set default values from the table columns for a view
295
+ def set_defaults(view_name, table_name)
296
+ column_definitions(table_name).each do |column_name, type, default, notnull|
297
+ if !default.nil?
298
+ execute("ALTER TABLE #{quote_table_name(view_name)} ALTER #{quote_column_name(column_name)} SET DEFAULT #{default}")
281
299
  end
282
300
  end
283
301
  end
284
302
 
285
303
  private
286
304
 
287
- def do_create_child_view(parent_table, parent_columns, parent_pk, child_view, child_columns, child_pk, child_table)
288
- view_columns = parent_columns + child_columns
289
- execute <<-end_sql
290
- CREATE OR REPLACE VIEW #{child_view} AS (
291
- SELECT parent.#{parent_pk},
292
- #{ view_columns.join(",") }
293
- FROM #{parent_table} parent
294
- INNER JOIN #{child_table} child
295
- ON ( parent.#{parent_pk}=child.#{child_pk} )
296
- )
297
- end_sql
298
- end
305
+ def do_create_child_view(parent_table, parent_columns, parent_pk, child_view, child_columns, child_pk, child_table)
306
+ view_columns = parent_columns + child_columns
307
+ execute(<<~SQL)
308
+ CREATE OR REPLACE VIEW #{quote_column_name(child_view)} AS (
309
+ SELECT parent.#{parent_pk},
310
+ #{ view_columns.map { |col| quote_column_name(col) }.join(",") }
311
+ FROM #{parent_table} parent
312
+ INNER JOIN #{child_table} child
313
+ ON ( parent.#{parent_pk}=child.#{child_pk} )
314
+ )
315
+ SQL
316
+ end
317
+
318
+ # Creates rules for +INSERT+, +UPDATE+ and +DELETE+ on the view
319
+ def make_child_view_updateable(parent_table, parent_columns, parent_pk, parent_pk_seq, child_view, child_columns, child_pk, child_table)
320
+ # insert
321
+ # NEW.#{parent_pk} can be explicitly specified and when it is null every call to it increments the sequence.
322
+ # Setting the sequence to its value (explicitly supplied or the default) covers both cases.
323
+ execute(<<~SQL)
324
+ CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_insert")} AS
325
+ ON INSERT TO #{quote_column_name(child_view)} DO INSTEAD (
326
+ INSERT INTO #{parent_table}
327
+ ( #{ [parent_pk, parent_columns].flatten.map { |col| quote_column_name(col) }.join(", ") } )
328
+ VALUES( DEFAULT #{ parent_columns.empty? ? '' : ' ,' + parent_columns.collect{ |col| "NEW.#{quote_column_name(col)}" }.join(", ") } ) ;
329
+ INSERT INTO #{child_table}
330
+ ( #{ [child_pk, child_columns].flatten.map { |col| quote_column_name(col) }.join(",")} )
331
+ VALUES( currval('#{parent_pk_seq}') #{ child_columns.empty? ? '' : ' ,' + child_columns.collect{ |col| "NEW.#{quote_column_name(col)}" }.join(", ") } )
332
+ #{insert_returning_clause(parent_pk, child_pk, child_view)}
333
+ )
334
+ SQL
335
+
336
+ # delete
337
+ execute(<<~SQL)
338
+ CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_delete")} AS
339
+ ON DELETE TO #{quote_column_name(child_view)} DO INSTEAD
340
+ DELETE FROM #{parent_table} WHERE #{parent_pk} = OLD.#{parent_pk}
341
+ SQL
299
342
 
300
- # Creates rules for +INSERT+, +UPDATE+ and +DELETE+ on the view
301
- def make_child_view_updateable(parent_table, parent_columns, parent_pk, parent_pk_seq, child_view, child_columns, child_pk, child_table)
302
- # insert
303
- # NEW.#{parent_pk} can be explicitly specified and when it is null every call to it increments the sequence.
304
- # Setting the sequence to its value (explicitly supplied or the default) covers both cases.
305
- execute <<-end_sql
306
- CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_insert")} AS
307
- ON INSERT TO #{child_view} DO INSTEAD (
308
- INSERT INTO #{parent_table}
309
- ( #{ [parent_pk, parent_columns].flatten.join(", ") } )
310
- VALUES( DEFAULT #{ parent_columns.empty? ? '' : ' ,' + parent_columns.collect{ |col| "NEW." + col}.join(", ") } ) ;
311
- INSERT INTO #{child_table}
312
- ( #{ [child_pk, child_columns].flatten.join(",")} )
313
- VALUES( currval('#{parent_pk_seq}') #{ child_columns.empty? ? '' : ' ,' + child_columns.collect{ |col| "NEW." + col}.join(", ") } )
314
- #{insert_returning_clause(parent_pk, child_pk, child_view)}
315
- )
316
- end_sql
317
-
318
- # delete
319
- execute <<-end_sql
320
- CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_delete")} AS
321
- ON DELETE TO #{child_view} DO INSTEAD
322
- DELETE FROM #{parent_table} WHERE #{parent_pk} = OLD.#{parent_pk}
323
- end_sql
324
-
325
- # update
326
- execute <<-end_sql
327
- CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_update")} AS
328
- ON UPDATE TO #{child_view} DO INSTEAD (
329
- #{ parent_columns.empty? ? '':
343
+ # update
344
+ execute(<<~SQL)
345
+ CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_update")} AS
346
+ ON UPDATE TO #{quote_column_name(child_view)} DO INSTEAD (
347
+ #{ if parent_columns.empty?
348
+ ''
349
+ else
330
350
  "UPDATE #{parent_table}
331
- SET #{ parent_columns.collect{ |col| col + "= NEW." + col }.join(", ") }
332
- WHERE #{parent_pk} = OLD.#{parent_pk};"}
333
- #{ child_columns.empty? ? '':
351
+ SET #{ parent_columns.map { |col| "#{quote_column_name(col)} = NEW.#{quote_column_name(col)}" }.join(', ')}
352
+ WHERE #{parent_pk} = OLD.#{parent_pk};"
353
+ end }
354
+ #{ if child_columns.empty?
355
+ ''
356
+ else
334
357
  "UPDATE #{child_table}
335
- SET #{ child_columns.collect{ |col| col + " = NEW." + col }.join(", ") }
358
+ SET #{ child_columns.map { |col| "#{quote_column_name(col)} = NEW.#{quote_column_name(col)}" }.join(', ')}
336
359
  WHERE #{child_pk} = OLD.#{parent_pk}"
337
- }
338
- )
339
- end_sql
340
- end
360
+ end }
361
+ )
362
+ SQL
363
+ end
341
364
 
342
- def insert_returning_clause(parent_pk, child_pk, child_view)
343
- columns_cast_to_null = columns(child_view)
344
- .reject { |c| c.name == parent_pk}
345
- .map { |c| "CAST (NULL AS #{c.sql_type})" }
346
- .join(", ")
347
- "RETURNING #{child_pk}, #{columns_cast_to_null}"
348
- end
365
+ def insert_returning_clause(parent_pk, child_pk, child_view)
366
+ columns_cast_to_null = columns(child_view)
367
+ .reject { |c| c.name == parent_pk}
368
+ .map { |c| "CAST (NULL AS #{c.sql_type})" }
369
+ .join(", ")
370
+ "RETURNING #{child_pk}, #{columns_cast_to_null}"
371
+ end
349
372
 
350
- # Set default values from the table columns for a view
351
- def set_defaults(view_name, table_name)
352
- column_definitions(table_name).each do |column_name, type, default, notnull|
353
- if !default.nil?
354
- execute("ALTER TABLE #{quote_table_name(view_name)} ALTER #{quote_column_name(column_name)} SET DEFAULT #{default}")
355
- end
356
- end
373
+ def create_system_table_records(parent_relation, child_aggregate_view, child_relation)
374
+ parent_relation, child_aggregate_view, child_relation = [parent_relation, child_aggregate_view, child_relation].collect{|rel| quote(rel.to_s)}
375
+ exists = query <<~SQL
376
+ SELECT parent_relation, child_aggregate_view, child_relation
377
+ FROM updateable_views_inheritance
378
+ WHERE parent_relation = #{parent_relation}
379
+ AND child_aggregate_view = #{child_aggregate_view}
380
+ AND child_relation = #{child_relation}
381
+ SQL
382
+ # log "res: #{exists}"
383
+ if exists.nil? or exists.empty?
384
+ execute "INSERT INTO updateable_views_inheritance (parent_relation, child_aggregate_view, child_relation)" +
385
+ "VALUES( #{parent_relation}, #{child_aggregate_view}, #{child_relation} )"
357
386
  end
387
+ end
358
388
 
359
- def create_system_table_records(parent_relation, child_aggregate_view, child_relation)
360
- parent_relation, child_aggregate_view, child_relation = [parent_relation, child_aggregate_view, child_relation].collect{|rel| quote(rel.to_s)}
361
- exists = query <<-end_sql
362
- SELECT parent_relation, child_aggregate_view, child_relation
389
+ def parent_table(relation)
390
+ if table_exists?('updateable_views_inheritance')
391
+ res = query(<<-SQL, 'Parent relation')[0]
392
+ SELECT parent_relation
363
393
  FROM updateable_views_inheritance
364
- WHERE parent_relation = #{parent_relation}
365
- AND child_aggregate_view = #{child_aggregate_view}
366
- AND child_relation = #{child_relation}
367
- end_sql
368
- # log "res: #{exists}"
369
- if exists.nil? or exists.empty?
370
- execute "INSERT INTO updateable_views_inheritance (parent_relation, child_aggregate_view, child_relation)" +
371
- "VALUES( #{parent_relation}, #{child_aggregate_view}, #{child_relation} )"
372
- end
373
- end
374
-
375
- def parent_table(relation)
376
- if table_exists?('updateable_views_inheritance')
377
- res = query(<<-end_sql, 'Parent relation')[0]
378
- SELECT parent_relation
379
- FROM updateable_views_inheritance
380
- WHERE child_aggregate_view = '#{relation}'
381
- end_sql
382
- res[0] if res
383
- end
394
+ WHERE child_aggregate_view = '#{relation}'
395
+ SQL
396
+ res[0] if res
384
397
  end
398
+ end
385
399
 
386
- # Single Table Inheritance Aggregate View
387
-
388
- # Nested list for the +parent_relation+ inheritance hierarchy
389
- # Every descendant relation is presented as an array with relation's name as first element
390
- # and the other elements are the relation's children presented in the same way as lists.
391
- # For example:
392
- # [[child_view1, [grandchild11,[...]], [grandchild12]],
393
- # [child_view2, [...]
394
- # ]
395
- def get_view_hierarchy_for(parent_relation)
396
- hierarchy = []
397
- children = query(<<-end_sql)
398
- SELECT parent_relation, child_aggregate_view, child_relation
399
- FROM updateable_views_inheritance
400
- WHERE parent_relation = '#{parent_relation}'
401
- end_sql
402
- children.each do |child|
403
- hierarchy << [child[1], *get_view_hierarchy_for(child[1])]
404
- end
405
- hierarchy
400
+ # Single Table Inheritance Aggregate View
401
+
402
+ # Nested list for the +parent_relation+ inheritance hierarchy
403
+ # Every descendant relation is presented as an array with relation's name as first element
404
+ # and the other elements are the relation's children presented in the same way as lists.
405
+ # For example:
406
+ # [[child_view1, [grandchild11,[...]], [grandchild12]],
407
+ # [child_view2, [...]
408
+ # ]
409
+ def get_view_hierarchy_for(parent_relation)
410
+ hierarchy = []
411
+ children = query(<<-SQL)
412
+ SELECT parent_relation, child_aggregate_view, child_relation
413
+ FROM updateable_views_inheritance
414
+ WHERE parent_relation = '#{parent_relation}'
415
+ SQL
416
+ children.each do |child|
417
+ hierarchy << [child[1], *get_view_hierarchy_for(child[1])]
406
418
  end
419
+ hierarchy
420
+ end
407
421
 
408
- def get_leaves_relations(hierarchy)
409
- return [] if hierarchy.nil? || hierarchy.empty?
410
- head, hierarchy = hierarchy.first, hierarchy[1..(hierarchy.size)]
411
- if(head.is_a? Array)
412
- return (get_leaves_relations(head) + get_leaves_relations(hierarchy)).compact
413
- elsif(hierarchy.nil? || hierarchy.empty?)
414
- return [head]
415
- else
416
- return get_leaves_relations(hierarchy).compact
417
- end
422
+ def get_leaves_relations(hierarchy)
423
+ return [] if hierarchy.nil? || hierarchy.empty?
424
+ head, hierarchy = hierarchy.first, hierarchy[1..(hierarchy.size)]
425
+ if(head.is_a? Array)
426
+ return (get_leaves_relations(head) + get_leaves_relations(hierarchy)).compact
427
+ elsif(hierarchy.nil? || hierarchy.empty?)
428
+ return [head]
429
+ else
430
+ return get_leaves_relations(hierarchy).compact
418
431
  end
432
+ end
419
433
 
420
- def generate_single_table_inheritanche_union_clause(rel, column_names, conflict_column_names, columns_hash, quoted_inheritance_column)
421
- relation_columns = columns(rel).collect{|c| c.name}
422
- columns_select = column_names.inject([]) do |arr, col_name|
423
- sql_type = conflict_column_names.include?(col_name) ? 'text' : columns_hash[col_name].sql_type
424
- value = "NULL::#{sql_type}"
425
- if(relation_columns.include?(col_name))
426
- value = col_name
427
- value = "#{value}::text" if conflict_column_names.include?(col_name)
428
- end
429
- statement = " AS #{col_name}"
430
- statement = "#{value} #{statement}"
431
- arr << " #{statement}"
434
+ def generate_single_table_inheritanche_union_clause(rel, column_names, conflict_column_names, columns_hash, quoted_inheritance_column)
435
+ relation_columns = columns(rel).collect{|c| c.name}
436
+ columns_select = column_names.inject([]) do |arr, col_name|
437
+ sql_type = conflict_column_names.include?(col_name) ? 'text' : columns_hash[col_name].sql_type
438
+ value = "NULL::#{sql_type}"
439
+ if(relation_columns.include?(col_name))
440
+ value = col_name
441
+ value = "#{value}::text" if conflict_column_names.include?(col_name)
432
442
  end
433
- columns_select = columns_select.join(", ")
434
- rel_klass_name = Tutuf::ClassTableReflection.get_klass_for_table(rel)
435
- where_clause = " WHERE #{quoted_inheritance_column} = '#{rel_klass_name}'"
436
- ["SELECT", columns_select, "FROM #{rel} #{where_clause}"].join(" ")
443
+ statement = " AS #{col_name}"
444
+ statement = "#{value} #{statement}"
445
+ arr << " #{statement}"
437
446
  end
447
+ columns_select = columns_select.join(", ")
448
+ rel_klass_name = Tutuf::ClassTableReflection.get_klass_for_table(rel)
449
+ where_clause = " WHERE #{quoted_inheritance_column} = '#{rel_klass_name}'"
450
+ ["SELECT", columns_select, "FROM #{rel} #{where_clause}"].join(" ")
451
+ end
438
452
  end
439
453
  end
440
454
  end
@@ -1,3 +1,3 @@
1
1
  module UpdateableViewsInheritance
2
- VERSION = "1.4.2"
2
+ VERSION = "1.4.4"
3
3
  end
data/test/content_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'test_helper')
1
+ require_relative 'test_helper'
2
2
 
3
3
  class UpdateableViewsInheritanceContentTest < ActiveSupport::TestCase
4
4
  def setup
@@ -6,6 +6,7 @@ class UpdateableViewsInheritanceContentTest < ActiveSupport::TestCase
6
6
  ActiveRecord::FixtureSet.reset_cache
7
7
  end
8
8
 
9
+
9
10
  def test_find
10
11
  ActiveRecord::FixtureSet.create_fixtures(File.dirname(__FILE__) + '/fixtures/', :steam_locomotives)
11
12
  locomotive = Locomotive.find(1)
File without changes
@@ -2,3 +2,4 @@ test:
2
2
  adapter: postgresql
3
3
  database: updateable_views_inheritance_test
4
4
  min_messages: ERROR
5
+ host: localhost
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'test_helper')
1
+ require_relative 'test_helper'
2
2
  require 'generators/updateable_views_inheritance/install_generator'
3
3
 
4
4
  class InstallGeneratorTest < Rails::Generators::TestCase
@@ -10,4 +10,4 @@ class InstallGeneratorTest < Rails::Generators::TestCase
10
10
  run_generator
11
11
  assert_migration 'db/migrate/create_updateable_views_inheritance.rb'
12
12
  end
13
- end
13
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'test_helper'
2
+
3
+ class InstantiationTest < ActiveSupport::TestCase
4
+ def setup
5
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 7)
6
+ # order of fixtures is important for the test - last loaded should not be with max(id)
7
+ %w[steam_locomotives electric_locomotives maglev_locomotives bicycles].each do |f|
8
+ ActiveRecord::FixtureSet.create_fixtures(File.dirname(__FILE__) + '/fixtures/', f)
9
+ end
10
+ @connection = ActiveRecord::Base.connection
11
+
12
+ Locomotive.disable_inheritance_instantiation = true
13
+ ElectricLocomotive.disable_inheritance_instantiation = false
14
+ end
15
+
16
+ def teardown
17
+ Locomotive.disable_inheritance_instantiation = false
18
+ ActiveRecord::FixtureSet.reset_cache
19
+ end
20
+
21
+ def test_setting_disable_inheritance_instantiation_does_not_load_child_columns
22
+ unless Locomotive.first
23
+ puts "num locomotives:", @connection.select_one("SELECT count(id) FROM locomotives")
24
+ end
25
+ assert_equal %w[id max_speed name type],
26
+ Locomotive.first.attributes.keys.sort
27
+ end
28
+
29
+ def test_switching_off_disable_inheritance_instantiation_loads_child_columns
30
+ assert_equal %w[electricity_consumption id magnetic_field max_speed name type],
31
+ MaglevLocomotive.first.attributes.keys.sort
32
+ end
33
+
34
+ def test_disable_inheritance_instantiatioon_not_set_loads_child_attributes
35
+ assert_equal %w[id name number_of_gears number_of_wheels vehicle_type],
36
+ Bicycle.first.attributes.keys.sort
37
+ end
38
+ end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'test_helper')
1
+ require_relative 'test_helper'
2
2
 
3
3
  class UpdateableViewsInheritanceMigrationTest < ActiveSupport::TestCase
4
4
  # We use transactional fixtures - migration from the setup is rolled back by Rails on teardown
@@ -17,7 +17,7 @@ class UpdateableViewsInheritanceMigrationTest < ActiveSupport::TestCase
17
17
  assert_equal %w(electricity_consumption id max_speed name type),
18
18
  @connection.columns(:electric_locomotives).map{ |c| c.name }.sort
19
19
  end
20
-
20
+
21
21
  def test_drop_child
22
22
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 3)
23
23
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 2)
@@ -27,4 +27,4 @@ class UpdateableViewsInheritanceMigrationTest < ActiveSupport::TestCase
27
27
  steam_locomotives_data
28
28
  updateable_views_inheritance), @connection.tables.sort
29
29
  end
30
- end
30
+ end