updateable_views_inheritance 1.4.2 → 1.4.4

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.
@@ -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