torque-postgresql 2.2.1 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fcb69118c9cca1afe5ef21a9ed9b4cf98e9036108b739707a1e7f65058aaaf84
4
- data.tar.gz: d7ca85705f802c0d15c0aabca99a9ec17b8403e60e912724b609d8b93cd7bd86
3
+ metadata.gz: 95fa82a4869d180b12518b184191e6877b9e6cf3108fb5e68e55d6a836809389
4
+ data.tar.gz: 45c6e24a30b3782ec26ff5caafe01430c24f137c460eb19b6f2a49e0b6609142
5
5
  SHA512:
6
- metadata.gz: 0a48ed549030276f9a3e4c845454eb89cf97b8d0bf3d00cd5431abd85a7317f6031e4f997e86ae3657012ca9d4578598d612a14c531247575f1d0496bc629f6e
7
- data.tar.gz: 50a1817ef37c0c8d98a71b92500a73b420f5dc27cf9d19afc6aeb8492e3d4582b69ee9ada91b281699987cc4bb33a9d1df584219073d432299ebad53a4a29513
6
+ metadata.gz: cd7e925ab13b8ae3eb6f0d92fbb994f12c6c121998792263b0752eb9647c0d2e4f72dec8d3973c6e0365fe0c5194a2af5fba9189189743c5bca7b06a4e6dfa41
7
+ data.tar.gz: 470bb4e8b0816318ca5ee1b99e1896a8a4f3297af46e653de4ddac5347cf40a781b530a4e2531cf41eae38f7205332e52ced09e5b857a618f965851a6d9d7162
@@ -6,6 +6,8 @@ module Torque
6
6
  module Quoting
7
7
 
8
8
  Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
9
+ Column = ActiveRecord::ConnectionAdapters::PostgreSQL::Column
10
+ ColumnDefinition = ActiveRecord::ConnectionAdapters::ColumnDefinition
9
11
 
10
12
  # Quotes type names for use in SQL queries.
11
13
  def quote_type_name(string, schema = nil)
@@ -20,11 +22,12 @@ module Torque
20
22
  end
21
23
 
22
24
  def quote_default_expression(value, column)
23
- if column.options.try(:[], :array) && value.class <= Array
24
- quote(value) + '::' + column.sql_type
25
- else
26
- super
27
- end
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
28
31
  end
29
32
 
30
33
  private
@@ -12,16 +12,16 @@ module Torque
12
12
  args.each { |name| column(name, :interval, **options) }
13
13
  end
14
14
 
15
- # Creates a column with an enum type, needing to specify the subtype,
15
+ # Creates a column with an enum type, needing to specify the enum_type,
16
16
  # which is basically the name of the type defined prior creating the
17
17
  # column
18
18
  def enum(*args, **options)
19
- subtype = options.delete(:subtype)
20
- args.each { |name| column(name, (subtype || name), **options) }
19
+ enum_type = [options.delete(:subtype), options.delete(:enum_type)].compact.first
20
+ args.each { |name| column(name, (enum_type || name), **options) }
21
21
  end
22
22
 
23
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
24
+ # enum_type, which is basically the name of the type defined prior
25
25
  # creating the column
26
26
  def enum_set(*args, **options)
27
27
  super(*args, **options.merge(array: true))
@@ -47,7 +47,7 @@ module Torque
47
47
 
48
48
  if ActiveRecord::ConnectionAdapters::PostgreSQL.const_defined?('ColumnDefinition')
49
49
  module ColumnDefinition
50
- attr_accessor :subtype
50
+ attr_accessor :subtype, :enum_type
51
51
  end
52
52
 
53
53
  ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition.include ColumnDefinition
@@ -22,12 +22,12 @@ module Torque
22
22
  column.type == :enum_set ? :enum : super
23
23
  end
24
24
 
25
- # Adds +:subtype+ option to the default set
25
+ # Adds +:enum_type+ option to the default set
26
26
  def prepare_column_options(column)
27
27
  spec = super
28
28
 
29
- if subtype = schema_subtype(column)
30
- spec[:subtype] = subtype
29
+ if enum_type = schema_enum_type(column)
30
+ spec[:enum_type] = enum_type
31
31
  end
32
32
 
33
33
  spec
@@ -35,7 +35,7 @@ module Torque
35
35
 
36
36
  private
37
37
 
38
- def schema_subtype(column)
38
+ def schema_enum_type(column)
39
39
  column.sql_type.to_sym.inspect if column.type == :enum || column.type == :enum_set
40
40
  end
41
41
 
@@ -89,7 +89,8 @@ module Torque
89
89
  types = @connection.user_defined_types('e')
90
90
  return unless types.any?
91
91
 
92
- stream.puts " # These are user-defined types used on this database"
92
+ stream.puts " # Custom types defined in this database."
93
+ stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
93
94
  types.sort_by(&:first).each { |(name, type)| send(type.to_sym, name, stream) }
94
95
  stream.puts
95
96
  rescue => e
@@ -53,7 +53,8 @@ module Torque
53
53
 
54
54
  def load_target
55
55
  if stale_target? || find_target?
56
- @target = merge_target_lists(find_target, target)
56
+ new_records = PostgreSQL::AR615 ? target.extract!(&:persisted?) : []
57
+ @target = merge_target_lists((find_target || []) + new_records, target)
57
58
  end
58
59
 
59
60
  loaded!
@@ -7,6 +7,7 @@ module Torque
7
7
  # Stores a version check for compatibility purposes
8
8
  AR604 = (ActiveRecord.gem_version >= Gem::Version.new('6.0.4'))
9
9
  AR610 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.0'))
10
+ AR615 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.5'))
10
11
 
11
12
  # Use the same logger as the Active Record one
12
13
  def self.logger
@@ -6,9 +6,6 @@ module Torque
6
6
  module AbstractReflection
7
7
  AREL_ATTR = ::Arel::Attributes::Attribute
8
8
 
9
- ARR_NO_CAST = 'bigint'
10
- ARR_CAST = 'bigint[]'
11
-
12
9
  # Check if the foreign key actually exists
13
10
  def connected_through_array?
14
11
  false
@@ -40,34 +37,29 @@ module Torque
40
37
  result
41
38
  end
42
39
 
43
- # Build the id constraint checking if both types are perfect matching
40
+ # Build the id constraint checking if both types are perfect matching.
41
+ # The klass attribute (left side) will always be a column attribute
44
42
  def build_id_constraint(klass_attr, source_attr)
45
43
  return klass_attr.eq(source_attr) unless connected_through_array?
46
44
 
47
45
  # Klass and key are associated with the reflection Class
48
46
  klass_type = klass.columns_hash[join_keys.key.to_s]
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]
51
47
 
52
- # If both are attributes but the left side is not an array, and the
53
- # right side is, use the ANY operation
54
- any_operation = arel_array_to_any(klass_attr, source_attr, klass_type, source_type)
55
- return klass_attr.eq(any_operation) if any_operation
48
+ # Apply an ANY operation which checks if the single value on the left
49
+ # side exists in the array on the right side
50
+ if source_attr.is_a?(AREL_ATTR)
51
+ any_value = [klass_attr, source_attr]
52
+ any_value.reverse! if klass_type.try(:array?)
53
+ return any_value.shift.eq(::Arel::Nodes::NamedFunction.new('ANY', any_value))
54
+ end
56
55
 
57
56
  # If the left side is not an array, just use the IN condition
58
57
  return klass_attr.in(source_attr) unless klass_type.try(:array)
59
58
 
60
- # Decide if should apply a cast to ensure same type comparision
61
- should_cast = klass_type.type.eql?(:integer) && source_type.type.eql?(:integer)
62
- should_cast &= !klass_type.sql_type.eql?(source_type.sql_type)
63
- should_cast |= !(klass_attr.is_a?(AREL_ATTR) && source_attr.is_a?(AREL_ATTR))
64
-
65
- # Apply necessary transformations to values
66
- klass_attr = cast_constraint_to_array(klass_type, klass_attr, should_cast)
67
- source_attr = cast_constraint_to_array(source_type, source_attr, should_cast)
68
-
69
- # Return the overlap condition
70
- klass_attr.overlaps(source_attr)
59
+ # Build the overlap condition (array && array) ensuring that the right
60
+ # side has the same type as the left side
61
+ source_attr = ::Arel::Nodes.build_quoted(Array.wrap(source_attr))
62
+ klass_attr.overlaps(source_attr.cast(klass_type.sql_type_metadata.sql_type))
71
63
  end
72
64
 
73
65
  if PostgreSQL::AR610
@@ -85,24 +77,6 @@ module Torque
85
77
 
86
78
  build_id_constraint(klass_attr, source_attr)
87
79
  end
88
-
89
- # Prepare a value for an array constraint overlap condition
90
- def cast_constraint_to_array(type, value, should_cast)
91
- base_ready = type.try(:array) && value.is_a?(AREL_ATTR)
92
- return value if base_ready && (type.sql_type.eql?(ARR_NO_CAST) || !should_cast)
93
-
94
- value = ::Arel::Nodes.build_quoted(Array.wrap(value)) unless base_ready
95
- value = value.cast(ARR_CAST) if should_cast
96
- value
97
- end
98
-
99
- # Check if it's possible to turn both attributes into an ANY condition
100
- def arel_array_to_any(klass_attr, source_attr, klass_type, source_type)
101
- return unless !klass_type.try(:array) && source_type.try(:array) &&
102
- klass_attr.is_a?(AREL_ATTR) && source_attr.is_a?(AREL_ATTR)
103
-
104
- ::Arel::Nodes::NamedFunction.new('ANY', [source_attr])
105
- end
106
80
  end
107
81
 
108
82
  ::ActiveRecord::Reflection::AbstractReflection.prepend(AbstractReflection)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- VERSION = '2.2.1'
5
+ VERSION = '2.2.4'
6
6
  end
7
7
  end
@@ -1,2 +1,4 @@
1
+ require_relative 'question'
2
+
1
3
  class QuestionSelect < Question
2
4
  end
data/spec/schema.rb CHANGED
@@ -10,151 +10,151 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- begin
14
- version = 73
15
-
16
- raise SystemExit if ActiveRecord::Migrator.current_version == version
17
- ActiveRecord::Schema.define(version: version) do
18
- self.verbose = false
19
-
20
- # These are extensions that must be enabled in order to support this database
21
- enable_extension "pgcrypto"
22
- enable_extension "plpgsql"
23
-
24
- # These are user-defined types used on this database
25
- create_enum "content_status", ["created", "draft", "published", "archived"], force: :cascade
26
- create_enum "specialties", ["books", "movies", "plays"], force: :cascade
27
- create_enum "roles", ["visitor", "assistant", "manager", "admin"], force: :cascade
28
- create_enum "conflicts", ["valid", "invalid", "untrusted"], force: :cascade
29
- create_enum "types", ["A", "B", "C", "D"], force: :cascade
30
-
31
- create_table "geometries", force: :cascade do |t|
32
- t.point "point"
33
- t.line "line"
34
- t.lseg "lseg"
35
- t.box "box"
36
- t.path "closed_path"
37
- t.path "open_path"
38
- t.polygon "polygon"
39
- t.circle "circle"
40
- end
41
-
42
- create_table "time_keepers", force: :cascade do |t|
43
- t.daterange "available"
44
- t.tsrange "period"
45
- t.tstzrange "tzperiod"
46
- t.interval "th"
47
- end
48
-
49
- create_table "tags", force: :cascade do |t|
50
- t.string "name"
51
- end
52
-
53
- create_table "videos", force: :cascade do |t|
54
- t.bigint "tag_ids", array: true
55
- t.string "title"
56
- t.string "url"
57
- t.enum "type", subtype: :types
58
- t.enum "conflicts", subtype: :conflicts, array: true
59
- t.datetime "created_at", null: false
60
- t.datetime "updated_at", null: false
61
- end
62
-
63
- create_table "authors", force: :cascade do |t|
64
- t.string "name"
65
- t.string "type"
66
- t.enum "specialty", subtype: :specialties
67
- end
68
-
69
- create_table "texts", force: :cascade do |t|
70
- t.integer "user_id"
71
- t.string "content"
72
- t.enum "conflict", subtype: :conflicts
73
- end
74
-
75
- create_table "comments", force: :cascade do |t|
76
- t.integer "user_id", null: false
77
- t.integer "comment_id"
78
- t.integer "video_id"
79
- t.text "content", null: false
80
- t.string "kind"
81
- t.index ["user_id"], name: "index_comments_on_user_id", using: :btree
82
- t.index ["comment_id"], name: "index_comments_on_comment_id", using: :btree
83
- end
84
-
85
- create_table "courses", force: :cascade do |t|
86
- t.string "title", null: false
87
- t.interval "duration"
88
- t.enum "types", subtype: :types, array: true
89
- t.datetime "created_at", null: false
90
- t.datetime "updated_at", null: false
91
- end
92
-
93
- create_table "images", force: :cascade, id: false do |t|
94
- t.string "file"
95
- end
96
-
97
- create_table "posts", force: :cascade do |t|
98
- t.integer "author_id"
99
- t.integer "activity_id"
100
- t.string "title"
101
- t.text "content"
102
- t.enum "status", subtype: :content_status
103
- t.index ["author_id"], name: "index_posts_on_author_id", using: :btree
104
- end
105
-
106
- create_table "items", force: :cascade do |t|
107
- t.string "name"
108
- t.bigint "tag_ids", array: true, default: "{1}"
109
- t.datetime "created_at", null: false
110
- t.datetime "updated_at", null: false
111
- end
112
-
113
- create_table "users", force: :cascade do |t|
114
- t.string "name", null: false
115
- t.enum "role", subtype: :roles, default: :visitor
116
- t.datetime "created_at", null: false
117
- t.datetime "updated_at", null: false
118
- end
119
-
120
- create_table "activities", force: :cascade do |t|
121
- t.integer "author_id"
122
- t.string "title"
123
- t.boolean "active"
124
- t.enum "kind", subtype: :types
125
- t.datetime "created_at", null: false
126
- t.datetime "updated_at", null: false
127
- end
128
-
129
- create_table "questions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
130
- t.string "title"
131
- t.datetime "created_at", null: false
132
- t.datetime "updated_at", null: false
133
- end
134
-
135
- create_table "activity_books", force: :cascade, inherits: :activities do |t|
136
- t.text "description"
137
- t.string "url"
138
- t.boolean "activated"
139
- end
140
-
141
- create_table "activity_posts", force: :cascade, inherits: [:activities, :images] do |t|
142
- t.integer "post_id"
143
- t.string "url"
144
- t.integer "activated"
145
- end
146
-
147
- create_table "activity_post_samples", force: :cascade, inherits: :activity_posts
148
-
149
- create_table "question_selects", force: :cascade, inherits: :questions do |t|
150
- t.string "options", array: true
151
- end
152
-
153
- # create_table "activity_blanks", force: :cascade, inherits: :activities
154
-
155
- # create_table "activity_images", force: :cascade, inherits: [:activities, :images]
156
-
157
- add_foreign_key "posts", "authors"
158
- end
159
- rescue SystemExit
13
+ version = 2
14
+
15
+ return if ActiveRecord::Migrator.current_version == version
16
+ ActiveRecord::Schema.define(version: version) do
17
+ self.verbose = false
18
+
19
+ # These are extensions that must be enabled in order to support this database
20
+ enable_extension "pgcrypto"
21
+ enable_extension "plpgsql"
22
+
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"], force: :cascade
26
+ create_enum "specialties", ["books", "movies", "plays"], force: :cascade
27
+ create_enum "roles", ["visitor", "assistant", "manager", "admin"], force: :cascade
28
+ create_enum "conflicts", ["valid", "invalid", "untrusted"], force: :cascade
29
+ create_enum "types", ["A", "B", "C", "D"], force: :cascade
30
+
31
+ create_table "geometries", force: :cascade do |t|
32
+ t.point "point"
33
+ t.line "line"
34
+ t.lseg "lseg"
35
+ t.box "box"
36
+ t.path "closed_path"
37
+ t.path "open_path"
38
+ t.polygon "polygon"
39
+ t.circle "circle"
40
+ end
41
+
42
+ create_table "time_keepers", force: :cascade do |t|
43
+ t.daterange "available"
44
+ t.tsrange "period"
45
+ t.tstzrange "tzperiod"
46
+ t.interval "th"
47
+ end
48
+
49
+ create_table "tags", force: :cascade do |t|
50
+ t.string "name"
51
+ end
52
+
53
+ create_table "videos", force: :cascade do |t|
54
+ t.bigint "tag_ids", array: true
55
+ t.string "title"
56
+ t.string "url"
57
+ t.enum "type", enum_type: :types
58
+ t.enum "conflicts", enum_type: :conflicts, array: true
59
+ t.datetime "created_at", null: false
60
+ t.datetime "updated_at", null: false
61
+ end
62
+
63
+ create_table "authors", force: :cascade do |t|
64
+ t.string "name"
65
+ t.string "type"
66
+ t.enum "specialty", enum_type: :specialties
67
+ end
68
+
69
+ create_table "texts", force: :cascade do |t|
70
+ t.integer "user_id"
71
+ t.string "content"
72
+ t.enum "conflict", enum_type: :conflicts
73
+ end
74
+
75
+ create_table "comments", force: :cascade do |t|
76
+ t.integer "user_id", null: false
77
+ t.integer "comment_id"
78
+ t.integer "video_id"
79
+ t.text "content", null: false
80
+ t.string "kind"
81
+ t.index ["user_id"], name: "index_comments_on_user_id", using: :btree
82
+ t.index ["comment_id"], name: "index_comments_on_comment_id", using: :btree
83
+ end
84
+
85
+ create_table "courses", force: :cascade do |t|
86
+ t.string "title", null: false
87
+ t.interval "duration"
88
+ t.enum "types", enum_type: :types, array: true
89
+ t.datetime "created_at", null: false
90
+ t.datetime "updated_at", null: false
91
+ end
92
+
93
+ create_table "images", force: :cascade, id: false do |t|
94
+ t.string "file"
95
+ end
96
+
97
+ create_table "posts", force: :cascade do |t|
98
+ t.integer "author_id"
99
+ t.integer "activity_id"
100
+ t.string "title"
101
+ t.text "content"
102
+ t.enum "status", enum_type: :content_status
103
+ t.index ["author_id"], name: "index_posts_on_author_id", using: :btree
104
+ end
105
+
106
+ create_table "items", force: :cascade do |t|
107
+ t.string "name"
108
+ t.bigint "tag_ids", array: true, default: "{1}"
109
+ t.datetime "created_at", null: false
110
+ t.datetime "updated_at", null: false
111
+ end
112
+
113
+ create_table "users", force: :cascade do |t|
114
+ t.string "name", null: false
115
+ t.enum "role", enum_type: :roles, default: :visitor
116
+ t.datetime "created_at", null: false
117
+ t.datetime "updated_at", null: false
118
+ end
119
+
120
+ create_table "activities", force: :cascade do |t|
121
+ t.integer "author_id"
122
+ t.string "title"
123
+ t.boolean "active"
124
+ t.enum "kind", enum_type: :types
125
+ t.datetime "created_at", null: false
126
+ t.datetime "updated_at", null: false
127
+ end
128
+
129
+ create_table "questions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
130
+ t.string "title"
131
+ t.datetime "created_at", null: false
132
+ t.datetime "updated_at", null: false
133
+ end
134
+
135
+ create_table "activity_books", force: :cascade, inherits: :activities do |t|
136
+ t.text "description"
137
+ t.string "url"
138
+ t.boolean "activated"
139
+ end
140
+
141
+ create_table "activity_posts", force: :cascade, inherits: [:activities, :images] do |t|
142
+ t.integer "post_id"
143
+ t.string "url"
144
+ t.integer "activated"
145
+ end
146
+
147
+ create_table "activity_post_samples", force: :cascade, inherits: :activity_posts
148
+
149
+ create_table "question_selects", force: :cascade, inherits: :questions do |t|
150
+ t.string "options", array: true
151
+ end
152
+
153
+ # create_table "activity_blanks", force: :cascade, inherits: :activities
154
+
155
+ # create_table "activity_images", force: :cascade, inherits: [:activities, :images]
156
+
157
+ add_foreign_key "posts", "authors"
160
158
  end
159
+
160
+ ActiveRecord::Base.connection.schema_cache.clear!
data/spec/spec_helper.rb CHANGED
@@ -8,9 +8,13 @@ require 'byebug'
8
8
 
9
9
  Dotenv.load
10
10
 
11
- ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
12
- cache = ActiveRecord::Base.connection.schema_cache
11
+ ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'] || {
12
+ adapter: 'postgresql',
13
+ username: 'travis',
14
+ port: 5433,
15
+ })
13
16
 
17
+ cache = ActiveRecord::Base.connection.schema_cache
14
18
  cleaner = ->() do
15
19
  cache.instance_variable_set(:@inheritance_loaded, false)
16
20
  cache.instance_variable_set(:@inheritance_dependencies, {})
@@ -27,6 +31,7 @@ I18n.load_path << Pathname.pwd.join('spec', 'en.yml')
27
31
  RSpec.configure do |config|
28
32
  config.extend Mocks::CreateTable
29
33
  config.include Mocks::CacheQuery
34
+ config.include FactoryBot::Syntax::Methods
30
35
 
31
36
  config.formatter = :documentation
32
37
  config.color = true
@@ -34,6 +39,7 @@ RSpec.configure do |config|
34
39
 
35
40
  # Handles acton before rspec initialize
36
41
  config.before(:suite) do
42
+ ActiveSupport::Deprecation.silenced = true
37
43
  DatabaseCleaner.clean_with(:truncation)
38
44
  end
39
45
 
@@ -41,10 +47,6 @@ RSpec.configure do |config|
41
47
  DatabaseCleaner.strategy = :transaction
42
48
  end
43
49
 
44
- config.before(:each, js: true) do
45
- DatabaseCleaner.strategy = :truncation
46
- end
47
-
48
50
  config.before(:each) do
49
51
  DatabaseCleaner.start
50
52
  end
@@ -55,6 +55,12 @@ RSpec.describe 'Arel' do
55
55
  after(:context) { Torque::PostgreSQL.config.use_extended_defaults = false }
56
56
  after { Author.reset_column_information }
57
57
 
58
+ it 'does not break the change column default value method' do
59
+ connection.add_column(:authors, :enabled, :boolean)
60
+ expect { connection.change_column_default(:authors, :enabled, { from: nil, to: true }) }.not_to raise_error
61
+ expect(Author.columns_hash['enabled'].default).to eq('true')
62
+ end
63
+
58
64
  it 'does not break jsonb' do
59
65
  expect { connection.add_column(:authors, :profile, :jsonb, default: []) }.not_to raise_error
60
66
  expect(Author.columns_hash['profile'].default).to eq('[]')
@@ -62,19 +68,37 @@ RSpec.describe 'Arel' do
62
68
 
63
69
  it 'works properly when column is an array' do
64
70
  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([])
71
+ expect(Author.new.tag_ids).to eq([])
72
+ end
73
+
74
+ it 'works with an array with enum values for a new enum' do
75
+ value = ['a', 'b']
76
+
77
+ expect do
78
+ connection.create_enum(:samples, %i[a b c d])
79
+ connection.add_column(:authors, :samples, :samples, array: true, default: value)
80
+ end.not_to raise_error
81
+
82
+ expect(Author.new.samples).to eq(value)
66
83
  end
67
84
 
68
- it 'works with an array with enum values' do
85
+ it 'works with an array with enum values for an existing enum' do
69
86
  value = ['visitor', 'assistant']
70
87
  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)
88
+ expect(Author.new.roles).to eq(value)
72
89
  end
73
90
 
74
91
  it 'works with multi dimentional array' do
75
92
  value = [['1', '2'], ['3', '4']]
76
93
  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)
94
+ expect(Author.new.tag_ids).to eq(value)
95
+ end
96
+
97
+ it 'works with change column default value' do
98
+ value = ['2', '3']
99
+ connection.add_column(:authors, :tag_ids, :string, array: true)
100
+ expect { connection.change_column_default(:authors, :tag_ids, { from: nil, to: value }) }.not_to raise_error
101
+ expect(Author.new.tag_ids).to eq(value)
78
102
  end
79
103
  end
80
104
 
@@ -339,6 +339,19 @@ RSpec.describe 'BelongsToMany' do
339
339
  expect(entries.first.tags.size).to be_eql(5)
340
340
  end
341
341
 
342
+ it 'can preload records using ActiveRecord::Associations::Preloader' do
343
+ records = FactoryBot.create_list(:tag, 5)
344
+ subject.tags.concat(records)
345
+
346
+ entries = Video.all
347
+ ActiveRecord::Associations::Preloader.new.preload(entries, :tags, Tag.all)
348
+ entries = entries.load
349
+
350
+ expect(entries.size).to be_eql(1)
351
+ expect(entries.first.tags).to be_loaded
352
+ expect(entries.first.tags.size).to be_eql(5)
353
+ end
354
+
342
355
  it 'can joins records' do
343
356
  query = Video.all.joins(:tags)
344
357
  expect(query.to_sql).to match(/INNER JOIN "tags"/)
@@ -379,4 +392,53 @@ RSpec.describe 'BelongsToMany' do
379
392
  end
380
393
  end
381
394
  end
395
+
396
+ context 'using uuid' do
397
+ let(:connection) { ActiveRecord::Base.connection }
398
+ let(:game) { Class.new(ActiveRecord::Base) }
399
+ let(:player) { Class.new(ActiveRecord::Base) }
400
+ let(:other) { player.create }
401
+
402
+ # TODO: Set as a shred example
403
+ before do
404
+ connection.create_table(:players, id: :uuid) { |t| t.string :name }
405
+ connection.create_table(:games, id: :uuid) { |t| t.uuid :player_ids, array: true }
406
+
407
+ options = { anonymous_class: player, foreign_key: :player_ids }
408
+ options[:inverse_of] = false if Torque::PostgreSQL::AR610
409
+
410
+ game.table_name = 'games'
411
+ player.table_name = 'players'
412
+ game.belongs_to_many :players, **options
413
+ end
414
+
415
+ subject { game.create }
416
+
417
+ it 'loads associated records' do
418
+ subject.update(player_ids: [other.id])
419
+ expect(subject.players.to_sql).to be_eql(<<-SQL.squish)
420
+ SELECT "players".* FROM "players" WHERE "players"."id" IN ('#{other.id}')
421
+ SQL
422
+
423
+ expect(subject.players.load).to be_a(ActiveRecord::Associations::CollectionProxy)
424
+ expect(subject.players.to_a).to be_eql([other])
425
+ end
426
+
427
+ it 'can preload records' do
428
+ records = 5.times.map { player.create }
429
+ subject.players.concat(records)
430
+
431
+ entries = game.all.includes(:players).load
432
+
433
+ expect(entries.size).to be_eql(1)
434
+ expect(entries.first.players).to be_loaded
435
+ expect(entries.first.players.size).to be_eql(5)
436
+ end
437
+
438
+ it 'can joins records' do
439
+ query = game.all.joins(:players)
440
+ expect(query.to_sql).to match(/INNER JOIN "players"/)
441
+ expect { query.load }.not_to raise_error
442
+ end
443
+ end
382
444
  end
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  RSpec.describe 'Enum' do
4
4
  let(:connection) { ActiveRecord::Base.connection }
5
5
  let(:attribute_klass) { Torque::PostgreSQL::Attributes::EnumSet }
6
+ let(:table_definition) { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition }
6
7
 
7
8
  def decorate(model, field, options = {})
8
9
  attribute_klass.include_on(model, :enum_set)
@@ -25,10 +26,10 @@ RSpec.describe 'Enum' do
25
26
  end
26
27
 
27
28
  context 'on table definition' do
28
- subject { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.new('articles') }
29
+ subject { table_definition.new(connection, 'articles') }
29
30
 
30
31
  it 'can be defined as an array' do
31
- subject.enum(:content_status, array: true)
32
+ subject.enum(:content_status, array: true, enum_type: :content_status)
32
33
  expect(subject['content_status'].name).to be_eql('content_status')
33
34
  expect(subject['content_status'].type).to be_eql(:content_status)
34
35
 
@@ -43,14 +44,14 @@ RSpec.describe 'Enum' do
43
44
  context 'on schema' do
44
45
  it 'can be used on tables' do
45
46
  dump_io = StringIO.new
46
- checker = /t\.enum +"conflicts", +array: true, +subtype: :conflicts/
47
+ checker = /t\.enum +"conflicts", +array: true, +enum_type: :conflicts/
47
48
  ActiveRecord::SchemaDumper.dump(connection, dump_io)
48
49
  expect(dump_io.string).to match checker
49
50
  end
50
51
 
51
52
  xit 'can have a default value as an array of symbols' do
52
53
  dump_io = StringIO.new
53
- checker = /t\.enum +"types", +default: \[:A, :B\], +array: true, +subtype: :types/
54
+ checker = /t\.enum +"types", +default: \[:A, :B\], +array: true, +enum_type: :types/
54
55
  ActiveRecord::SchemaDumper.dump(connection, dump_io)
55
56
  expect(dump_io.string).to match checker
56
57
  end
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  RSpec.describe 'Enum' do
4
4
  let(:connection) { ActiveRecord::Base.connection }
5
5
  let(:attribute_klass) { Torque::PostgreSQL::Attributes::Enum }
6
+ let(:table_definition) { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition }
6
7
 
7
8
  def decorate(model, field, options = {})
8
9
  attribute_klass.include_on(model, :pg_enum)
@@ -85,7 +86,7 @@ RSpec.describe 'Enum' do
85
86
  end
86
87
 
87
88
  context 'on table definition' do
88
- subject { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.new('articles') }
89
+ subject { table_definition.new(connection, 'articles') }
89
90
 
90
91
  it 'has the enum method' do
91
92
  expect(subject).to respond_to(:enum)
@@ -98,13 +99,19 @@ RSpec.describe 'Enum' do
98
99
  end
99
100
 
100
101
  it 'can be used in a multiple form' do
101
- subject.enum('foo', 'bar', 'baz', subtype: :content_status)
102
+ subject.enum('foo', 'bar', 'baz', enum_type: :content_status)
102
103
  expect(subject['foo'].type).to be_eql(:content_status)
103
104
  expect(subject['bar'].type).to be_eql(:content_status)
104
105
  expect(subject['baz'].type).to be_eql(:content_status)
105
106
  end
106
107
 
107
108
  it 'can have custom type' do
109
+ subject.enum('foo', enum_type: :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 'can use the deprecated subtype option' do
108
115
  subject.enum('foo', subtype: :content_status)
109
116
  expect(subject['foo'].name).to be_eql('foo')
110
117
  expect(subject['foo'].type).to be_eql(:content_status)
@@ -142,13 +149,13 @@ RSpec.describe 'Enum' do
142
149
  it 'can be used on tables too' do
143
150
  dump_io = StringIO.new
144
151
  ActiveRecord::SchemaDumper.dump(connection, dump_io)
145
- expect(dump_io.string).to match /t\.enum +"status", +subtype: :content_status/
152
+ expect(dump_io.string).to match /t\.enum +"status", +enum_type: :content_status/
146
153
  end
147
154
 
148
155
  it 'can have a default value as symbol' do
149
156
  dump_io = StringIO.new
150
157
  ActiveRecord::SchemaDumper.dump(connection, dump_io)
151
- expect(dump_io.string).to match /t\.enum +"role", +default: :visitor, +subtype: :roles/
158
+ expect(dump_io.string).to match /t\.enum +"role", +default: :visitor, +enum_type: :roles/
152
159
  end
153
160
  end
154
161
 
@@ -411,4 +411,50 @@ RSpec.describe 'HasMany' do
411
411
  expect { query.load }.not_to raise_error
412
412
  end
413
413
  end
414
+
415
+ context 'using uuid' do
416
+ let(:connection) { ActiveRecord::Base.connection }
417
+ let(:game) { Class.new(ActiveRecord::Base) }
418
+ let(:player) { Class.new(ActiveRecord::Base) }
419
+
420
+ # TODO: Set as a shred example
421
+ before do
422
+ connection.create_table(:players, id: :uuid) { |t| t.string :name }
423
+ connection.create_table(:games, id: :uuid) { |t| t.uuid :player_ids, array: true }
424
+
425
+ options = { anonymous_class: game, foreign_key: :player_ids }
426
+ options[:inverse_of] = false if Torque::PostgreSQL::AR610
427
+
428
+ game.table_name = 'games'
429
+ player.table_name = 'players'
430
+ player.has_many :games, array: true, **options
431
+ end
432
+
433
+ subject { player.create }
434
+
435
+ it 'loads associated records' do
436
+ expect(subject.games.to_sql).to match(Regexp.new(<<-SQL.squish))
437
+ SELECT "games"\\.\\* FROM "games"
438
+ WHERE \\(?"games"\\."player_ids" && ARRAY\\['#{subject.id}'\\]::uuid\\[\\]\\)?
439
+ SQL
440
+
441
+ expect(subject.games.load).to be_a(ActiveRecord::Associations::CollectionProxy)
442
+ expect(subject.games.to_a).to be_eql([])
443
+ end
444
+
445
+ it 'can preload records' do
446
+ 5.times { game.create(player_ids: [subject.id]) }
447
+ entries = player.all.includes(:games).load
448
+
449
+ expect(entries.size).to be_eql(1)
450
+ expect(entries.first.games).to be_loaded
451
+ expect(entries.first.games.size).to be_eql(5)
452
+ end
453
+
454
+ it 'can joins records' do
455
+ query = player.all.joins(:games)
456
+ expect(query.to_sql).to match(/INNER JOIN "games"/)
457
+ expect { query.load }.not_to raise_error
458
+ end
459
+ end
414
460
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe 'Interval' do
4
4
  let(:connection) { ActiveRecord::Base.connection }
5
+ let(:table_definition) { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition }
5
6
 
6
7
  context 'on settings' do
7
8
  it 'must be set to ISO 8601' do
@@ -10,7 +11,7 @@ RSpec.describe 'Interval' do
10
11
  end
11
12
 
12
13
  context 'on table definition' do
13
- subject { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.new('articles') }
14
+ subject { table_definition.new(connection, 'articles') }
14
15
 
15
16
  it 'has the interval method' do
16
17
  expect(subject).to respond_to(:interval)
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.1
4
+ version: 2.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-25 00:00:00.000000000 Z
11
+ date: 2022-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -124,40 +124,34 @@ dependencies:
124
124
  requirements:
125
125
  - - "~>"
126
126
  - !ruby/object:Gem::Version
127
- version: '5.0'
127
+ version: '6.2'
128
128
  - - ">="
129
129
  - !ruby/object:Gem::Version
130
- version: 5.0.2
130
+ version: 6.2.1
131
131
  type: :development
132
132
  prerelease: false
133
133
  version_requirements: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: '5.0'
137
+ version: '6.2'
138
138
  - - ">="
139
139
  - !ruby/object:Gem::Version
140
- version: 5.0.2
140
+ version: 6.2.1
141
141
  - !ruby/object:Gem::Dependency
142
142
  name: faker
143
143
  requirement: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - "~>"
146
146
  - !ruby/object:Gem::Version
147
- version: '1.5'
148
- - - ">="
149
- - !ruby/object:Gem::Version
150
- version: 1.5.0
147
+ version: '2.20'
151
148
  type: :development
152
149
  prerelease: false
153
150
  version_requirements: !ruby/object:Gem::Requirement
154
151
  requirements:
155
152
  - - "~>"
156
153
  - !ruby/object:Gem::Version
157
- version: '1.5'
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- version: 1.5.0
154
+ version: '2.20'
161
155
  description: Add support to complex resources of PostgreSQL, like data types, array
162
156
  associations, and auxiliary statements (CTE)
163
157
  email:
@@ -292,7 +286,7 @@ homepage: https://github.com/crashtech/torque-postgresql
292
286
  licenses:
293
287
  - MIT
294
288
  metadata: {}
295
- post_install_message:
289
+ post_install_message:
296
290
  rdoc_options: []
297
291
  require_paths:
298
292
  - lib
@@ -300,64 +294,64 @@ required_ruby_version: !ruby/object:Gem::Requirement
300
294
  requirements:
301
295
  - - ">="
302
296
  - !ruby/object:Gem::Version
303
- version: 2.5.0
297
+ version: '2.6'
304
298
  required_rubygems_version: !ruby/object:Gem::Requirement
305
299
  requirements:
306
300
  - - ">="
307
301
  - !ruby/object:Gem::Version
308
302
  version: 1.8.11
309
303
  requirements: []
310
- rubygems_version: 3.2.14
311
- signing_key:
304
+ rubygems_version: 3.2.15
305
+ signing_key:
312
306
  specification_version: 4
313
307
  summary: ActiveRecord extension to access PostgreSQL advanced resources
314
308
  test_files:
315
- - spec/models/activity_post/sample.rb
309
+ - spec/en.yml
310
+ - spec/factories/authors.rb
311
+ - spec/factories/comments.rb
312
+ - spec/factories/item.rb
313
+ - spec/factories/posts.rb
314
+ - spec/factories/tags.rb
315
+ - spec/factories/texts.rb
316
+ - spec/factories/users.rb
317
+ - spec/factories/videos.rb
318
+ - spec/mocks/cache_query.rb
319
+ - spec/mocks/create_table.rb
316
320
  - spec/models/activity.rb
321
+ - spec/models/activity_book.rb
322
+ - spec/models/activity_post/sample.rb
323
+ - spec/models/activity_post.rb
317
324
  - spec/models/author.rb
318
325
  - spec/models/author_journalist.rb
319
326
  - spec/models/comment.rb
320
327
  - spec/models/course.rb
321
- - spec/models/post.rb
322
- - spec/models/text.rb
323
- - spec/models/user.rb
324
- - spec/models/activity_book.rb
325
- - spec/models/activity_post.rb
326
328
  - spec/models/geometry.rb
327
329
  - spec/models/guest_comment.rb
328
- - spec/models/tag.rb
329
- - spec/models/time_keeper.rb
330
- - spec/models/video.rb
331
330
  - spec/models/item.rb
331
+ - spec/models/post.rb
332
332
  - spec/models/question.rb
333
333
  - spec/models/question_select.rb
334
- - spec/factories/authors.rb
335
- - spec/factories/comments.rb
336
- - spec/factories/posts.rb
337
- - spec/factories/tags.rb
338
- - spec/factories/texts.rb
339
- - spec/factories/users.rb
340
- - spec/factories/videos.rb
341
- - spec/factories/item.rb
342
- - spec/tests/geometric_builder_spec.rb
343
- - spec/tests/enum_spec.rb
344
- - spec/tests/range_spec.rb
345
- - spec/tests/table_inheritance_spec.rb
346
- - spec/tests/collector_spec.rb
347
- - spec/tests/distinct_on_spec.rb
348
- - spec/tests/interval_spec.rb
349
- - spec/tests/lazy_spec.rb
350
- - spec/tests/quoting_spec.rb
351
- - spec/tests/relation_spec.rb
334
+ - spec/models/tag.rb
335
+ - spec/models/text.rb
336
+ - spec/models/time_keeper.rb
337
+ - spec/models/user.rb
338
+ - spec/models/video.rb
339
+ - spec/schema.rb
340
+ - spec/spec_helper.rb
341
+ - spec/tests/arel_spec.rb
352
342
  - spec/tests/auxiliary_statement_spec.rb
353
343
  - spec/tests/belongs_to_many_spec.rb
344
+ - spec/tests/collector_spec.rb
345
+ - spec/tests/distinct_on_spec.rb
354
346
  - spec/tests/enum_set_spec.rb
347
+ - spec/tests/enum_spec.rb
348
+ - spec/tests/geometric_builder_spec.rb
355
349
  - spec/tests/has_many_spec.rb
356
350
  - spec/tests/insert_all_spec.rb
357
- - spec/tests/arel_spec.rb
351
+ - spec/tests/interval_spec.rb
352
+ - spec/tests/lazy_spec.rb
358
353
  - spec/tests/period_spec.rb
359
- - spec/mocks/cache_query.rb
360
- - spec/mocks/create_table.rb
361
- - spec/en.yml
362
- - spec/spec_helper.rb
363
- - spec/schema.rb
354
+ - spec/tests/quoting_spec.rb
355
+ - spec/tests/range_spec.rb
356
+ - spec/tests/relation_spec.rb
357
+ - spec/tests/table_inheritance_spec.rb