torque-postgresql 2.0.3 → 2.1.1
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.rb +1 -0
- data/lib/torque/postgresql/adapter.rb +23 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +2 -0
- data/lib/torque/postgresql/adapter/oid.rb +3 -1
- data/lib/torque/postgresql/adapter/oid/box.rb +2 -0
- data/lib/torque/postgresql/adapter/oid/circle.rb +2 -0
- data/lib/torque/postgresql/adapter/oid/enum.rb +5 -0
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +2 -0
- data/lib/torque/postgresql/adapter/oid/interval.rb +2 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +2 -0
- data/lib/torque/postgresql/adapter/oid/range.rb +2 -0
- data/lib/torque/postgresql/adapter/oid/segment.rb +2 -0
- data/lib/torque/postgresql/adapter/quoting.rb +2 -0
- data/lib/torque/postgresql/adapter/schema_creation.rb +8 -1
- data/lib/torque/postgresql/adapter/schema_definitions.rb +2 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +9 -3
- data/lib/torque/postgresql/adapter/schema_statements.rb +2 -0
- data/lib/torque/postgresql/arel/infix_operation.rb +5 -1
- data/lib/torque/postgresql/arel/join_source.rb +2 -0
- data/lib/torque/postgresql/arel/nodes.rb +2 -0
- data/lib/torque/postgresql/arel/operations.rb +2 -0
- data/lib/torque/postgresql/arel/select_manager.rb +2 -0
- data/lib/torque/postgresql/arel/visitors.rb +6 -3
- data/lib/torque/postgresql/associations/association.rb +14 -3
- data/lib/torque/postgresql/associations/association_scope.rb +2 -0
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +168 -48
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +8 -5
- data/lib/torque/postgresql/associations/builder/has_many.rb +2 -0
- data/lib/torque/postgresql/associations/preloader/association.rb +30 -1
- data/lib/torque/postgresql/attributes/builder.rb +3 -1
- data/lib/torque/postgresql/attributes/builder/enum.rb +10 -8
- data/lib/torque/postgresql/attributes/builder/period.rb +6 -4
- data/lib/torque/postgresql/attributes/enum.rb +6 -11
- data/lib/torque/postgresql/attributes/enum_set.rb +3 -1
- data/lib/torque/postgresql/attributes/lazy.rb +3 -1
- data/lib/torque/postgresql/attributes/period.rb +2 -0
- data/lib/torque/postgresql/autosave_association.rb +19 -16
- data/lib/torque/postgresql/auxiliary_statement.rb +2 -0
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +2 -0
- data/lib/torque/postgresql/base.rb +11 -2
- data/lib/torque/postgresql/coder.rb +5 -3
- data/lib/torque/postgresql/collector.rb +2 -0
- data/lib/torque/postgresql/config.rb +5 -0
- data/lib/torque/postgresql/geometry_builder.rb +2 -0
- data/lib/torque/postgresql/i18n.rb +2 -0
- data/lib/torque/postgresql/inheritance.rb +2 -0
- data/lib/torque/postgresql/insert_all.rb +26 -0
- data/lib/torque/postgresql/migration/command_recorder.rb +2 -0
- data/lib/torque/postgresql/railtie.rb +2 -0
- data/lib/torque/postgresql/reflection.rb +2 -0
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +13 -28
- data/lib/torque/postgresql/reflection/association_reflection.rb +24 -0
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +2 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +2 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +2 -0
- data/lib/torque/postgresql/reflection/through_reflection.rb +2 -0
- data/lib/torque/postgresql/relation.rb +15 -11
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +6 -1
- data/lib/torque/postgresql/relation/distinct_on.rb +2 -0
- data/lib/torque/postgresql/relation/inheritance.rb +2 -0
- data/lib/torque/postgresql/relation/merger.rb +2 -0
- data/lib/torque/postgresql/schema_cache.rb +2 -0
- data/lib/torque/postgresql/version.rb +3 -1
- data/spec/schema.rb +3 -2
- data/spec/tests/arel_spec.rb +3 -1
- data/spec/tests/belongs_to_many_spec.rb +139 -13
- data/spec/tests/enum_set_spec.rb +1 -1
- data/spec/tests/has_many_spec.rb +15 -1
- data/spec/tests/insert_all_spec.rb +89 -0
- data/spec/tests/table_inheritance_spec.rb +1 -1
- metadata +9 -6
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
module InsertAll
|
6
|
+
attr_reader :where
|
7
|
+
|
8
|
+
def initialize(*args, where: nil, **xargs)
|
9
|
+
super(*args, **xargs)
|
10
|
+
|
11
|
+
@where = where
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module InsertAll::Builder
|
16
|
+
delegate :where, to: :insert_all
|
17
|
+
|
18
|
+
def where_condition?
|
19
|
+
!where.nil?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveRecord::InsertAll.prepend InsertAll
|
24
|
+
ActiveRecord::InsertAll::Builder.include InsertAll::Builder
|
25
|
+
end
|
26
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Torque
|
2
4
|
module PostgreSQL
|
3
5
|
module Reflection
|
4
6
|
module AbstractReflection
|
5
7
|
AREL_ATTR = ::Arel::Attributes::Attribute
|
6
8
|
|
7
|
-
ARR_NO_CAST = 'bigint'
|
8
|
-
ARR_CAST = 'bigint[]'
|
9
|
+
ARR_NO_CAST = 'bigint'
|
10
|
+
ARR_CAST = 'bigint[]'
|
9
11
|
|
10
12
|
# Check if the foreign key actually exists
|
11
13
|
def connected_through_array?
|
@@ -68,11 +70,18 @@ module Torque
|
|
68
70
|
klass_attr.overlaps(source_attr)
|
69
71
|
end
|
70
72
|
|
73
|
+
if PostgreSQL::AR610
|
74
|
+
# TODO: Deprecate this method
|
75
|
+
def join_keys
|
76
|
+
OpenStruct.new(key: join_primary_key, foreign_key: join_foreign_key)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
71
80
|
private
|
72
81
|
|
73
82
|
def build_id_constraint_between(table, foreign_table)
|
74
|
-
klass_attr = table[
|
75
|
-
source_attr = foreign_table[
|
83
|
+
klass_attr = table[join_primary_key]
|
84
|
+
source_attr = foreign_table[join_foreign_key]
|
76
85
|
|
77
86
|
build_id_constraint(klass_attr, source_attr)
|
78
87
|
end
|
@@ -94,30 +103,6 @@ module Torque
|
|
94
103
|
|
95
104
|
::Arel::Nodes::NamedFunction.new('ANY', [source_attr])
|
96
105
|
end
|
97
|
-
|
98
|
-
# returns either +nil+ or the inverse association name that it finds.
|
99
|
-
def automatic_inverse_of
|
100
|
-
return super unless connected_through_array?
|
101
|
-
|
102
|
-
if can_find_inverse_of_automatically?(self)
|
103
|
-
inverse_name = options[:as] || active_record.name.demodulize
|
104
|
-
inverse_name = ActiveSupport::Inflector.underscore(inverse_name)
|
105
|
-
inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
|
106
|
-
inverse_name = inverse_name.to_sym
|
107
|
-
|
108
|
-
begin
|
109
|
-
reflection = klass._reflect_on_association(inverse_name)
|
110
|
-
rescue NameError
|
111
|
-
# Give up: we couldn't compute the klass type so we won't be able
|
112
|
-
# to find any associations either.
|
113
|
-
reflection = false
|
114
|
-
end
|
115
|
-
|
116
|
-
return inverse_name if reflection.connected_through_array? &&
|
117
|
-
valid_inverse_reflection?(reflection)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
106
|
end
|
122
107
|
|
123
108
|
::ActiveRecord::Reflection::AbstractReflection.prepend(AbstractReflection)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Torque
|
2
4
|
module PostgreSQL
|
3
5
|
module Reflection
|
@@ -22,6 +24,28 @@ module Torque
|
|
22
24
|
result
|
23
25
|
end
|
24
26
|
|
27
|
+
# returns either +nil+ or the inverse association name that it finds.
|
28
|
+
def automatic_inverse_of
|
29
|
+
return super unless connected_through_array?
|
30
|
+
|
31
|
+
if can_find_inverse_of_automatically?(self)
|
32
|
+
inverse_name = options[:as] || active_record.name.demodulize
|
33
|
+
inverse_name = ActiveSupport::Inflector.underscore(inverse_name)
|
34
|
+
inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
|
35
|
+
inverse_name = inverse_name.to_sym
|
36
|
+
|
37
|
+
begin
|
38
|
+
reflection = klass._reflect_on_association(inverse_name)
|
39
|
+
rescue NameError
|
40
|
+
# Give up: we couldn't compute the klass type so we won't be able
|
41
|
+
# to find any associations either.
|
42
|
+
reflection = false
|
43
|
+
end
|
44
|
+
|
45
|
+
return inverse_name if valid_inverse_reflection?(reflection)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
25
49
|
end
|
26
50
|
|
27
51
|
::ActiveRecord::Reflection::AssociationReflection.prepend(AssociationReflection)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'relation/distinct_on'
|
2
4
|
require_relative 'relation/auxiliary_statement'
|
3
5
|
require_relative 'relation/inheritance'
|
@@ -48,12 +50,12 @@ module Torque
|
|
48
50
|
when String
|
49
51
|
::Arel.sql(klass.send(:sanitize_sql, item.to_s))
|
50
52
|
when Symbol
|
51
|
-
base ? base.
|
53
|
+
base ? base.arel_table[item] : klass.arel_table[item]
|
52
54
|
when Array
|
53
55
|
resolve_column(item, base)
|
54
56
|
when Hash
|
55
57
|
raise ArgumentError, 'Unsupported Hash for attributes on third level' if base
|
56
|
-
item.map { |key, other_list| resolve_column(
|
58
|
+
item.map { |key, other_list| resolve_column(other_list, key) }
|
57
59
|
else
|
58
60
|
raise ArgumentError, "Unsupported argument type: #{value} (#{value.class})"
|
59
61
|
end
|
@@ -65,8 +67,8 @@ module Torque
|
|
65
67
|
return unless relation
|
66
68
|
|
67
69
|
table = predicate_builder.send(:table)
|
68
|
-
if table.associated_with?(relation)
|
69
|
-
table.associated_table(relation).send(:klass)
|
70
|
+
if table.associated_with?(relation.to_s)
|
71
|
+
table.associated_table(relation.to_s).send(:klass)
|
70
72
|
else
|
71
73
|
raise ArgumentError, "Relation for #{relation} not found on #{klass}"
|
72
74
|
end
|
@@ -138,14 +140,16 @@ module Torque
|
|
138
140
|
ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUES += %i[cast_records itself_only
|
139
141
|
distinct_on auxiliary_statements]
|
140
142
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
143
|
+
unless AR610
|
144
|
+
Relation::SINGLE_VALUE_METHODS.each do |value|
|
145
|
+
ActiveRecord::QueryMethods::DEFAULT_VALUES[value] = nil \
|
146
|
+
if ActiveRecord::QueryMethods::DEFAULT_VALUES[value].nil?
|
147
|
+
end
|
145
148
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
+
Relation::MULTI_VALUE_METHODS.each do |value|
|
150
|
+
ActiveRecord::QueryMethods::DEFAULT_VALUES[value] ||= \
|
151
|
+
ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY
|
152
|
+
end
|
149
153
|
end
|
150
154
|
|
151
155
|
$VERBOSE = warn_level
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Torque
|
2
4
|
module PostgreSQL
|
3
5
|
module Relation
|
@@ -34,7 +36,10 @@ module Torque
|
|
34
36
|
# attributes as well
|
35
37
|
def bound_attributes
|
36
38
|
visitor = ::Arel::Visitors::PostgreSQL.new(ActiveRecord::Base.connection)
|
37
|
-
visitor.accept(self.arel.ast, ::Arel::Collectors::
|
39
|
+
visitor.accept(self.arel.ast, ::Arel::Collectors::Composite.new(
|
40
|
+
::Arel::Collectors::SQLString.new,
|
41
|
+
::Arel::Collectors::Bind.new,
|
42
|
+
)).value.last
|
38
43
|
end
|
39
44
|
|
40
45
|
private
|
data/spec/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
begin
|
14
|
-
version =
|
14
|
+
version = 63
|
15
15
|
|
16
16
|
raise SystemExit if ActiveRecord::Migrator.current_version == version
|
17
17
|
ActiveRecord::Schema.define(version: version) do
|
@@ -74,6 +74,7 @@ begin
|
|
74
74
|
create_table "comments", force: :cascade do |t|
|
75
75
|
t.integer "user_id", null: false
|
76
76
|
t.integer "comment_id"
|
77
|
+
t.integer "video_id"
|
77
78
|
t.text "content", null: false
|
78
79
|
t.string "kind"
|
79
80
|
t.index ["user_id"], name: "index_comments_on_user_id", using: :btree
|
@@ -83,7 +84,7 @@ begin
|
|
83
84
|
create_table "courses", force: :cascade do |t|
|
84
85
|
t.string "title", null: false
|
85
86
|
t.interval "duration"
|
86
|
-
t.enum "types", subtype: :types, array: true
|
87
|
+
t.enum "types", subtype: :types, array: true
|
87
88
|
t.datetime "created_at", null: false
|
88
89
|
t.datetime "updated_at", null: false
|
89
90
|
end
|
data/spec/tests/arel_spec.rb
CHANGED
@@ -25,7 +25,9 @@ RSpec.describe 'Arel' do
|
|
25
25
|
klass_name = operator.to_s.camelize
|
26
26
|
|
27
27
|
context "##{operator}" do
|
28
|
-
let(:instance)
|
28
|
+
let(:instance) do
|
29
|
+
attribute.public_send(operator, value.is_a?(Array) ? ::Arel.array(value) : value)
|
30
|
+
end
|
29
31
|
|
30
32
|
context 'for attribute' do
|
31
33
|
let(:klass) { ::Arel::Nodes.const_get(klass_name) }
|
@@ -26,9 +26,13 @@ RSpec.describe 'BelongsToMany' do
|
|
26
26
|
let(:other) { Tag }
|
27
27
|
let(:initial) { FactoryBot.create(:tag) }
|
28
28
|
|
29
|
-
before { Video.belongs_to_many
|
29
|
+
before { Video.belongs_to_many(:tags) }
|
30
30
|
subject { Video.create(title: 'A') }
|
31
|
-
|
31
|
+
|
32
|
+
after do
|
33
|
+
Video.reset_callbacks(:save)
|
34
|
+
Video._reflections = {}
|
35
|
+
end
|
32
36
|
|
33
37
|
it 'has the method' do
|
34
38
|
expect(subject).to respond_to(:tags)
|
@@ -93,13 +97,64 @@ RSpec.describe 'BelongsToMany' do
|
|
93
97
|
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
94
98
|
end
|
95
99
|
|
100
|
+
it 'can create the owner record with direct set items' do
|
101
|
+
# Having another association would break this test due to how
|
102
|
+
# +@new_record_before_save+ is set on autosave association
|
103
|
+
Video.has_many(:comments)
|
104
|
+
|
105
|
+
record = Video.create(title: 'A', tags: [initial])
|
106
|
+
record.reload
|
107
|
+
|
108
|
+
expect(record.tags.size).to be_eql(1)
|
109
|
+
expect(record.tags.first.id).to be_eql(initial.id)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'can keep record changes accordingly' do
|
113
|
+
expect(subject.tags.count).to be_eql(0)
|
114
|
+
|
115
|
+
local_previous_changes = nil
|
116
|
+
local_saved_changes = nil
|
117
|
+
|
118
|
+
Video.after_commit do
|
119
|
+
local_previous_changes = self.previous_changes.dup
|
120
|
+
local_saved_changes = self.saved_changes.dup
|
121
|
+
end
|
122
|
+
|
123
|
+
subject.update(title: 'B')
|
124
|
+
|
125
|
+
expect(local_previous_changes).to include('title')
|
126
|
+
expect(local_saved_changes).to include('title')
|
127
|
+
|
128
|
+
subject.tags = FactoryBot.create_list(:tag, 5)
|
129
|
+
subject.update(title: 'C', url: 'X')
|
130
|
+
subject.reload
|
131
|
+
|
132
|
+
expect(local_previous_changes).to include('title', 'url')
|
133
|
+
expect(local_saved_changes).to include('title', 'url')
|
134
|
+
expect(local_previous_changes).not_to include('tag_ids')
|
135
|
+
expect(local_saved_changes).not_to include('tag_ids')
|
136
|
+
expect(subject.tag_ids.size).to be_eql(5)
|
137
|
+
expect(subject.tags.count).to be_eql(5)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'can assign the record ids during before callback' do
|
141
|
+
Video.before_save { self.tags = FactoryBot.create_list(:tag, 5) }
|
142
|
+
|
143
|
+
record = Video.create(title: 'A')
|
144
|
+
|
145
|
+
expect(Tag.count).to be_eql(5)
|
146
|
+
expect(record.tag_ids.size).to be_eql(5)
|
147
|
+
expect(record.tags.count).to be_eql(5)
|
148
|
+
end
|
149
|
+
|
96
150
|
it 'can build an associated record' do
|
97
151
|
record = subject.tags.build(name: 'Test')
|
98
152
|
expect(record).to be_a(other)
|
99
153
|
expect(record).not_to be_persisted
|
100
154
|
expect(record.name).to be_eql('Test')
|
155
|
+
expect(subject.tags.target).to be_eql([record])
|
101
156
|
|
102
|
-
expect(subject.save).to be_truthy
|
157
|
+
expect(subject.save && subject.reload).to be_truthy
|
103
158
|
expect(subject.tag_ids).to be_eql([record.id])
|
104
159
|
expect(subject.tags.size).to be_eql(1)
|
105
160
|
end
|
@@ -120,7 +175,7 @@ RSpec.describe 'BelongsToMany' do
|
|
120
175
|
expect(subject.tags.size).to be_eql(1)
|
121
176
|
|
122
177
|
subject.tags.concat(other.new(name: 'Test'))
|
123
|
-
subject.
|
178
|
+
subject.reload
|
124
179
|
|
125
180
|
expect(subject.tags.size).to be_eql(2)
|
126
181
|
expect(subject.tag_ids.size).to be_eql(2)
|
@@ -131,10 +186,27 @@ RSpec.describe 'BelongsToMany' do
|
|
131
186
|
subject.tags << FactoryBot.create(:tag)
|
132
187
|
expect(subject.tags.size).to be_eql(1)
|
133
188
|
|
134
|
-
subject.tags
|
135
|
-
|
189
|
+
subject.tags = [other.new(name: 'Test 1')]
|
190
|
+
subject.reload
|
191
|
+
|
192
|
+
expect(subject.tags.size).to be_eql(1)
|
136
193
|
expect(subject.tags[0].name).to be_eql('Test 1')
|
137
|
-
|
194
|
+
|
195
|
+
subject.tags.replace([other.new(name: 'Test 2'), other.new(name: 'Test 3')])
|
196
|
+
subject.reload
|
197
|
+
|
198
|
+
expect(subject.tags.size).to be_eql(2)
|
199
|
+
expect(subject.tags[0].name).to be_eql('Test 2')
|
200
|
+
expect(subject.tags[1].name).to be_eql('Test 3')
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'can delete specific records' do
|
204
|
+
subject.tags << initial
|
205
|
+
expect(subject.tags.size).to be_eql(1)
|
206
|
+
|
207
|
+
subject.tags.delete(initial)
|
208
|
+
expect(subject.tags.size).to be_eql(0)
|
209
|
+
expect(subject.reload.tags.size).to be_eql(0)
|
138
210
|
end
|
139
211
|
|
140
212
|
it 'can delete all records' do
|
@@ -153,6 +225,17 @@ RSpec.describe 'BelongsToMany' do
|
|
153
225
|
expect(subject.tags.size).to be_eql(0)
|
154
226
|
end
|
155
227
|
|
228
|
+
it 'can clear the array' do
|
229
|
+
record = Video.create(title: 'B', tags: [initial])
|
230
|
+
expect(record.tags.size).to be_eql(1)
|
231
|
+
|
232
|
+
record.update(tag_ids: [])
|
233
|
+
record.reload
|
234
|
+
|
235
|
+
expect(record.tag_ids).to be_nil
|
236
|
+
expect(record.tags.size).to be_eql(0)
|
237
|
+
end
|
238
|
+
|
156
239
|
it 'can have sum operations' do
|
157
240
|
records = FactoryBot.create_list(:tag, 5)
|
158
241
|
subject.tags.concat(records)
|
@@ -182,11 +265,15 @@ RSpec.describe 'BelongsToMany' do
|
|
182
265
|
it 'can check if a record is included on the list' do
|
183
266
|
outside = FactoryBot.create(:tag)
|
184
267
|
inside = FactoryBot.create(:tag)
|
268
|
+
|
269
|
+
expect(subject.tags).not_to be_include(inside)
|
270
|
+
expect(subject.tags).not_to be_include(outside)
|
271
|
+
|
185
272
|
subject.tags << inside
|
186
273
|
|
187
274
|
expect(subject.tags).to respond_to(:include?)
|
188
|
-
expect(subject.tags.
|
189
|
-
expect(subject.tags.
|
275
|
+
expect(subject.tags).to be_include(inside)
|
276
|
+
expect(subject.tags).not_to be_include(outside)
|
190
277
|
end
|
191
278
|
|
192
279
|
it 'can append records' do
|
@@ -194,6 +281,9 @@ RSpec.describe 'BelongsToMany' do
|
|
194
281
|
expect(subject.tags.size).to be_eql(1)
|
195
282
|
|
196
283
|
subject.tags << other.new(name: 'Test 2')
|
284
|
+
subject.update(title: 'B')
|
285
|
+
subject.reload
|
286
|
+
|
197
287
|
expect(subject.tags.size).to be_eql(2)
|
198
288
|
expect(subject.tags.last.name).to be_eql('Test 2')
|
199
289
|
end
|
@@ -208,10 +298,18 @@ RSpec.describe 'BelongsToMany' do
|
|
208
298
|
|
209
299
|
it 'can reload records' do
|
210
300
|
expect(subject.tags.size).to be_eql(0)
|
211
|
-
|
301
|
+
new_tag = FactoryBot.create(:tag)
|
302
|
+
subject.tags << new_tag
|
212
303
|
|
213
304
|
subject.tags.reload
|
214
305
|
expect(subject.tags.size).to be_eql(1)
|
306
|
+
expect(subject.tags.first.id).to be_eql(new_tag.id)
|
307
|
+
|
308
|
+
record = Video.create(title: 'B', tags: [new_tag])
|
309
|
+
record.reload
|
310
|
+
|
311
|
+
expect(record.tags.size).to be_eql(1)
|
312
|
+
expect(record.tags.first.id).to be_eql(new_tag.id)
|
215
313
|
end
|
216
314
|
|
217
315
|
it 'can preload records' do
|
@@ -231,11 +329,39 @@ RSpec.describe 'BelongsToMany' do
|
|
231
329
|
expect { query.load }.not_to raise_error
|
232
330
|
end
|
233
331
|
|
234
|
-
context
|
332
|
+
context 'When the attribute has a default value' do
|
333
|
+
after(:all) { Video.reset_column_information }
|
334
|
+
let(:sql) { %{ALTER TABLE "videos" ALTER COLUMN "tag_ids" SET DEFAULT '{}'::bigint[]} }
|
335
|
+
|
336
|
+
before do
|
337
|
+
Video.connection.execute(sql)
|
338
|
+
Video.reset_column_information
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'will always return the column default value' do
|
342
|
+
expect(subject.tag_ids).to be_a(Array)
|
343
|
+
expect(subject.tag_ids).to be_empty
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'will keep the value as an array even when the association is cleared' do
|
347
|
+
records = FactoryBot.create_list(:tag, 5)
|
348
|
+
subject.tags.concat(records)
|
349
|
+
|
350
|
+
subject.reload
|
351
|
+
expect(subject.tag_ids).to be_a(Array)
|
352
|
+
expect(subject.tag_ids).not_to be_empty
|
353
|
+
|
354
|
+
subject.tags.clear
|
355
|
+
subject.reload
|
356
|
+
expect(subject.tag_ids).to be_a(Array)
|
357
|
+
expect(subject.tag_ids).to be_empty
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context 'When record is not persisted' do
|
235
362
|
let(:initial) { FactoryBot.create(:tag) }
|
236
|
-
|
363
|
+
|
237
364
|
subject { Video.new(title: 'A', tags: [initial]) }
|
238
|
-
after { Video._reflections = {} }
|
239
365
|
|
240
366
|
it 'loads associated records' do
|
241
367
|
expect(subject.tags.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|