torque-postgresql 2.2.3 → 3.0.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/torque/postgresql/adapter/database_statements.rb +12 -1
  3. data/lib/torque/postgresql/adapter/oid/enum_set.rb +1 -1
  4. data/lib/torque/postgresql/adapter/oid.rb +0 -3
  5. data/lib/torque/postgresql/adapter/quoting.rb +12 -20
  6. data/lib/torque/postgresql/adapter/schema_creation.rb +1 -2
  7. data/lib/torque/postgresql/adapter/schema_definitions.rb +0 -37
  8. data/lib/torque/postgresql/adapter/schema_dumper.rb +0 -40
  9. data/lib/torque/postgresql/adapter/schema_statements.rb +0 -15
  10. data/lib/torque/postgresql/adapter.rb +0 -9
  11. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +5 -4
  12. data/lib/torque/postgresql/associations/{association.rb → foreign_association.rb} +1 -4
  13. data/lib/torque/postgresql/associations/preloader/association.rb +53 -26
  14. data/lib/torque/postgresql/associations/preloader/loader_query.rb +36 -0
  15. data/lib/torque/postgresql/associations/preloader.rb +1 -0
  16. data/lib/torque/postgresql/associations.rb +6 -1
  17. data/lib/torque/postgresql/attributes/builder/period.rb +6 -2
  18. data/lib/torque/postgresql/config.rb +2 -12
  19. data/lib/torque/postgresql/reflection/abstract_reflection.rb +5 -7
  20. data/lib/torque/postgresql/relation.rb +10 -12
  21. data/lib/torque/postgresql/schema_cache.rb +2 -1
  22. data/lib/torque/postgresql/version.rb +1 -1
  23. data/lib/torque-postgresql.rb +0 -1
  24. data/spec/schema.rb +16 -15
  25. data/spec/spec_helper.rb +0 -4
  26. data/spec/tests/arel_spec.rb +17 -8
  27. data/spec/tests/belongs_to_many_spec.rb +2 -1
  28. data/spec/tests/enum_set_spec.rb +5 -4
  29. data/spec/tests/enum_spec.rb +0 -84
  30. metadata +7 -9
  31. data/lib/torque/range.rb +0 -20
  32. data/spec/tests/range_spec.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e9379e388022831a9441cb6b3b646ff8bdea7340cba61f0dd197bcdbf589f72
4
- data.tar.gz: cfb88b94086027202e92a2120ad8ba1a5e939ef089aea5dcdfd85e05a7d122ea
3
+ metadata.gz: d4fac19ef8680f477df0f79502331ddda06266658a54c8128321d68145a9f18b
4
+ data.tar.gz: 0e93ec49f80d40ec9ce9b7fcc5ef0eff65a882cad79a63e5429657ad9c66691b
5
5
  SHA512:
6
- metadata.gz: 809642eb8f6ee574e7b3b9557590baa58624fec54d1c69b6f341999dfc4217aea5010abc1b438119b3213f958918ce00ddd1af1df3a4714321b1f6153f8ee57d
7
- data.tar.gz: e81cf089a977d964326548144b14dd4ab7ce4be0612f2e923e34add1aa951aa2e5467daaa544da42ac17773fb2cc047335a297f3807a1e72f8dc1942f1df4195
6
+ metadata.gz: 4a9abd492b544296c29e0949e1ad0ad4852a1ed0c48379551e5ffbbaad7018579cad994ac2a841231603bdddd8da4b448d7771a9086312289f818124d842d6a6
7
+ data.tar.gz: 8019255d20eff471ed177078e1bd6042a0a9b95752d32578bdb8ce86afdf9cffbefa8ed181caab0143f0dbbf80cfc39c99fd005cfc2b28089687b26a635268ef
@@ -34,6 +34,15 @@ module Torque
34
34
  execute("SET SESSION IntervalStyle TO 'iso_8601'", 'SCHEMA')
35
35
  end
36
36
 
37
+ # Since enums create new types, type map needs to be rebooted to include
38
+ # the new ones, both normal and array one
39
+ def create_enum(name, *)
40
+ super
41
+
42
+ oid = query_value("SELECT #{quote(name)}::regtype::oid", "SCHEMA").to_i
43
+ load_additional_types([oid])
44
+ end
45
+
37
46
  # Change some of the types being mapped
38
47
  def initialize_type_map(m = type_map)
39
48
  super
@@ -54,7 +63,7 @@ module Torque
54
63
 
55
64
  # Add the composite types to be loaded too.
56
65
  def torque_load_additional_types(oids = nil)
57
- filter = "AND a.typelem::integer IN (%s)" % oids.join(", ") if oids
66
+ filter = ("AND a.typelem::integer IN (%s)" % oids.join(', ')) if oids
58
67
 
59
68
  query = <<-SQL
60
69
  SELECT a.typelem AS oid, t.typname, t.typelem,
@@ -132,7 +141,9 @@ module Torque
132
141
  # Get the list of columns, and their definition, but only from the
133
142
  # actual table, does not include columns that comes from inherited table
134
143
  def column_definitions(table_name) # :nodoc:
144
+ # Only affects inheritance
135
145
  local_condition = 'AND a.attislocal IS TRUE' if @_dump_mode
146
+
136
147
  query(<<-SQL, 'SCHEMA')
137
148
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
138
149
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
@@ -14,7 +14,7 @@ module Torque
14
14
  end
15
15
 
16
16
  def type
17
- :enum_set
17
+ :enum
18
18
  end
19
19
 
20
20
  def deserialize(value)
@@ -19,9 +19,6 @@ module Torque
19
19
  ActiveRecord::Type.register(:enum_set, OID::EnumSet, adapter: :postgresql)
20
20
  ActiveRecord::Type.register(:line, OID::Line, adapter: :postgresql)
21
21
  ActiveRecord::Type.register(:segment, OID::Segment, adapter: :postgresql)
22
-
23
- ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql) \
24
- unless PostgreSQL::AR610
25
22
  end
26
23
  end
27
24
  end
@@ -22,27 +22,19 @@ module Torque
22
22
  end
23
23
 
24
24
  def quote_default_expression(value, column)
25
- return super unless value.class <= Array &&
26
- ((column.is_a?(ColumnDefinition) && column.dig(:options, :array)) ||
27
- (column.is_a?(Column) && column.array?))
28
-
29
- type = column.is_a?(Column) ? column.sql_type_metadata.sql_type : column.sql_type
30
- quote(value) + '::' + type
25
+ return super unless value.class <= Array || value.class <= Set
26
+
27
+ type =
28
+ if column.is_a?(ColumnDefinition) && column.options.try(:[], :array)
29
+ # This is the general way
30
+ lookup_cast_type(column.sql_type)
31
+ elsif column.is_a?(Column) && column.array?
32
+ # When using +change_column_default+
33
+ lookup_cast_type_from_column(column)
34
+ end
35
+
36
+ type.nil? ? super : quote(type.serialize(value.to_a))
31
37
  end
32
-
33
- private
34
-
35
- def _quote(value)
36
- return super unless value.is_a?(Array)
37
-
38
- values = value.map(&method(:quote))
39
- "ARRAY[#{values.join(','.freeze)}]"
40
- end
41
-
42
- def _type_cast(value)
43
- return super unless value.is_a?(Array)
44
- value.map(&method(:quote)).join(','.freeze)
45
- end
46
38
  end
47
39
  end
48
40
  end
@@ -33,8 +33,7 @@ module Torque
33
33
  create_sql << "(#{statements.join(', ')})" \
34
34
  if statements.present? || o.inherits.present?
35
35
 
36
- options = PostgreSQL::AR610 ? o : table_options(o)
37
- add_table_options!(create_sql, options)
36
+ add_table_options!(create_sql, o)
38
37
 
39
38
  if o.inherits.present?
40
39
  tables = o.inherits.map(&method(:quote_table_name))
@@ -3,35 +3,7 @@
3
3
  module Torque
4
4
  module PostgreSQL
5
5
  module Adapter
6
- module ColumnMethods
7
-
8
- # Creates a column with an interval type, allowing span of times and
9
- # dates to be stored without having to store a seconds-based integer
10
- # or any sort of other approach
11
- def interval(*args, **options)
12
- args.each { |name| column(name, :interval, **options) }
13
- end
14
-
15
- # Creates a column with an enum type, needing to specify the subtype,
16
- # which is basically the name of the type defined prior creating the
17
- # column
18
- def enum(*args, **options)
19
- subtype = options.delete(:subtype)
20
- args.each { |name| column(name, (subtype || name), **options) }
21
- end
22
-
23
- # Creates a column with an enum array type, needing to specify the
24
- # subtype, which is basically the name of the type defined prior
25
- # creating the column
26
- def enum_set(*args, **options)
27
- super(*args, **options.merge(array: true))
28
- end
29
-
30
- end
31
-
32
6
  module TableDefinition
33
- include ColumnMethods
34
-
35
7
  attr_reader :inherits
36
8
 
37
9
  def initialize(*args, **options)
@@ -42,16 +14,7 @@ module Torque
42
14
  end
43
15
  end
44
16
 
45
- ActiveRecord::ConnectionAdapters::PostgreSQL::Table.include ColumnMethods
46
17
  ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.include TableDefinition
47
-
48
- if ActiveRecord::ConnectionAdapters::PostgreSQL.const_defined?('ColumnDefinition')
49
- module ColumnDefinition
50
- attr_accessor :subtype
51
- end
52
-
53
- ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition.include ColumnDefinition
54
- end
55
18
  end
56
19
  end
57
20
  end
@@ -12,33 +12,13 @@ module Torque
12
12
  stream
13
13
  end
14
14
 
15
- def extensions(stream) # :nodoc:
16
- super
17
- user_defined_types(stream)
18
- end
19
-
20
15
  # Translate +:enum_set+ into +:enum+
21
16
  def schema_type(column)
22
17
  column.type == :enum_set ? :enum : super
23
18
  end
24
19
 
25
- # Adds +:subtype+ option to the default set
26
- def prepare_column_options(column)
27
- spec = super
28
-
29
- if subtype = schema_subtype(column)
30
- spec[:subtype] = subtype
31
- end
32
-
33
- spec
34
- end
35
-
36
20
  private
37
21
 
38
- def schema_subtype(column)
39
- column.sql_type.to_sym.inspect if column.type == :enum || column.type == :enum_set
40
- end
41
-
42
22
  def tables(stream) # :nodoc:
43
23
  inherited_tables = @connection.inherited_tables
44
24
  sorted_tables = @connection.tables.sort - @connection.views
@@ -83,26 +63,6 @@ module Torque
83
63
  functions(stream) if defined?(::Fx::SchemaDumper::Function)
84
64
  triggers(stream) if defined?(::Fx::SchemaDumper::Trigger)
85
65
  end
86
-
87
- # Dump user defined types like enum
88
- def user_defined_types(stream)
89
- types = @connection.user_defined_types('e')
90
- return unless types.any?
91
-
92
- stream.puts " # These are user-defined types used on this database"
93
- types.sort_by(&:first).each { |(name, type)| send(type.to_sym, name, stream) }
94
- stream.puts
95
- rescue => e
96
- stream.puts "# Could not dump user-defined types because of following #{e.class}"
97
- stream.puts "# #{e.message}"
98
- stream.puts
99
- end
100
-
101
- # Dump enum custom type
102
- def enum(name, stream)
103
- values = @connection.enum_values(name).map { |v| "\"#{v}\"" }
104
- stream.puts " create_enum \"#{name}\", [#{values.join(', ')}], force: :cascade"
105
- end
106
66
  end
107
67
 
108
68
  ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend SchemaDumper
@@ -25,21 +25,6 @@ module Torque
25
25
  SQL
26
26
  end
27
27
 
28
- # Creates a new PostgreSQL enumerator type
29
- #
30
- # Example:
31
- # create_enum 'status', ['foo', 'bar']
32
- # create_enum 'status', ['foo', 'bar'], prefix: true
33
- # create_enum 'status', ['foo', 'bar'], suffix: 'test'
34
- # create_enum 'status', ['foo', 'bar'], force: true
35
- def create_enum(name, values, options = {})
36
- drop_type(name, options) if options[:force]
37
- execute <<-SQL.squish
38
- CREATE TYPE #{quote_type_name(name, options[:schema])} AS ENUM
39
- (#{quote_enum_values(name, values, options).join(', ')})
40
- SQL
41
- end
42
-
43
28
  # Changes the enumerator by adding new values
44
29
  #
45
30
  # Example:
@@ -49,15 +49,6 @@ module Torque
49
49
  end
50
50
  end
51
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
61
52
  end
62
53
 
63
54
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend Adapter
@@ -53,8 +53,8 @@ module Torque
53
53
 
54
54
  def load_target
55
55
  if stale_target? || find_target?
56
- new_records = PostgreSQL::AR615 ? target.extract!(&:persisted?) : []
57
- @target = merge_target_lists((find_target || []) + new_records, target)
56
+ persisted_records = (find_target || []) + target.extract!(&:persisted?)
57
+ @target = merge_target_lists(persisted_records, target)
58
58
  end
59
59
 
60
60
  loaded!
@@ -228,8 +228,9 @@ module Torque
228
228
  end
229
229
 
230
230
  def invertible_for?(record)
231
- inverse = inverse_reflection_for(record)
232
- inverse && (inverse.has_many? && inverse.connected_through_array?)
231
+ return unless (inverse = inverse_reflection_for(record))
232
+ collection_class = ::ActiveRecord::Associations::HasManyAssociation
233
+ inverse.is_a?(collection_class) && inverse.connected_through_array?
233
234
  end
234
235
 
235
236
  def stale_state
@@ -3,7 +3,7 @@
3
3
  module Torque
4
4
  module PostgreSQL
5
5
  module Associations
6
- module Association
6
+ module ForeignAssociation
7
7
 
8
8
  # There is no problem of adding temporary items on target because
9
9
  # CollectionProxy will handle memory and persisted relationship
@@ -33,9 +33,6 @@ module Torque
33
33
  end
34
34
 
35
35
  end
36
-
37
- ::ActiveRecord::Associations::Association.prepend(Association)
38
- ::ActiveRecord::Associations::HasManyAssociation.prepend(Association)
39
36
  end
40
37
  end
41
38
  end
@@ -11,8 +11,61 @@ module Torque
11
11
  # For reflections connected through an array, make sure to properly
12
12
  # decuple the list of ids and set them as associated with the owner
13
13
  def run
14
+ return self if run?
14
15
  return super unless connected_through_array?
16
+
17
+ @run = true
15
18
  send("run_array_for_#{@reflection.macro}")
19
+ self
20
+ end
21
+
22
+ # Correctly correlate records when they are connected theough an array
23
+ def set_inverse(record)
24
+ return super unless connected_through_array? && @reflection.macro == :has_many
25
+
26
+ # Only the first owner is associated following the same instruction
27
+ # on the original implementation
28
+ convert_key(record[association_key_name])&.each do |key|
29
+ if owners = owners_by_key[key]
30
+ association = owners.first.association(reflection.name)
31
+ association.set_inverse_instance(record)
32
+ end
33
+ end
34
+ end
35
+
36
+ # Requires a slight change when running on has many since the value
37
+ # of the foreign key being an array
38
+ def load_records(raw_records = nil)
39
+ return super unless connected_through_array? && @reflection.macro == :has_many
40
+
41
+ @records_by_owner = {}.compare_by_identity
42
+ raw_records ||= loader_query.records_for([self])
43
+
44
+ @preloaded_records = raw_records.select do |record|
45
+ assignments = false
46
+
47
+ keys = convert_key(record[association_key_name]) || []
48
+ owners_by_key.values_at(*keys).each do |owner|
49
+ entries = (@records_by_owner[owner] ||= [])
50
+
51
+ if reflection.collection? || entries.empty?
52
+ entries << record
53
+ assignments = true
54
+ end
55
+ end
56
+
57
+ assignments
58
+ end
59
+ end
60
+
61
+ # Make sure to change the process when connected through an array
62
+ def owners_by_key
63
+ return super unless connected_through_array?
64
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
65
+ Array.wrap(convert_key(owner[owner_key_name])).each do |key|
66
+ (result[key] ||= []) << owner
67
+ end
68
+ end
16
69
  end
17
70
 
18
71
  private
@@ -41,32 +94,6 @@ module Torque
41
94
  end
42
95
  end
43
96
 
44
- if PostgreSQL::AR604
45
- # This is how Rails 6.0.4 and 6.1 now load the records
46
- def load_records
47
- return super unless connected_through_array?
48
-
49
- @records_by_owner = {}.compare_by_identity
50
- raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
51
-
52
- @preloaded_records = raw_records.select do |record|
53
- assignments = false
54
-
55
- ids = convert_key(record[association_key_name])
56
- owners_by_key.values_at(*ids).flat_map do |owner|
57
- entries = (@records_by_owner[owner] ||= [])
58
-
59
- if reflection.collection? || entries.empty?
60
- entries << record
61
- assignments = true
62
- end
63
- end
64
-
65
- assignments
66
- end
67
- end
68
- end
69
-
70
97
  # Build correctly the constraint condition in order to get the
71
98
  # associated ids
72
99
  def records_for(ids, &block)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module Associations
6
+ module Preloader
7
+ module LoaderQuery
8
+ def foreign_column
9
+ @foreign_column ||= scope.columns_hash[association_key_name]
10
+ end
11
+
12
+ def load_records_for_keys(keys, &block)
13
+ condition = query_condition_for(keys)
14
+ scope.where(condition).load(&block)
15
+ end
16
+
17
+ def query_condition_for(keys)
18
+ if connected_through_array?
19
+ value = scope.cast_for_condition(foreign_column, keys.to_a)
20
+ scope.table[association_key_name].overlaps(value)
21
+ else
22
+ { association_key_name => keys }
23
+ end
24
+ end
25
+
26
+ def connected_through_array?
27
+ foreign_column.array?
28
+ end
29
+ end
30
+
31
+ ::ActiveRecord::Associations::Preloader::Association::LoaderQuery
32
+ .prepend(LoaderQuery)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1 +1,2 @@
1
1
  require_relative 'preloader/association'
2
+ require_relative 'preloader/loader_query'
@@ -1,5 +1,10 @@
1
- require_relative 'associations/association'
2
1
  require_relative 'associations/association_scope'
3
2
  require_relative 'associations/belongs_to_many_association'
3
+ require_relative 'associations/foreign_association'
4
+
4
5
  require_relative 'associations/builder'
5
6
  require_relative 'associations/preloader'
7
+
8
+ association_mod = Torque::PostgreSQL::Associations::ForeignAssociation
9
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(association_mod)
10
+ ::ActiveRecord::Associations::BelongsToManyAssociation.prepend(association_mod)
@@ -220,7 +220,7 @@ module Torque
220
220
  @arel_threshold_value ||= begin
221
221
  case threshold
222
222
  when Symbol, String
223
- "arel_attribute('#{threshold}')"
223
+ "arel_table['#{threshold}']"
224
224
  when ActiveSupport::Duration
225
225
  value = "'#{threshold.to_i} seconds'"
226
226
  "::Arel.sql(\"#{value}\").cast(:interval)"
@@ -453,7 +453,11 @@ module Torque
453
453
  attr_value = threshold.present? ? method_names[:real] : attribute
454
454
  default_value = default.inspect
455
455
 
456
- "#{attr_value}.nil? ? #{default_value} : #{attr_value}.include?(value)"
456
+ [
457
+ "return #{default_value} if #{attr_value}.nil?",
458
+ "(#{attr_value}.min.try(:infinite?) || #{attr_value}.min <= value) &&",
459
+ " (#{attr_value}.max.try(:infinite?) || #{attr_value}.max > value)",
460
+ ].join("\n")
457
461
  end
458
462
 
459
463
  def instance_start
@@ -4,11 +4,6 @@ module Torque
4
4
  module PostgreSQL
5
5
  include ActiveSupport::Configurable
6
6
 
7
- # Stores a version check for compatibility purposes
8
- AR604 = (ActiveRecord.gem_version >= Gem::Version.new('6.0.4'))
9
- AR610 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.0'))
10
- AR615 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.5'))
11
-
12
7
  # Use the same logger as the Active Record one
13
8
  def self.logger
14
9
  ActiveRecord::Base.logger
@@ -27,11 +22,6 @@ module Torque
27
22
  # same configuration is set to true
28
23
  config.eager_load = false
29
24
 
30
- # This allows default values to have extended values like arrays and casted
31
- # values. Extended defaults are still experimental, so enable and test it
32
- # before using it in prod
33
- config.use_extended_defaults = false
34
-
35
25
  # Set a list of irregular model name when associated with table names
36
26
  config.irregular_models = {}
37
27
  def config.irregular_models=(hash)
@@ -67,11 +57,11 @@ module Torque
67
57
 
68
58
  # The name of the method to be used on any ActiveRecord::Base to
69
59
  # initialize model-based enum features
70
- enum.base_method = :enum
60
+ enum.base_method = :torque_enum
71
61
 
72
62
  # The name of the method to be used on any ActiveRecord::Base to
73
63
  # initialize model-based enum set features
74
- enum.set_method = :enum_set
64
+ enum.set_method = :torque_enum_set
75
65
 
76
66
  # Indicates if bang methods like 'disabled!' should update the record on
77
67
  # database or not
@@ -45,9 +45,9 @@ module Torque
45
45
  return klass_attr.eq(source_attr) unless connected_through_array?
46
46
 
47
47
  # Klass and key are associated with the reflection Class
48
- klass_type = klass.columns_hash[join_keys.key.to_s]
48
+ klass_type = klass.columns_hash[join_primary_key.to_s]
49
49
  # active_record and foreign_key are associated with the source Class
50
- source_type = active_record.columns_hash[join_keys.foreign_key.to_s]
50
+ source_type = active_record.columns_hash[join_foreign_key.to_s]
51
51
 
52
52
  # If both are attributes but the left side is not an array, and the
53
53
  # right side is, use the ANY operation
@@ -70,11 +70,9 @@ module Torque
70
70
  klass_attr.overlaps(source_attr)
71
71
  end
72
72
 
73
- if PostgreSQL::AR610
74
- # TODO: Deprecate this method
75
- def join_keys
76
- OpenStruct.new(key: join_primary_key, foreign_key: join_foreign_key)
77
- end
73
+ # TODO: Deprecate this method
74
+ def join_keys
75
+ OpenStruct.new(key: join_primary_key, foreign_key: join_foreign_key)
78
76
  end
79
77
 
80
78
  private
@@ -19,6 +19,8 @@ module Torque
19
19
  MULTI_VALUE_METHODS = [:distinct_on, :auxiliary_statements, :cast_records, :select_extra]
20
20
  VALUE_METHODS = SINGLE_VALUE_METHODS + MULTI_VALUE_METHODS
21
21
 
22
+ ARColumn = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Column
23
+
22
24
  # :nodoc:
23
25
  def select_extra_values; get_value(:select_extra); end
24
26
  # :nodoc:
@@ -74,6 +76,14 @@ module Torque
74
76
  end
75
77
  end
76
78
 
79
+ # Serialize the given value so it can be used in a condition tha involves
80
+ # the given column
81
+ def cast_for_condition(column, value)
82
+ column = columns_hash[column.to_s] unless column.is_a?(ARColumn)
83
+ caster = connection.lookup_cast_type_from_column(column)
84
+ connection.type_cast(caster.serialize(value))
85
+ end
86
+
77
87
  private
78
88
 
79
89
  def build_arel(*)
@@ -140,18 +150,6 @@ module Torque
140
150
  ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUES += %i[cast_records itself_only
141
151
  distinct_on auxiliary_statements]
142
152
 
143
- unless AR610
144
- Relation::SINGLE_VALUE_METHODS.each do |value|
145
- ActiveRecord::QueryMethods::DEFAULT_VALUES[value] = nil \
146
- if ActiveRecord::QueryMethods::DEFAULT_VALUES[value].nil?
147
- end
148
-
149
- Relation::MULTI_VALUE_METHODS.each do |value|
150
- ActiveRecord::QueryMethods::DEFAULT_VALUES[value] ||= \
151
- ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY
152
- end
153
- end
154
-
155
153
  $VERBOSE = warn_level
156
154
  end
157
155
  end
@@ -109,6 +109,7 @@ module Torque
109
109
 
110
110
  # Try to find a model based on a given table
111
111
  def lookup_model(table_name, scoped_class = '')
112
+ # byebug if table_name == 'activities'
112
113
  scoped_class = scoped_class.name if scoped_class.is_a?(Class)
113
114
  return @data_sources_model_names[table_name] \
114
115
  if @data_sources_model_names.key?(table_name)
@@ -123,7 +124,7 @@ module Torque
123
124
 
124
125
  # Test all the possible names against all the possible scopes
125
126
  until scopes.size == 0
126
- scope = scopes.join.safe_constantize
127
+ scope = scopes.join.chomp('::').safe_constantize
127
128
  model = find_model(max_name, table_name, scope) unless scope.nil?
128
129
  return @data_sources_model_names[table_name] = model unless model.nil?
129
130
  scopes.pop
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- VERSION = '2.2.3'
5
+ VERSION = '3.0.0'
6
6
  end
7
7
  end
@@ -1,2 +1 @@
1
1
  require 'torque/postgresql'
2
- require 'torque/range'
data/spec/schema.rb CHANGED
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- version = 77
13
+ version = 2
14
14
 
15
15
  return if ActiveRecord::Migrator.current_version == version
16
16
  ActiveRecord::Schema.define(version: version) do
@@ -20,12 +20,13 @@ ActiveRecord::Schema.define(version: version) do
20
20
  enable_extension "pgcrypto"
21
21
  enable_extension "plpgsql"
22
22
 
23
- # These are user-defined types used on this database
24
- create_enum "content_status", ["created", "draft", "published", "archived"], force: :cascade
25
- create_enum "specialties", ["books", "movies", "plays"], force: :cascade
26
- create_enum "roles", ["visitor", "assistant", "manager", "admin"], force: :cascade
27
- create_enum "conflicts", ["valid", "invalid", "untrusted"], force: :cascade
28
- create_enum "types", ["A", "B", "C", "D"], force: :cascade
23
+ # Custom types defined in this database.
24
+ # Note that some types may not work with other database engines. Be careful if changing database.
25
+ create_enum "content_status", ["created", "draft", "published", "archived"]
26
+ create_enum "specialties", ["books", "movies", "plays"]
27
+ create_enum "roles", ["visitor", "assistant", "manager", "admin"]
28
+ create_enum "conflicts", ["valid", "invalid", "untrusted"]
29
+ create_enum "types", ["A", "B", "C", "D"]
29
30
 
30
31
  create_table "geometries", force: :cascade do |t|
31
32
  t.point "point"
@@ -53,8 +54,8 @@ ActiveRecord::Schema.define(version: version) do
53
54
  t.bigint "tag_ids", array: true
54
55
  t.string "title"
55
56
  t.string "url"
56
- t.enum "type", subtype: :types
57
- t.enum "conflicts", subtype: :conflicts, array: true
57
+ t.enum "type", enum_type: :types
58
+ t.enum "conflicts", enum_type: :conflicts, array: true
58
59
  t.datetime "created_at", null: false
59
60
  t.datetime "updated_at", null: false
60
61
  end
@@ -62,13 +63,13 @@ ActiveRecord::Schema.define(version: version) do
62
63
  create_table "authors", force: :cascade do |t|
63
64
  t.string "name"
64
65
  t.string "type"
65
- t.enum "specialty", subtype: :specialties
66
+ t.enum "specialty", enum_type: :specialties
66
67
  end
67
68
 
68
69
  create_table "texts", force: :cascade do |t|
69
70
  t.integer "user_id"
70
71
  t.string "content"
71
- t.enum "conflict", subtype: :conflicts
72
+ t.enum "conflict", enum_type: :conflicts
72
73
  end
73
74
 
74
75
  create_table "comments", force: :cascade do |t|
@@ -84,7 +85,7 @@ ActiveRecord::Schema.define(version: version) do
84
85
  create_table "courses", force: :cascade do |t|
85
86
  t.string "title", null: false
86
87
  t.interval "duration"
87
- t.enum "types", subtype: :types, array: true
88
+ t.enum "types", enum_type: :types, array: true
88
89
  t.datetime "created_at", null: false
89
90
  t.datetime "updated_at", null: false
90
91
  end
@@ -98,7 +99,7 @@ ActiveRecord::Schema.define(version: version) do
98
99
  t.integer "activity_id"
99
100
  t.string "title"
100
101
  t.text "content"
101
- t.enum "status", subtype: :content_status
102
+ t.enum "status", enum_type: :content_status
102
103
  t.index ["author_id"], name: "index_posts_on_author_id", using: :btree
103
104
  end
104
105
 
@@ -111,7 +112,7 @@ ActiveRecord::Schema.define(version: version) do
111
112
 
112
113
  create_table "users", force: :cascade do |t|
113
114
  t.string "name", null: false
114
- t.enum "role", subtype: :roles, default: :visitor
115
+ t.enum "role", enum_type: :roles, default: :visitor
115
116
  t.datetime "created_at", null: false
116
117
  t.datetime "updated_at", null: false
117
118
  end
@@ -120,7 +121,7 @@ ActiveRecord::Schema.define(version: version) do
120
121
  t.integer "author_id"
121
122
  t.string "title"
122
123
  t.boolean "active"
123
- t.enum "kind", subtype: :types
124
+ t.enum "kind", enum_type: :types
124
125
  t.datetime "created_at", null: false
125
126
  t.datetime "updated_at", null: false
126
127
  end
data/spec/spec_helper.rb CHANGED
@@ -46,10 +46,6 @@ RSpec.configure do |config|
46
46
  DatabaseCleaner.strategy = :transaction
47
47
  end
48
48
 
49
- config.before(:each, js: true) do
50
- DatabaseCleaner.strategy = :truncation
51
- end
52
-
53
49
  config.before(:each) do
54
50
  DatabaseCleaner.start
55
51
  end
@@ -51,8 +51,6 @@ RSpec.describe 'Arel' do
51
51
  context 'on default value' do
52
52
  let(:connection) { ActiveRecord::Base.connection }
53
53
 
54
- before(:context) { Torque::PostgreSQL.config.use_extended_defaults = true }
55
- after(:context) { Torque::PostgreSQL.config.use_extended_defaults = false }
56
54
  after { Author.reset_column_information }
57
55
 
58
56
  it 'does not break the change column default value method' do
@@ -68,26 +66,37 @@ RSpec.describe 'Arel' do
68
66
 
69
67
  it 'works properly when column is an array' do
70
68
  expect { connection.add_column(:authors, :tag_ids, :bigint, array: true, default: []) }.not_to raise_error
71
- expect(Author.columns_hash['tag_ids'].default).to eq([])
69
+ expect(Author.new.tag_ids).to eq([])
72
70
  end
73
71
 
74
- it 'works with an array with enum values' do
72
+ it 'works with an array with enum values for a new enum' do
73
+ value = ['a', 'b']
74
+
75
+ expect do
76
+ connection.create_enum(:samples, %i[a b c d])
77
+ connection.add_column(:authors, :samples, :enum, enum_type: :samples, array: true, default: value)
78
+ end.not_to raise_error
79
+
80
+ expect(Author.new.samples).to eq(value)
81
+ end
82
+
83
+ it 'works with an array with enum values for an existing enum' do
75
84
  value = ['visitor', 'assistant']
76
- expect { connection.add_column(:authors, :roles, :roles, array: true, default: value) }.not_to raise_error
77
- expect(Author.columns_hash['roles'].default).to eq(value)
85
+ expect { connection.add_column(:authors, :roles, :enum, enum_type: :roles, array: true, default: value) }.not_to raise_error
86
+ expect(Author.new.roles).to eq(value)
78
87
  end
79
88
 
80
89
  it 'works with multi dimentional array' do
81
90
  value = [['1', '2'], ['3', '4']]
82
91
  expect { connection.add_column(:authors, :tag_ids, :string, array: true, default: value) }.not_to raise_error
83
- expect(Author.columns_hash['tag_ids'].default).to eq(value)
92
+ expect(Author.new.tag_ids).to eq(value)
84
93
  end
85
94
 
86
95
  it 'works with change column default value' do
87
96
  value = ['2', '3']
88
97
  connection.add_column(:authors, :tag_ids, :string, array: true)
89
98
  expect { connection.change_column_default(:authors, :tag_ids, { from: nil, to: value }) }.not_to raise_error
90
- expect(Author.columns_hash['tag_ids'].default).to eq(value)
99
+ expect(Author.new.tag_ids).to eq(value)
91
100
  end
92
101
  end
93
102
 
@@ -344,7 +344,8 @@ RSpec.describe 'BelongsToMany' do
344
344
  subject.tags.concat(records)
345
345
 
346
346
  entries = Video.all
347
- ActiveRecord::Associations::Preloader.new.preload(entries, :tags, Tag.all)
347
+ arguments = { records: entries, associations: :tags, available_records: Tag.all.to_a }
348
+ ActiveRecord::Associations::Preloader.new(**arguments).call
348
349
  entries = entries.load
349
350
 
350
351
  expect(entries.size).to be_eql(1)
@@ -29,9 +29,10 @@ RSpec.describe 'Enum' do
29
29
  subject { table_definition.new(connection, 'articles') }
30
30
 
31
31
  it 'can be defined as an array' do
32
- subject.enum(:content_status, array: true)
32
+ subject.enum(:content_status, array: true, enum_type: :content_status)
33
33
  expect(subject['content_status'].name).to be_eql('content_status')
34
- expect(subject['content_status'].type).to be_eql(:content_status)
34
+ expect(subject['content_status'].type).to be_eql(:enum)
35
+ expect(subject['content_status'].options[:enum_type]).to be_eql(:content_status)
35
36
 
36
37
  array = subject['content_status'].respond_to?(:options) \
37
38
  ? subject['content_status'].options[:array] \
@@ -44,14 +45,14 @@ RSpec.describe 'Enum' do
44
45
  context 'on schema' do
45
46
  it 'can be used on tables' do
46
47
  dump_io = StringIO.new
47
- checker = /t\.enum +"conflicts", +array: true, +subtype: :conflicts/
48
+ checker = /t\.enum +"conflicts", +array: true, +enum_type: "conflicts"/
48
49
  ActiveRecord::SchemaDumper.dump(connection, dump_io)
49
50
  expect(dump_io.string).to match checker
50
51
  end
51
52
 
52
53
  xit 'can have a default value as an array of symbols' do
53
54
  dump_io = StringIO.new
54
- checker = /t\.enum +"types", +default: \[:A, :B\], +array: true, +subtype: :types/
55
+ checker = /t\.enum +"types", +default: \[:A, :B\], +array: true, +enum_type: "types"/
55
56
  ActiveRecord::SchemaDumper.dump(connection, dump_io)
56
57
  expect(dump_io.string).to match checker
57
58
  end
@@ -26,12 +26,6 @@ RSpec.describe 'Enum' do
26
26
  end
27
27
 
28
28
  context 'on migration' do
29
- it 'can be created' do
30
- connection.create_enum(:status, %i(foo bar))
31
- expect(connection.type_exists?(:status)).to be_truthy
32
- expect(connection.enum_values(:status)).to be_eql(['foo', 'bar'])
33
- end
34
-
35
29
  it 'can be deleted' do
36
30
  connection.create_enum(:status, %i(foo bar))
37
31
  expect(connection.type_exists?(:status)).to be_truthy
@@ -46,16 +40,6 @@ RSpec.describe 'Enum' do
46
40
  expect(connection.type_exists?(:status)).to be_truthy
47
41
  end
48
42
 
49
- it 'can have prefix' do
50
- connection.create_enum(:status, %i(foo bar), prefix: true)
51
- expect(connection.enum_values(:status)).to be_eql(['status_foo', 'status_bar'])
52
- end
53
-
54
- it 'can have suffix' do
55
- connection.create_enum(:status, %i(foo bar), suffix: 'tst')
56
- expect(connection.enum_values(:status)).to be_eql(['foo_tst', 'bar_tst'])
57
- end
58
-
59
43
  it 'inserts values at the end' do
60
44
  connection.create_enum(:status, %i(foo bar))
61
45
  connection.add_enum_values(:status, %i(baz qux))
@@ -85,74 +69,6 @@ RSpec.describe 'Enum' do
85
69
  end
86
70
  end
87
71
 
88
- context 'on table definition' do
89
- subject { table_definition.new(connection, 'articles') }
90
-
91
- it 'has the enum method' do
92
- expect(subject).to respond_to(:enum)
93
- end
94
-
95
- it 'can be used in a single form' do
96
- subject.enum('content_status')
97
- expect(subject['content_status'].name).to be_eql('content_status')
98
- expect(subject['content_status'].type).to be_eql(:content_status)
99
- end
100
-
101
- it 'can be used in a multiple form' do
102
- subject.enum('foo', 'bar', 'baz', subtype: :content_status)
103
- expect(subject['foo'].type).to be_eql(:content_status)
104
- expect(subject['bar'].type).to be_eql(:content_status)
105
- expect(subject['baz'].type).to be_eql(:content_status)
106
- end
107
-
108
- it 'can have custom type' do
109
- subject.enum('foo', subtype: :content_status)
110
- expect(subject['foo'].name).to be_eql('foo')
111
- expect(subject['foo'].type).to be_eql(:content_status)
112
- end
113
-
114
- it 'raises StatementInvalid when type isn\'t defined' do
115
- subject.enum('foo')
116
- creation = connection.send(:schema_creation).accept subject
117
- expect{ connection.execute creation }.to raise_error(ActiveRecord::StatementInvalid)
118
- end
119
- end
120
-
121
- context 'on schema' do
122
- it 'dumps when has it' do
123
- dump_io = StringIO.new
124
- ActiveRecord::SchemaDumper.dump(connection, dump_io)
125
- expect(dump_io.string).to match /create_enum \"content_status\", \[/
126
- end
127
-
128
- it 'sorts the enum entries to better consistency' do
129
- dump_io = StringIO.new
130
- ActiveRecord::SchemaDumper.dump(connection, dump_io)
131
- items = dump_io.string.scan(/create_enum "(\w+)"/).flatten
132
- expect(items).to be_eql(items.sort)
133
- end
134
-
135
- it 'do not dump when has none' do
136
- connection.drop_type(:content_status, force: :cascade)
137
-
138
- dump_io = StringIO.new
139
- ActiveRecord::SchemaDumper.dump(connection, dump_io)
140
- expect(dump_io.string).not_to match /create_enum \"content_status\", \[/
141
- end
142
-
143
- it 'can be used on tables too' do
144
- dump_io = StringIO.new
145
- ActiveRecord::SchemaDumper.dump(connection, dump_io)
146
- expect(dump_io.string).to match /t\.enum +"status", +subtype: :content_status/
147
- end
148
-
149
- it 'can have a default value as symbol' do
150
- dump_io = StringIO.new
151
- ActiveRecord::SchemaDumper.dump(connection, dump_io)
152
- expect(dump_io.string).to match /t\.enum +"role", +default: :visitor, +subtype: :roles/
153
- end
154
- end
155
-
156
72
  context 'on value' do
157
73
  subject { Enum::ContentStatus }
158
74
  let(:values) { %w(created draft published archived) }
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.2.3
4
+ version: 3.0.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: 2022-03-31 00:00:00.000000000 Z
11
+ date: 2022-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '6.0'
19
+ version: '7.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '6.0'
26
+ version: '7.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -189,14 +189,15 @@ files:
189
189
  - lib/torque/postgresql/arel/select_manager.rb
190
190
  - lib/torque/postgresql/arel/visitors.rb
191
191
  - lib/torque/postgresql/associations.rb
192
- - lib/torque/postgresql/associations/association.rb
193
192
  - lib/torque/postgresql/associations/association_scope.rb
194
193
  - lib/torque/postgresql/associations/belongs_to_many_association.rb
195
194
  - lib/torque/postgresql/associations/builder.rb
196
195
  - lib/torque/postgresql/associations/builder/belongs_to_many.rb
197
196
  - lib/torque/postgresql/associations/builder/has_many.rb
197
+ - lib/torque/postgresql/associations/foreign_association.rb
198
198
  - lib/torque/postgresql/associations/preloader.rb
199
199
  - lib/torque/postgresql/associations/preloader/association.rb
200
+ - lib/torque/postgresql/associations/preloader/loader_query.rb
200
201
  - lib/torque/postgresql/attributes.rb
201
202
  - lib/torque/postgresql/attributes/builder.rb
202
203
  - lib/torque/postgresql/attributes/builder/enum.rb
@@ -232,7 +233,6 @@ files:
232
233
  - lib/torque/postgresql/relation/merger.rb
233
234
  - lib/torque/postgresql/schema_cache.rb
234
235
  - lib/torque/postgresql/version.rb
235
- - lib/torque/range.rb
236
236
  - spec/en.yml
237
237
  - spec/factories/authors.rb
238
238
  - spec/factories/comments.rb
@@ -279,7 +279,6 @@ files:
279
279
  - spec/tests/lazy_spec.rb
280
280
  - spec/tests/period_spec.rb
281
281
  - spec/tests/quoting_spec.rb
282
- - spec/tests/range_spec.rb
283
282
  - spec/tests/relation_spec.rb
284
283
  - spec/tests/table_inheritance_spec.rb
285
284
  homepage: https://github.com/crashtech/torque-postgresql
@@ -294,7 +293,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
294
293
  requirements:
295
294
  - - ">="
296
295
  - !ruby/object:Gem::Version
297
- version: '2.6'
296
+ version: 2.7.2
298
297
  required_rubygems_version: !ruby/object:Gem::Requirement
299
298
  requirements:
300
299
  - - ">="
@@ -352,6 +351,5 @@ test_files:
352
351
  - spec/tests/lazy_spec.rb
353
352
  - spec/tests/period_spec.rb
354
353
  - spec/tests/quoting_spec.rb
355
- - spec/tests/range_spec.rb
356
354
  - spec/tests/relation_spec.rb
357
355
  - spec/tests/table_inheritance_spec.rb
data/lib/torque/range.rb DELETED
@@ -1,20 +0,0 @@
1
- module Torque
2
- module Range
3
- def intersection(other)
4
- raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
5
-
6
- new_min = self.cover?(other.min) ? other.min : other.cover?(min) ? min : nil
7
- new_max = self.cover?(other.max) ? other.max : other.cover?(max) ? max : nil
8
-
9
- new_min && new_max ? new_min..new_max : nil
10
- end
11
- alias_method :&, :intersection
12
-
13
- def union(other)
14
- raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
15
-
16
- ([min, other.min].min)..([max, other.max].max)
17
- end
18
- alias_method :|, :union
19
- end
20
- end
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.xdescribe 'Range' do
4
- let(:sample) { (5..15) }
5
-
6
- it 'has intersection' do
7
- expect { sample.intersection(10) }.to raise_error(ArgumentError, /Range/)
8
-
9
- result = sample & (10..15)
10
- expect(result).to be_a(Range)
11
- expect(result.min).to be_eql(10)
12
- expect(result.max).to be_eql(15)
13
-
14
- result = sample & (15..20)
15
- expect(result).to be_a(Range)
16
- expect(result.min).to be_eql(15)
17
- expect(result.max).to be_eql(15)
18
-
19
- result = sample & (0..10)
20
- expect(result).to be_a(Range)
21
- expect(result.min).to be_eql(5)
22
- expect(result.max).to be_eql(10)
23
-
24
- result = sample & (-10..0)
25
- expect(result).to be_nil
26
- end
27
-
28
- it 'has union' do
29
- expect { sample.union(10) }.to raise_error(ArgumentError, /Range/)
30
-
31
- result = sample | (0..10)
32
- expect(result).to be_a(Range)
33
- expect(result.min).to be_eql(0)
34
- expect(result.max).to be_eql(15)
35
- end
36
- end