torque-postgresql 2.2.1 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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