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 +4 -4
- data/lib/torque/postgresql/adapter/quoting.rb +8 -5
- data/lib/torque/postgresql/adapter/schema_definitions.rb +5 -5
- data/lib/torque/postgresql/adapter/schema_dumper.rb +6 -5
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +2 -1
- data/lib/torque/postgresql/config.rb +1 -0
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +13 -39
- data/lib/torque/postgresql/version.rb +1 -1
- data/spec/models/question_select.rb +2 -0
- data/spec/schema.rb +147 -147
- data/spec/spec_helper.rb +8 -6
- data/spec/tests/arel_spec.rb +28 -4
- data/spec/tests/belongs_to_many_spec.rb +62 -0
- data/spec/tests/enum_set_spec.rb +5 -4
- data/spec/tests/enum_spec.rb +11 -4
- data/spec/tests/has_many_spec.rb +46 -0
- data/spec/tests/interval_spec.rb +2 -1
- metadata +46 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95fa82a4869d180b12518b184191e6877b9e6cf3108fb5e68e55d6a836809389
|
4
|
+
data.tar.gz: 45c6e24a30b3782ec26ff5caafe01430c24f137c460eb19b6f2a49e0b6609142
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
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
|
-
|
20
|
-
args.each { |name| column(name, (
|
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
|
-
#
|
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 +:
|
25
|
+
# Adds +:enum_type+ option to the default set
|
26
26
|
def prepare_column_options(column)
|
27
27
|
spec = super
|
28
28
|
|
29
|
-
if
|
30
|
-
spec[:
|
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
|
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 " #
|
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
|
-
|
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
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
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
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
|
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)
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
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
|
data/spec/tests/arel_spec.rb
CHANGED
@@ -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.
|
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.
|
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.
|
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
|
data/spec/tests/enum_set_spec.rb
CHANGED
@@ -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 {
|
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, +
|
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, +
|
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
|
data/spec/tests/enum_spec.rb
CHANGED
@@ -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 {
|
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',
|
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", +
|
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, +
|
158
|
+
expect(dump_io.string).to match /t\.enum +"role", +default: :visitor, +enum_type: :roles/
|
152
159
|
end
|
153
160
|
end
|
154
161
|
|
data/spec/tests/has_many_spec.rb
CHANGED
@@ -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
|
data/spec/tests/interval_spec.rb
CHANGED
@@ -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 {
|
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.
|
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:
|
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: '
|
127
|
+
version: '6.2'
|
128
128
|
- - ">="
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version:
|
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: '
|
137
|
+
version: '6.2'
|
138
138
|
- - ">="
|
139
139
|
- !ruby/object:Gem::Version
|
140
|
-
version:
|
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: '
|
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: '
|
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.
|
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.
|
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/
|
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/
|
335
|
-
- spec/
|
336
|
-
- spec/
|
337
|
-
- spec/
|
338
|
-
- spec/
|
339
|
-
- spec/
|
340
|
-
- spec/
|
341
|
-
- spec/
|
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/
|
351
|
+
- spec/tests/interval_spec.rb
|
352
|
+
- spec/tests/lazy_spec.rb
|
358
353
|
- spec/tests/period_spec.rb
|
359
|
-
- spec/
|
360
|
-
- spec/
|
361
|
-
- spec/
|
362
|
-
- spec/
|
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
|