updateable_views_inheritance 1.4.4 → 1.4.6

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
  SHA256:
3
- metadata.gz: 5a592cb214e5825bb4af87c5412ae3f91730d0e3882931f71bd16378ace7640a
4
- data.tar.gz: 2fbf14052dde92e69724d574c0b5fb7b4d79297928220bc8bee5851cc8ee60ad
3
+ metadata.gz: 1c36199deb17d6a32a617a2193c52ef0cb5e6b955464fb31b8509eb4aef0212d
4
+ data.tar.gz: 62286805f012a6db4bbc44de4a3ac9848f735d7d0e90d674f7605cbd48c590da
5
5
  SHA512:
6
- metadata.gz: b4da55b7632575a0fba84248130308890f56dd726e6aaec835d3423b874a959d88ecf51dbf482795ba97869e5255411b9599b0117df61c6e70b3f095322c23fe
7
- data.tar.gz: edd29da257c91e7d8a53df032bc70dd2317c78433637649e23285118b058109116259ed22efc86b8495f44301c5c6831c125e26565c1aa203ad00663205c8f6c
6
+ metadata.gz: a533be494b122a7b0e9bf2fcebd0083b314892cfe6da865faed721bd7cd5859820bc1eb969f098cfbb43ef4ec6d762521dbd9b911e57444fd286bdfa85540911
7
+ data.tar.gz: 45cd523f6e1430c3638ce7df73aabc87e09246a51d6b22591eea762c4d191fc7114c3f9a3f7d358dadf808c09844b8d5fa6a5f7480b45e61158f91796d1b8bf0
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
- ## 1.4.4 (09 October 2024)
1
+ ## 1.4.6 (22 October 2024)
2
+
3
+ Features:
4
+
5
+ - Add option for child primary key.
6
+
7
+ ## 1.4.5 (09 October 2024)
2
8
 
3
9
  Bugfixes:
4
10
 
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Class Table Inheritance
2
2
 
3
3
  [![Build](https://github.com/tutuf/updateable_views_inheritance/actions/workflows/build.yml/badge.svg)](https://github.com/tutuf/updateable_views_inheritance/actions?query=workflow:build)
4
- [![Coverage](https://app.deepsource.com/gh/tutuf/updateable_views_inheritance.svg/?label=code+coverage&show_trend=true&token=AMfm8-_-qDZoknMh9-8IYp3R)](https://app.deepsource.com/gh/tutuf/updateable_views_inheritance/)
4
+ [![Codecov](https://codecov.io/gh/tutuf/updateable_views_inheritance/graph/badge.svg?token=L8P5LYNNU4)](https://codecov.io/gh/tutuf/updateable_views_inheritance)
5
+ [![DeepSource](https://app.deepsource.com/gh/tutuf/updateable_views_inheritance.svg/?label=active+issues&show_trend=true&token=AMfm8-_-qDZoknMh9-8IYp3R)](https://app.deepsource.com/gh/tutuf/updateable_views_inheritance/)
5
6
 
6
7
  Class Table Inheritance for ActiveRecord using updateable views
7
8
 
@@ -138,6 +139,31 @@ end
138
139
  ```
139
140
  Useful when converting legacy DB schema to use inheritance.
140
141
 
142
+ ### Using view as a child table
143
+
144
+ ```ruby
145
+ execute <<-SQL.squish
146
+ CREATE VIEW punk_locomotives_data AS (
147
+ SELECT steam_locomotives.id,
148
+ steam_locomotives.coal_consumption AS coal,
149
+ NULL AS electro
150
+ FROM steam_locomotives
151
+ UNION ALL
152
+ SELECT electric_locomotives.id,
153
+ NULL AS coal,
154
+ electric_locomotives.electricity_consumption AS electro
155
+ FROM electric_locomotives)
156
+ SQL
157
+ create_child(:punk_locomotives,
158
+ { parent: :locomotives,
159
+ child_table: :punk_locomotives_data,
160
+ child_table_pk: :id,
161
+ skip_creating_child_table: true })
162
+ ```
163
+ Views in PostgreSQL cannot have primary keys, so you have to manually specify it
164
+ when you use. Note that views also cannot have `NOT NULL` constraints, although
165
+ the `NOT NULL` constraint of the underlying table will still be enforced.
166
+
141
167
  ## Compatibility with Single Table Inheritance
142
168
 
143
169
  The approach of this gem is completely independent from Rails built-in Single
@@ -7,11 +7,16 @@ module ActiveRecord #:nodoc:
7
7
  # Use this in migration to create child table and view.
8
8
  # Options:
9
9
  # [:parent]
10
- # parent relation
10
+ # Parent relation
11
11
  # [:table]
12
- # default is <tt>"#{child_view}_data"</tt>
12
+ # Deprecated. Use :child_table instead
13
+ # [:child_table]
14
+ # Default is <tt>"#{child_view}_data"</tt>
15
+ # [:child_table_pk]
16
+ # Handy when :child_table is a view and PK cannot be inferred
17
+ # from the database.
13
18
  # [:skip_creating_child_table]
14
- # use together with :table option
19
+ # When given, :child_table option also must be specified
15
20
  def create_child(child_view, options)
16
21
  raise 'Please call me with a parent, for example: create_child(:steam_locomotives, :parent => :locomotives)' unless options[:parent]
17
22
 
@@ -26,7 +31,7 @@ module ActiveRecord #:nodoc:
26
31
  parent_relation
27
32
  end
28
33
 
29
- child_table = options[:table] || quote_table_name("#{child_view}_data")
34
+ child_table = options[:child_table] || options[:table] || quote_table_name("#{child_view}_data")
30
35
 
31
36
  unless options.key?(:skip_creating_child_table)
32
37
  unqualified_child_view_name = Utils.extract_schema_qualified_name(child_view).identifier
@@ -41,7 +46,9 @@ module ActiveRecord #:nodoc:
41
46
  REFERENCES #{parent_table} ON DELETE CASCADE ON UPDATE CASCADE"
42
47
  end
43
48
 
44
- create_child_view(parent_relation, child_view, child_table)
49
+ child_table_pk ||= options[:child_table_pk].to_s if options[:child_table_pk]
50
+
51
+ create_child_view(parent_relation, child_view, child_table, child_table_pk)
45
52
  end
46
53
 
47
54
  # Drop child view and table
@@ -54,7 +61,7 @@ module ActiveRecord #:nodoc:
54
61
 
55
62
  # Creates aggregate updateable view of parent and child relations. The convention for naming child tables is
56
63
  # <tt>"#{child_view}_data"</tt>. If you don't follow it, supply +child_table_name+ as third argument.
57
- def create_child_view(parent_table, child_view, child_table=nil)
64
+ def create_child_view(parent_table, child_view, child_table=nil, child_table_pk=nil)
58
65
  child_table ||= child_view.to_s + "_data"
59
66
 
60
67
  parent_columns = columns(parent_table)
@@ -63,7 +70,7 @@ module ActiveRecord #:nodoc:
63
70
  child_column_names = child_columns.collect{|c| c.name}
64
71
  parent_column_names = parent_columns.collect{|c| c.name}
65
72
 
66
- child_pk = pk_and_sequence_for(child_table)[0]
73
+ child_pk = child_table_pk || pk_and_sequence_for(child_table)[0]
67
74
  child_column_names.delete(child_pk)
68
75
 
69
76
  parent_pk, parent_pk_seq = pk_and_sequence_for(parent_table)
@@ -106,10 +113,12 @@ module ActiveRecord #:nodoc:
106
113
  res && res.first
107
114
  end
108
115
 
109
- # Returns a relation's primary key and belonging sequence. If +relation+ is a table the result is its PK and sequence.
110
- # When it is a view, PK and sequence of the table at the root of the inheritance chain are returned.
116
+ # Returns a relation's primary key and belonging sequence.
117
+ # If +relation+ is a table the result is its PK and sequence.
118
+ # When it is a view, PK and sequence of the table at the root
119
+ # of the inheritance chain are returned.
111
120
  def pk_and_sequence_for(relation)
112
- result = query(<<-SQL, 'PK')[0]
121
+ result = query(<<-SQL.squish, 'PK')[0]
113
122
  SELECT attr.attname
114
123
  FROM pg_attribute attr,
115
124
  pg_constraint cons
@@ -118,15 +127,13 @@ module ActiveRecord #:nodoc:
118
127
  AND cons.contype = 'p'
119
128
  AND attr.attnum = ANY(cons.conkey)
120
129
  SQL
121
- if result.nil? or result.empty?
130
+ if result.blank? #result.empty?
122
131
  parent = parent_table(relation)
123
132
  pk_and_sequence_for(parent) if parent
124
133
  else
125
134
  # log(result[0], "PK for #{relation}") {}
126
135
  [result[0], query("SELECT pg_get_serial_sequence('#{relation}', '#{result[0]}') ")[0][0]]
127
136
  end
128
- rescue
129
- nil
130
137
  end
131
138
 
132
139
  # Drops a view from the database.
@@ -305,7 +312,7 @@ module ActiveRecord #:nodoc:
305
312
  def do_create_child_view(parent_table, parent_columns, parent_pk, child_view, child_columns, child_pk, child_table)
306
313
  view_columns = parent_columns + child_columns
307
314
  execute(<<~SQL)
308
- CREATE OR REPLACE VIEW #{quote_column_name(child_view)} AS (
315
+ CREATE OR REPLACE VIEW #{quote_table_name(child_view)} AS (
309
316
  SELECT parent.#{parent_pk},
310
317
  #{ view_columns.map { |col| quote_column_name(col) }.join(",") }
311
318
  FROM #{parent_table} parent
@@ -322,7 +329,7 @@ module ActiveRecord #:nodoc:
322
329
  # Setting the sequence to its value (explicitly supplied or the default) covers both cases.
323
330
  execute(<<~SQL)
324
331
  CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_insert")} AS
325
- ON INSERT TO #{quote_column_name(child_view)} DO INSTEAD (
332
+ ON INSERT TO #{quote_table_name(child_view)} DO INSTEAD (
326
333
  INSERT INTO #{parent_table}
327
334
  ( #{ [parent_pk, parent_columns].flatten.map { |col| quote_column_name(col) }.join(", ") } )
328
335
  VALUES( DEFAULT #{ parent_columns.empty? ? '' : ' ,' + parent_columns.collect{ |col| "NEW.#{quote_column_name(col)}" }.join(", ") } ) ;
@@ -336,14 +343,14 @@ module ActiveRecord #:nodoc:
336
343
  # delete
337
344
  execute(<<~SQL)
338
345
  CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_delete")} AS
339
- ON DELETE TO #{quote_column_name(child_view)} DO INSTEAD
346
+ ON DELETE TO #{quote_table_name(child_view)} DO INSTEAD
340
347
  DELETE FROM #{parent_table} WHERE #{parent_pk} = OLD.#{parent_pk}
341
348
  SQL
342
349
 
343
350
  # update
344
351
  execute(<<~SQL)
345
352
  CREATE OR REPLACE RULE #{quote_column_name("#{child_view}_update")} AS
346
- ON UPDATE TO #{quote_column_name(child_view)} DO INSTEAD (
353
+ ON UPDATE TO #{quote_table_name(child_view)} DO INSTEAD (
347
354
  #{ if parent_columns.empty?
348
355
  ''
349
356
  else
@@ -364,9 +371,9 @@ module ActiveRecord #:nodoc:
364
371
 
365
372
  def insert_returning_clause(parent_pk, child_pk, child_view)
366
373
  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(", ")
374
+ .reject { |c| c.name == parent_pk }
375
+ .map { |c| "CAST (NULL AS #{c.sql_type})" }
376
+ .join(", ")
370
377
  "RETURNING #{child_pk}, #{columns_cast_to_null}"
371
378
  end
372
379
 
@@ -1,3 +1,3 @@
1
1
  module UpdateableViewsInheritance
2
- VERSION = "1.4.4"
2
+ VERSION = "1.4.6"
3
3
  end
@@ -5,15 +5,15 @@ class CreateWithDefaultTable < ActiveRecord::Migration
5
5
  t.column :max_speed, :integer
6
6
  t.column :type, :string
7
7
  end
8
-
9
- create_child(:steam_locomotives, :parent => :locomotives) do |t|
10
- t.decimal :water_consumption, :precision => 6, :scale => 2
11
- t.decimal :coal_consumption, :precision => 6, :scale => 2
8
+
9
+ create_child(:steam_locomotives, parent: :locomotives) do |t|
10
+ t.decimal :water_consumption, precision: 6, scale: 2, null: false
11
+ t.decimal :coal_consumption, precision: 6, scale: 2, null: false
12
12
  end
13
13
  end
14
-
14
+
15
15
  def self.down
16
16
  drop_child :steam_locomotives
17
17
  drop_table :locomotives
18
18
  end
19
- end
19
+ end
data/test/schema_test.rb CHANGED
@@ -80,6 +80,10 @@ class UpdateableViewsInheritanceSchemaTest < ActiveSupport::TestCase
80
80
  assert RackLocomotive.new.narrow_gauge
81
81
  end
82
82
 
83
+ def test_does_not_preserve_not_null_on_views
84
+ assert SteamLocomotive.columns.find { |c| c.name == 'water_consumption' }.null
85
+ end
86
+
83
87
  class ChangeDefaultValueOfColumn < ActiveRecord::Migration
84
88
  def self.up
85
89
  remove_parent_and_children_views(:rack_locomotives)
@@ -184,22 +188,22 @@ class UpdateableViewsInheritanceSchemaTest < ActiveSupport::TestCase
184
188
  t.column :interrail_max_speed, :integer
185
189
  t.column :type, :string
186
190
  end
187
- create_child('interrail.steam_locomotives', :parent => 'interrail.locomotives') do |t|
188
- t.decimal :interrail_water_consumption, :precision => 6, :scale => 2
189
- t.decimal :interrail_coal_consumption, :precision => 6, :scale => 2
191
+ create_child('interrail.steam_locomotives', parent: 'interrail.locomotives') do |t|
192
+ t.decimal :interrail_water_consumption, precision: 6, scale: 2
193
+ t.decimal :interrail_coal_consumption, precision: 6, scale: 2
190
194
  end
191
195
  end
192
196
  end
193
197
 
194
198
  def test_create_child_in_schema
195
199
  CreateChildInSchema.up
196
- assert_equal %w(id
200
+ assert_equal %w[id
197
201
  interrail_coal_consumption
198
202
  interrail_max_speed
199
203
  interrail_name
200
204
  interrail_water_consumption
201
- type),
202
- @connection.columns('interrail.steam_locomotives').map{ |c| c.name }.sort
205
+ type],
206
+ @connection.columns('interrail.steam_locomotives').map(&:name).sort
203
207
  end
204
208
 
205
209
  class ChangeTablesInTwoInheritanceChains < ActiveRecord::Migration
@@ -251,4 +255,31 @@ class UpdateableViewsInheritanceSchemaTest < ActiveSupport::TestCase
251
255
  ReservedSQLWords.up
252
256
  assert @connection.columns(:table).map(&:name).include?("column")
253
257
  end
258
+
259
+ class ChildTableIsActuallyView < ActiveRecord::Migration
260
+ def self.up
261
+ execute <<-SQL.squish
262
+ CREATE VIEW punk_locomotives_data AS (
263
+ SELECT steam_locomotives.id,
264
+ steam_locomotives.coal_consumption AS coal,
265
+ NULL AS electro
266
+ FROM steam_locomotives
267
+ UNION ALL
268
+ SELECT electric_locomotives.id,
269
+ NULL AS coal,
270
+ electric_locomotives.electricity_consumption AS electro
271
+ FROM electric_locomotives)
272
+ SQL
273
+ create_child(:punk_locomotives,
274
+ { parent: :locomotives,
275
+ child_table: :punk_locomotives_data,
276
+ child_table_pk: :id,
277
+ skip_creating_child_table: true })
278
+ end
279
+ end
280
+
281
+ def test_child_table_is_view
282
+ ChildTableIsActuallyView.up
283
+ assert @connection.columns(:punk_locomotives).map(&:name).sort == %w(coal electro id max_speed name type)
284
+ end
254
285
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: updateable_views_inheritance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.4
4
+ version: 1.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sava Chankov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-10-09 00:00:00.000000000 Z
12
+ date: 2024-10-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -271,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
271
271
  - !ruby/object:Gem::Version
272
272
  version: '0'
273
273
  requirements: []
274
- rubygems_version: 3.4.12
274
+ rubygems_version: 3.1.6
275
275
  signing_key:
276
276
  specification_version: 4
277
277
  summary: Class table inheritance for ActiveRecord