torque-postgresql 2.1.3 → 2.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09e539b25b952fb4d48bd092a90bae032ad8c26f1584cc5a704cdf96985b1e7e'
4
- data.tar.gz: d79aa047ac44dc47a25979f71a0b46dfcbdc573f28d18bfcccbcb59b0a693c7b
3
+ metadata.gz: cb3d81538edfb988d8566295fac7d055208e399d552b619d9d8b3b9234254686
4
+ data.tar.gz: 7c7c62bec966085eccbdc351041a94d04788e40f84b1120d88c1673c9d0e3b3c
5
5
  SHA512:
6
- metadata.gz: e2f75f66a5e0bed5f14006ff4ffd8e32f96bbe17f18ff4603c0706301c87114a1880b1a8f7bd530501d95c8f6dc10c563af6da5a2ba5f4414a31115c5510ca0f
7
- data.tar.gz: 930b6381e8e920a274c411c2ba8abfe96a5b8462227c51dbf5ee9f2a41ac88b6f3a3946d4e49cab0017bb271c176d58c37690e5e646b8536476b83a1cba5c62b
6
+ metadata.gz: 93cf49b971a0013d3f10263917437b7a552632e62f462d43b91aaad86ec66ba10b20a014bf2bbcaa883f1c6801df423fd3d98dd8d9546bd849920bb98b743e6c
7
+ data.tar.gz: 88cf63c02e13e80e48b5dfbe66e9157a52ecc8cc11410f0688e97a6c28e7f25a03adc9c7b71a45ffe9384a81debbf1a29d55363145a4ae6568c82cc9f7207a30
@@ -42,6 +42,8 @@ module Torque
42
42
  m.register_type 'interval', OID::Interval.new
43
43
  m.register_type 'line', OID::Line.new
44
44
  m.register_type 'segment', OID::Segment.new
45
+
46
+ m.alias_type 'regclass', 'varchar'
45
47
  end
46
48
 
47
49
  # :nodoc:
@@ -20,7 +20,7 @@ module Torque
20
20
  end
21
21
 
22
22
  def quote_default_expression(value, column)
23
- if value.class <= Array
23
+ if column.dig(:options, :array) && value.class <= Array
24
24
  quote(value) + '::' + column.sql_type
25
25
  else
26
26
  super
@@ -15,7 +15,14 @@ module Torque
15
15
  include DatabaseStatements
16
16
  include SchemaStatements
17
17
 
18
- INJECT_WHERE_REGEX = /(DO UPDATE SET.*excluded\.[^ ]+) RETURNING/.freeze
18
+ # :nodoc:
19
+ class DeduplicatableArray < ::Array
20
+ def deduplicate
21
+ map { |value| -value }
22
+ end
23
+
24
+ alias :-@ :deduplicate
25
+ end
19
26
 
20
27
  # Get the current PostgreSQL version as a Gem Version.
21
28
  def version
@@ -29,19 +36,28 @@ module Torque
29
36
  super.merge(options.extract!(:inherits))
30
37
  end
31
38
 
32
- # Allow filtered bulk insert by adding the where clause. This method is only used by
33
- # +InsertAll+, so it somewhat safe to override it
39
+ # Allow filtered bulk insert by adding the where clause. This method is
40
+ # only used by +InsertAll+, so it somewhat safe to override it
34
41
  def build_insert_sql(insert)
35
42
  super.tap do |sql|
36
43
  if insert.update_duplicates? && insert.where_condition?
37
44
  if insert.returning
38
- sql.gsub!(INJECT_WHERE_REGEX, "\\1 WHERE #{insert.where} RETURNING")
45
+ sql.sub!(' RETURNING ', " WHERE #{insert.where} RETURNING ")
39
46
  else
40
47
  sql << " WHERE #{insert.where}"
41
48
  end
42
49
  end
43
50
  end
44
51
  end
52
+
53
+ # Extend the extract default value to support array
54
+ def extract_value_from_default(default)
55
+ return super unless Torque::PostgreSQL.config.use_extended_defaults
56
+ return super unless default&.match(/ARRAY\[(.*?)\](?:::"?([\w. ]+)"?(?:\[\])+)?$/)
57
+
58
+ arr = $1.split(/(?!\B\[[^\]]*), ?(?![^\[]*\]\B)/)
59
+ DeduplicatableArray.new(arr.map(&method(:extract_value_from_default)))
60
+ end
45
61
  end
46
62
 
47
63
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend Adapter
@@ -41,8 +41,8 @@ module Torque
41
41
  end
42
42
  end
43
43
 
44
- if PostgreSQL::AR610
45
- # This is how Rails 6.1 now load the records
44
+ if PostgreSQL::AR604
45
+ # This is how Rails 6.0.4 and 6.1 now load the records
46
46
  def load_records
47
47
  return super unless connected_through_array?
48
48
 
@@ -452,12 +452,8 @@ module Torque
452
452
  def instance_current_on?
453
453
  attr_value = threshold.present? ? method_names[:real] : attribute
454
454
  default_value = default.inspect
455
- [
456
- "return #{default_value} if #{attr_value}.nil?",
457
- "return #{default_value} if #{attr_value}.min.try(:infinite?)",
458
- "return #{default_value} if #{attr_value}.max.try(:infinite?)",
459
- "#{attr_value}.min < value && #{attr_value}.max > value",
460
- ].join("\n")
455
+
456
+ "#{attr_value}.nil? ? #{default_value} : #{attr_value}.include?(value)"
461
457
  end
462
458
 
463
459
  def instance_start
@@ -5,6 +5,7 @@ module Torque
5
5
  include ActiveSupport::Configurable
6
6
 
7
7
  # Stores a version check for compatibility purposes
8
+ AR604 = (ActiveRecord.gem_version >= Gem::Version.new('6.0.4'))
8
9
  AR610 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.0'))
9
10
 
10
11
  # Use the same logger as the Active Record one
@@ -25,6 +26,11 @@ module Torque
25
26
  # same configuration is set to true
26
27
  config.eager_load = false
27
28
 
29
+ # This allows default values to have extended values like arrays and casted
30
+ # values. Extended defaults are still experimental, so enable and test it
31
+ # before using it in prod
32
+ config.use_extended_defaults = false
33
+
28
34
  # Set a list of irregular model name when associated with table names
29
35
  config.irregular_models = {}
30
36
  def config.irregular_models=(hash)
@@ -146,7 +146,7 @@ module Torque
146
146
  auto_cast = _auto_cast_attribute.to_s
147
147
  record_class = _record_class_attribute.to_s
148
148
  return super unless attributes.key?(record_class) &&
149
- attributes.delete(auto_cast) && attributes[record_class] != table_name
149
+ attributes.delete(auto_cast) && attributes[record_class] != table_name
150
150
 
151
151
  klass = casted_dependents[attributes[record_class]]
152
152
  raise_unable_to_cast(attributes[record_class]) if klass.nil?
@@ -56,8 +56,10 @@ module Torque
56
56
  end
57
57
 
58
58
  ::ActiveRecord::Reflection.const_set(:BelongsToManyReflection, BelongsToManyReflection)
59
- ::ActiveRecord::Reflection::AssociationReflection::VALID_AUTOMATIC_INVERSE_MACROS
60
- .push(:belongs_to_many)
59
+
60
+ reflection_class = ::ActiveRecord::Reflection::AssociationReflection
61
+ reflection_class::VALID_AUTOMATIC_INVERSE_MACROS.push(:belongs_to_many) \
62
+ if reflection_class.const_defined?('VALID_AUTOMATIC_INVERSE_MACROS')
61
63
  end
62
64
  end
63
65
  end
@@ -5,6 +5,8 @@ module Torque
5
5
  module Relation
6
6
  module Inheritance
7
7
 
8
+ # REGCLASS = ::Arel.sql('tableoid').cast('regclass')
9
+
8
10
  # :nodoc:
9
11
  def cast_records_value; get_value(:cast_records); end
10
12
  # :nodoc:
@@ -44,14 +46,8 @@ module Torque
44
46
 
45
47
  # Like #cast_records, but modifies relation in place
46
48
  def cast_records!(*types, **options)
47
- record_class = self.class._record_class_attribute
48
-
49
- with!(record_class)
50
- if options[:filter]
51
- table = record_class.to_s.camelize.underscore
52
- where!(table => { record_class => types.map(&:table_name) })
53
- end
54
-
49
+ where!(regclass.cast(:varchar).in(types.map(&:table_name))) if options[:filter]
50
+ self.select_extra_values += [regclass.as(_record_class_attribute.to_s)]
55
51
  self.cast_records_value = (types.present? ? types : model.casted_dependents.values)
56
52
  self
57
53
  end
@@ -109,13 +105,12 @@ module Torque
109
105
  end
110
106
 
111
107
  def build_auto_caster_marker(arel, types)
112
- types = types.map(&:table_name)
113
- type_attribute = self.class._record_class_attribute.to_s
114
- auto_cast_attribute = self.class._auto_cast_attribute.to_s
108
+ attribute = regclass.cast(:varchar).in(types.map(&:table_name))
109
+ attribute.as(self.class._auto_cast_attribute.to_s)
110
+ end
115
111
 
116
- table = ::Arel::Table.new(type_attribute.camelize.underscore)
117
- column = table[type_attribute].in(types)
118
- ::Arel.sql(column.to_sql).as(auto_cast_attribute)
112
+ def regclass
113
+ arel_table['tableoid'].cast(:regclass)
119
114
  end
120
115
 
121
116
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- VERSION = '2.1.3'
5
+ VERSION = '2.2.0'
6
6
  end
7
7
  end
data/lib/torque/range.rb CHANGED
@@ -17,6 +17,4 @@ module Torque
17
17
  end
18
18
  alias_method :|, :union
19
19
  end
20
-
21
- ::Range.include(Range)
22
20
  end
@@ -48,6 +48,36 @@ RSpec.describe 'Arel' do
48
48
  end
49
49
  end
50
50
 
51
+ context 'on default value' do
52
+ let(:connection) { ActiveRecord::Base.connection }
53
+
54
+ before(:context) { Torque::PostgreSQL.config.use_extended_defaults = true }
55
+ after(:context) { Torque::PostgreSQL.config.use_extended_defaults = false }
56
+ after { Author.reset_column_information }
57
+
58
+ it 'does not break jsonb' do
59
+ expect { connection.add_column(:authors, :profile, :jsonb, default: []) }.not_to raise_error
60
+ expect(Author.columns_hash['profile'].default).to eq('[]')
61
+ end
62
+
63
+ it 'works properly when column is an array' do
64
+ expect { connection.add_column(:authors, :tag_ids, :bigint, array: true, default: []) }.not_to raise_error
65
+ expect(Author.columns_hash['tag_ids'].default).to eq([])
66
+ end
67
+
68
+ it 'works with an array with enum values' do
69
+ value = ['visitor', 'assistant']
70
+ expect { connection.add_column(:authors, :roles, :roles, array: true, default: value) }.not_to raise_error
71
+ expect(Author.columns_hash['roles'].default).to eq(value)
72
+ end
73
+
74
+ it 'works with multi dimentional array' do
75
+ value = [['1', '2'], ['3', '4']]
76
+ expect { connection.add_column(:authors, :tag_ids, :string, array: true, default: value) }.not_to raise_error
77
+ expect(Author.columns_hash['tag_ids'].default).to eq(value)
78
+ end
79
+ end
80
+
51
81
  context 'on cast' do
52
82
  it 'provides an array method' do
53
83
  sample1 = ::Arel.array(1, 2, 3, 4)
@@ -266,6 +266,15 @@ RSpec.describe 'Period' do
266
266
 
267
267
  instance.period = 4.hour.from_now.utc..6.hour.from_now.utc
268
268
  expect(instance).not_to be_current_period
269
+
270
+ instance.period = [nil, 4.hours.ago.utc]
271
+ expect(instance).not_to be_current_period
272
+
273
+ instance.period = [4.hours.from_now.utc, nil]
274
+ expect(instance).not_to be_current_period
275
+
276
+ instance.period = [nil, nil]
277
+ expect(instance).to be_current_period
269
278
  end
270
279
 
271
280
  it 'checks fro current based on a value' do
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe 'Range' do
3
+ RSpec.xdescribe 'Range' do
4
4
  let(:sample) { (5..15) }
5
5
 
6
6
  it 'has intersection' do
@@ -292,14 +292,12 @@ RSpec.describe 'TableInheritance' do
292
292
  end
293
293
 
294
294
  it 'adds all statements to load all the necessary records' do
295
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
296
- result << ' SELECT "activities".*, "record_class"."_record_class", "i_0"."description"'
295
+ result = 'SELECT "activities".*, "activities"."tableoid"::regclass AS _record_class, "i_0"."description"'
297
296
  result << ', COALESCE("i_0"."url", "i_1"."url", "i_2"."url") AS url, "i_0"."activated" AS activity_books__activated'
298
297
  result << ', "i_1"."activated" AS activity_posts__activated, "i_2"."activated" AS activity_post_samples__activated'
299
298
  result << ', COALESCE("i_1"."file", "i_2"."file") AS file, COALESCE("i_1"."post_id", "i_2"."post_id") AS post_id'
300
- result << ", \"record_class\".\"_record_class\" IN ('activity_books', 'activity_posts', 'activity_post_samples') AS _auto_cast"
299
+ result << ", \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books', 'activity_posts', 'activity_post_samples') AS _auto_cast"
301
300
  result << ' FROM "activities"'
302
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
303
301
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
304
302
  result << ' LEFT OUTER JOIN "activity_posts" "i_1" ON "activities"."id" = "i_1"."id"'
305
303
  result << ' LEFT OUTER JOIN "activity_post_samples" "i_2" ON "activities"."id" = "i_2"."id"'
@@ -307,33 +305,27 @@ RSpec.describe 'TableInheritance' do
307
305
  end
308
306
 
309
307
  it 'can be have simplefied joins' do
310
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
311
- result << ' SELECT "activities".*, "record_class"."_record_class"'
308
+ result = 'SELECT "activities".*, "activities"."tableoid"::regclass AS _record_class'
312
309
  result << ', "i_0"."description", "i_0"."url", "i_0"."activated"'
313
- result << ", \"record_class\".\"_record_class\" IN ('activity_books') AS _auto_cast"
310
+ result << ", \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books') AS _auto_cast"
314
311
  result << ' FROM "activities"'
315
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
316
312
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
317
313
  expect(base.cast_records(child).all.to_sql).to eql(result)
318
314
  end
319
315
 
320
316
  it 'can be filtered by record type' do
321
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
322
- result << ' SELECT "activities".*, "record_class"."_record_class"'
317
+ result = 'SELECT "activities".*, "activities"."tableoid"::regclass AS _record_class'
323
318
  result << ', "i_0"."description", "i_0"."url", "i_0"."activated"'
324
- result << ", \"record_class\".\"_record_class\" IN ('activity_books') AS _auto_cast"
319
+ result << ", \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books') AS _auto_cast"
325
320
  result << ' FROM "activities"'
326
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
327
321
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
328
- result << " WHERE \"record_class\".\"_record_class\" = 'activity_books'"
322
+ result << " WHERE \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books')"
329
323
  expect(base.cast_records(child, filter: true).all.to_sql).to eql(result)
330
324
  end
331
325
 
332
326
  it 'works with count and does not add extra columns' do
333
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
334
- result << ' SELECT COUNT(*)'
327
+ result = 'SELECT COUNT(*)'
335
328
  result << ' FROM "activities"'
336
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
337
329
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
338
330
  result << ' LEFT OUTER JOIN "activity_posts" "i_1" ON "activities"."id" = "i_1"."id"'
339
331
  result << ' LEFT OUTER JOIN "activity_post_samples" "i_2" ON "activities"."id" = "i_2"."id"'
@@ -342,10 +334,8 @@ RSpec.describe 'TableInheritance' do
342
334
  end
343
335
 
344
336
  it 'works with sum and does not add extra columns' do
345
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
346
- result << ' SELECT SUM("activities"."id")'
337
+ result = 'SELECT SUM("activities"."id")'
347
338
  result << ' FROM "activities"'
348
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
349
339
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
350
340
  result << ' LEFT OUTER JOIN "activity_posts" "i_1" ON "activities"."id" = "i_1"."id"'
351
341
  result << ' LEFT OUTER JOIN "activity_post_samples" "i_2" ON "activities"."id" = "i_2"."id"'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torque-postgresql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-16 00:00:00.000000000 Z
11
+ date: 2021-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails