torque-postgresql 2.0.1 → 2.0.6
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.rb +7 -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 +2 -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 +11 -2
- 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 +164 -47
- 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 +5 -3
- data/lib/torque/postgresql/attributes/builder/period.rb +6 -4
- data/lib/torque/postgresql/attributes/enum.rb +5 -10
- data/lib/torque/postgresql/attributes/enum_set.rb +2 -0
- 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 +5 -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/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 +18 -4
- 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 +104 -12
- data/spec/tests/enum_set_spec.rb +1 -1
- data/spec/tests/has_many_spec.rb +25 -1
- data/spec/tests/table_inheritance_spec.rb +1 -1
- metadata +7 -7
@@ -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
|
module Torque
|
2
4
|
module PostgreSQL
|
3
5
|
module Reflection
|
@@ -10,6 +12,10 @@ module Torque
|
|
10
12
|
true
|
11
13
|
end
|
12
14
|
|
15
|
+
def belongs_to?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
13
19
|
def collection?
|
14
20
|
true
|
15
21
|
end
|
@@ -19,7 +25,7 @@ module Torque
|
|
19
25
|
end
|
20
26
|
|
21
27
|
def foreign_key
|
22
|
-
@foreign_key ||= options[:
|
28
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
|
23
29
|
end
|
24
30
|
|
25
31
|
def association_foreign_key
|
@@ -27,16 +33,24 @@ module Torque
|
|
27
33
|
end
|
28
34
|
|
29
35
|
def active_record_primary_key
|
30
|
-
@active_record_primary_key ||= options[:
|
36
|
+
@active_record_primary_key ||= options[:primary_key] || derive_primary_key
|
37
|
+
end
|
38
|
+
|
39
|
+
def join_primary_key(*)
|
40
|
+
active_record_primary_key
|
41
|
+
end
|
42
|
+
|
43
|
+
def join_foreign_key
|
44
|
+
foreign_key
|
31
45
|
end
|
32
46
|
|
33
47
|
private
|
34
48
|
|
35
|
-
def
|
49
|
+
def derive_primary_key
|
36
50
|
klass.primary_key
|
37
51
|
end
|
38
52
|
|
39
|
-
def
|
53
|
+
def derive_foreign_key
|
40
54
|
"#{name.to_s.singularize}_ids"
|
41
55
|
end
|
42
56
|
end
|
@@ -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,15 +26,24 @@ 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)
|
35
39
|
expect(subject._reflections).to include('tags')
|
36
40
|
end
|
37
41
|
|
42
|
+
it 'has correct foreign key' do
|
43
|
+
item = subject._reflections['tags']
|
44
|
+
expect(item.foreign_key).to be_eql('tag_ids')
|
45
|
+
end
|
46
|
+
|
38
47
|
it 'loads associated records' do
|
39
48
|
subject.update(tag_ids: [initial.id])
|
40
49
|
expect(subject.tags.to_sql).to be_eql(<<-SQL.squish)
|
@@ -88,13 +97,64 @@ RSpec.describe 'BelongsToMany' do
|
|
88
97
|
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
89
98
|
end
|
90
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
|
+
|
91
150
|
it 'can build an associated record' do
|
92
151
|
record = subject.tags.build(name: 'Test')
|
93
152
|
expect(record).to be_a(other)
|
94
153
|
expect(record).not_to be_persisted
|
95
154
|
expect(record.name).to be_eql('Test')
|
155
|
+
expect(subject.tags.target).to be_eql([record])
|
96
156
|
|
97
|
-
expect(subject.save).to be_truthy
|
157
|
+
expect(subject.save && subject.reload).to be_truthy
|
98
158
|
expect(subject.tag_ids).to be_eql([record.id])
|
99
159
|
expect(subject.tags.size).to be_eql(1)
|
100
160
|
end
|
@@ -115,7 +175,8 @@ RSpec.describe 'BelongsToMany' do
|
|
115
175
|
expect(subject.tags.size).to be_eql(1)
|
116
176
|
|
117
177
|
subject.tags.concat(other.new(name: 'Test'))
|
118
|
-
subject.
|
178
|
+
subject.reload
|
179
|
+
|
119
180
|
expect(subject.tags.size).to be_eql(2)
|
120
181
|
expect(subject.tag_ids.size).to be_eql(2)
|
121
182
|
expect(subject.tags.last.name).to be_eql('Test')
|
@@ -125,10 +186,27 @@ RSpec.describe 'BelongsToMany' do
|
|
125
186
|
subject.tags << FactoryBot.create(:tag)
|
126
187
|
expect(subject.tags.size).to be_eql(1)
|
127
188
|
|
128
|
-
subject.tags
|
129
|
-
|
189
|
+
subject.tags = [other.new(name: 'Test 1')]
|
190
|
+
subject.reload
|
191
|
+
|
192
|
+
expect(subject.tags.size).to be_eql(1)
|
130
193
|
expect(subject.tags[0].name).to be_eql('Test 1')
|
131
|
-
|
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)
|
132
210
|
end
|
133
211
|
|
134
212
|
it 'can delete all records' do
|
@@ -176,11 +254,15 @@ RSpec.describe 'BelongsToMany' do
|
|
176
254
|
it 'can check if a record is included on the list' do
|
177
255
|
outside = FactoryBot.create(:tag)
|
178
256
|
inside = FactoryBot.create(:tag)
|
257
|
+
|
258
|
+
expect(subject.tags).not_to be_include(inside)
|
259
|
+
expect(subject.tags).not_to be_include(outside)
|
260
|
+
|
179
261
|
subject.tags << inside
|
180
262
|
|
181
263
|
expect(subject.tags).to respond_to(:include?)
|
182
|
-
expect(subject.tags.
|
183
|
-
expect(subject.tags.
|
264
|
+
expect(subject.tags).to be_include(inside)
|
265
|
+
expect(subject.tags).not_to be_include(outside)
|
184
266
|
end
|
185
267
|
|
186
268
|
it 'can append records' do
|
@@ -188,6 +270,9 @@ RSpec.describe 'BelongsToMany' do
|
|
188
270
|
expect(subject.tags.size).to be_eql(1)
|
189
271
|
|
190
272
|
subject.tags << other.new(name: 'Test 2')
|
273
|
+
subject.update(title: 'B')
|
274
|
+
subject.reload
|
275
|
+
|
191
276
|
expect(subject.tags.size).to be_eql(2)
|
192
277
|
expect(subject.tags.last.name).to be_eql('Test 2')
|
193
278
|
end
|
@@ -202,10 +287,18 @@ RSpec.describe 'BelongsToMany' do
|
|
202
287
|
|
203
288
|
it 'can reload records' do
|
204
289
|
expect(subject.tags.size).to be_eql(0)
|
205
|
-
|
290
|
+
new_tag = FactoryBot.create(:tag)
|
291
|
+
subject.tags << new_tag
|
206
292
|
|
207
293
|
subject.tags.reload
|
208
294
|
expect(subject.tags.size).to be_eql(1)
|
295
|
+
expect(subject.tags.first.id).to be_eql(new_tag.id)
|
296
|
+
|
297
|
+
record = Video.create(title: 'B', tags: [new_tag])
|
298
|
+
record.reload
|
299
|
+
|
300
|
+
expect(record.tags.size).to be_eql(1)
|
301
|
+
expect(record.tags.first.id).to be_eql(new_tag.id)
|
209
302
|
end
|
210
303
|
|
211
304
|
it 'can preload records' do
|
@@ -227,9 +320,8 @@ RSpec.describe 'BelongsToMany' do
|
|
227
320
|
|
228
321
|
context "When record is not persisted" do
|
229
322
|
let(:initial) { FactoryBot.create(:tag) }
|
230
|
-
|
323
|
+
|
231
324
|
subject { Video.new(title: 'A', tags: [initial]) }
|
232
|
-
after { Video._reflections = {} }
|
233
325
|
|
234
326
|
it 'loads associated records' do
|
235
327
|
expect(subject.tags.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|