torque-postgresql 3.4.1 → 4.0.0.rc1
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/database_statements.rb +63 -84
- data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
- data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
- data/lib/torque/postgresql/adapter/oid.rb +1 -23
- data/lib/torque/postgresql/adapter/quoting.rb +13 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
- data/lib/torque/postgresql/adapter/schema_definitions.rb +36 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +90 -34
- data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
- data/lib/torque/postgresql/adapter/schema_statements.rb +64 -49
- data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
- data/lib/torque/postgresql/arel/nodes.rb +2 -2
- data/lib/torque/postgresql/arel/operations.rb +7 -1
- data/lib/torque/postgresql/arel/visitors.rb +3 -9
- data/lib/torque/postgresql/associations/association_scope.rb +23 -31
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
- data/lib/torque/postgresql/attributes/builder/full_text_search.rb +121 -0
- data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
- data/lib/torque/postgresql/attributes/builder.rb +49 -11
- data/lib/torque/postgresql/attributes/enum.rb +7 -7
- data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
- data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
- data/lib/torque/postgresql/attributes/period.rb +2 -2
- data/lib/torque/postgresql/attributes.rb +0 -4
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
- data/lib/torque/postgresql/base.rb +3 -10
- data/lib/torque/postgresql/collector.rb +1 -1
- data/lib/torque/postgresql/config.rb +95 -5
- data/lib/torque/postgresql/function.rb +61 -0
- data/lib/torque/postgresql/inheritance.rb +52 -36
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
- data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
- data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
- data/lib/torque/postgresql/predicate_builder.rb +35 -0
- data/lib/torque/postgresql/railtie.rb +112 -30
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
- data/lib/torque/postgresql/relation/inheritance.rb +4 -7
- data/lib/torque/postgresql/relation.rb +6 -10
- data/lib/torque/postgresql/schema_cache.rb +6 -12
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +2 -1
- data/spec/initialize.rb +58 -0
- data/spec/mocks/cache_query.rb +21 -21
- data/spec/mocks/create_table.rb +6 -26
- data/spec/schema.rb +19 -12
- data/spec/spec_helper.rb +5 -1
- data/spec/tests/arel_spec.rb +32 -7
- data/spec/tests/auxiliary_statement_spec.rb +3 -3
- data/spec/tests/belongs_to_many_spec.rb +72 -5
- data/spec/tests/enum_set_spec.rb +12 -11
- data/spec/tests/enum_spec.rb +4 -2
- data/spec/tests/full_text_seach_test.rb +252 -0
- data/spec/tests/function_spec.rb +42 -0
- data/spec/tests/has_many_spec.rb +21 -8
- data/spec/tests/interval_spec.rb +1 -7
- data/spec/tests/period_spec.rb +61 -61
- data/spec/tests/predicate_builder_spec.rb +132 -0
- data/spec/tests/schema_spec.rb +2 -8
- data/spec/tests/table_inheritance_spec.rb +25 -26
- metadata +34 -39
data/spec/tests/enum_spec.rb
CHANGED
@@ -70,7 +70,6 @@ RSpec.describe 'Enum' do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
context 'on value' do
|
73
|
-
subject { Enum::ContentStatus }
|
74
73
|
let(:values) { %w(created draft published archived) }
|
75
74
|
let(:error) { Torque::PostgreSQL::Attributes::Enum::EnumError }
|
76
75
|
let(:mock_enum) do
|
@@ -79,6 +78,8 @@ RSpec.describe 'Enum' do
|
|
79
78
|
klass
|
80
79
|
end
|
81
80
|
|
81
|
+
subject { Enum::ContentStatus }
|
82
|
+
|
82
83
|
it 'class exists' do
|
83
84
|
namespace = Torque::PostgreSQL.config.enum.namespace
|
84
85
|
expect(namespace.const_defined?('ContentStatus')).to be_truthy
|
@@ -341,10 +342,11 @@ RSpec.describe 'Enum' do
|
|
341
342
|
end
|
342
343
|
|
343
344
|
context 'on model' do
|
345
|
+
let(:instance) { FactoryBot.build(:user) }
|
346
|
+
|
344
347
|
before(:each) { decorate(User, :role) }
|
345
348
|
|
346
349
|
subject { User }
|
347
|
-
let(:instance) { FactoryBot.build(:user) }
|
348
350
|
|
349
351
|
it 'has all enum methods' do
|
350
352
|
expect(subject).to respond_to(:roles)
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'FullTextSearch' do
|
4
|
+
context 'on builder' do
|
5
|
+
let(:builder) { Torque::PostgreSQL::Attributes::Builder }
|
6
|
+
|
7
|
+
describe '.to_search_weights' do
|
8
|
+
it 'works with a single column' do
|
9
|
+
expect(builder.to_search_weights('title')).to eq({ 'title' => 'A' })
|
10
|
+
expect(builder.to_search_weights(:title)).to eq({ 'title' => 'A' })
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'works with an array of columns' do
|
14
|
+
value = { 'title' => 'A', 'content' => 'B' }
|
15
|
+
expect(builder.to_search_weights(%w[title content])).to eq(value)
|
16
|
+
expect(builder.to_search_weights(%i[title content])).to eq(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'works with a hash of columns and weights' do
|
20
|
+
value = { 'title' => 'A', 'content' => 'B', 'summary' => 'C' }
|
21
|
+
expect(builder.to_search_weights(value.transform_keys(&:to_sym))).to eq(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'works with a hash of columns and invalid weights' do
|
25
|
+
value = { 'title' => 'X', 'content' => 'Y', 'summary' => 'Z' }
|
26
|
+
expect(builder.to_search_weights(value.transform_keys(&:to_sym))).to eq(value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.to_search_vector_operation' do
|
31
|
+
it 'builds a simple one' do
|
32
|
+
result = builder.to_search_vector_operation('english', { 'title' => 'A' })
|
33
|
+
expect(result.to_sql).to eq("TO_TSVECTOR('english', COALESCE(title, ''))")
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'builds with 2 columns' do
|
37
|
+
columns = { 'title' => 'A', 'content' => 'B' }
|
38
|
+
result = builder.to_search_vector_operation('english', columns)
|
39
|
+
expect(result.to_sql).to eq(<<~SQL.squish)
|
40
|
+
SETWEIGHT(TO_TSVECTOR('english', COALESCE(title, '')), 'A') ||
|
41
|
+
SETWEIGHT(TO_TSVECTOR('english', COALESCE(content, '')), 'B')
|
42
|
+
SQL
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'builds with a dynamic language' do
|
46
|
+
columns = { 'title' => 'A', 'content' => 'B' }
|
47
|
+
result = builder.to_search_vector_operation(:lang, columns)
|
48
|
+
expect(result.to_sql).to eq(<<~SQL.squish)
|
49
|
+
SETWEIGHT(TO_TSVECTOR(lang, COALESCE(title, '')), 'A') ||
|
50
|
+
SETWEIGHT(TO_TSVECTOR(lang, COALESCE(content, '')), 'B')
|
51
|
+
SQL
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '.search_vector_options' do
|
56
|
+
it 'correctly translates the settings' do
|
57
|
+
options = builder.search_vector_options(columns: 'title')
|
58
|
+
expect(options).to eq(
|
59
|
+
type: :tsvector,
|
60
|
+
as: "TO_TSVECTOR('english', COALESCE(title, ''))",
|
61
|
+
stored: true,
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'properly adds the index type' do
|
66
|
+
options = builder.search_vector_options(columns: 'title', index: true)
|
67
|
+
expect(options).to eq(
|
68
|
+
type: :tsvector,
|
69
|
+
as: "TO_TSVECTOR('english', COALESCE(title, ''))",
|
70
|
+
stored: true,
|
71
|
+
index: { using: :gin },
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'on schema dumper' do
|
78
|
+
let(:connection) { ActiveRecord::Base.connection }
|
79
|
+
let(:source) { ActiveRecord::Base.connection_pool }
|
80
|
+
let(:dump_result) do
|
81
|
+
ActiveRecord::SchemaDumper.dump(source, (dump_result = StringIO.new))
|
82
|
+
dump_result.string
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'properly supports search language' do
|
86
|
+
parts = %{t.search_language "lang", default: "english", null: false}
|
87
|
+
expect(dump_result).to include(parts)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'properly translates a simple single search vector with embedded language' do
|
91
|
+
parts = 't.search_vector "search_vector", stored: true'
|
92
|
+
parts << ', language: :lang, columns: :title'
|
93
|
+
expect(dump_result).to include(parts)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'properly translates a simple multiple column search vector with language' do
|
97
|
+
parts = 't.search_vector "search_vector", stored: true'
|
98
|
+
parts << ', language: "english", columns: [:title, :content]'
|
99
|
+
expect(dump_result).to include(parts)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'supports a custom definition of weights' do
|
103
|
+
connection.create_table :custom_search do |t|
|
104
|
+
t.string :title
|
105
|
+
t.string :content
|
106
|
+
t.string :subtitle
|
107
|
+
t.search_vector :sample_a, columns: {
|
108
|
+
title: 'A',
|
109
|
+
subtitle: 'A',
|
110
|
+
content: 'B',
|
111
|
+
}
|
112
|
+
t.search_vector :sample_b, columns: {
|
113
|
+
title: 'A',
|
114
|
+
subtitle: 'C',
|
115
|
+
content: 'D',
|
116
|
+
}
|
117
|
+
t.search_vector :sample_c, columns: {
|
118
|
+
title: 'C',
|
119
|
+
subtitle: 'B',
|
120
|
+
content: 'A',
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
parts = 't.search_vector "sample_a", stored: true'
|
125
|
+
parts << ', language: "english", columns: { title: "A", subtitle: "A", content: "B" }'
|
126
|
+
expect(dump_result).to include(parts)
|
127
|
+
|
128
|
+
parts = 't.search_vector "sample_b", stored: true'
|
129
|
+
parts << ', language: "english", columns: { title: "A", subtitle: "C", content: "D" }'
|
130
|
+
expect(dump_result).to include(parts)
|
131
|
+
|
132
|
+
parts = 't.search_vector "sample_c", stored: true'
|
133
|
+
parts << ', language: "english", columns: [:content, :subtitle, :title]'
|
134
|
+
expect(dump_result).to include(parts)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'on config' do
|
139
|
+
let(:base) { Course }
|
140
|
+
let(:scope) { 'full_text_search' }
|
141
|
+
|
142
|
+
let(:mod) { base.singleton_class.included_modules.first }
|
143
|
+
|
144
|
+
after { mod.send(:undef_method, scope) if scope.present? }
|
145
|
+
|
146
|
+
it 'has the initialization method' do
|
147
|
+
scope.replace('')
|
148
|
+
expect(base).to respond_to(:torque_search_for)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'properly generates the search scope' do
|
152
|
+
base.torque_search_for(:search_vector)
|
153
|
+
expect(base.all).to respond_to(:full_text_search)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'works with prefix and suffix' do
|
157
|
+
scope.replace('custom_full_text_search_scope')
|
158
|
+
base.torque_search_for(:search_vector, prefix: 'custom', suffix: 'scope')
|
159
|
+
expect(base.all).to respond_to(:custom_full_text_search_scope)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'on relation' do
|
164
|
+
let(:base) { Course }
|
165
|
+
let(:scope) { 'full_text_search' }
|
166
|
+
|
167
|
+
let(:mod) { base.singleton_class.included_modules.first }
|
168
|
+
|
169
|
+
before { Course.torque_search_for(:search_vector) }
|
170
|
+
after { mod.send(:undef_method, :full_text_search) }
|
171
|
+
|
172
|
+
it 'performs a simple query' do
|
173
|
+
result = Course.full_text_search('test')
|
174
|
+
parts = 'SELECT "courses".* FROM "courses"'
|
175
|
+
parts << ' WHERE "courses"."search_vector" @@'
|
176
|
+
parts << " PHRASETO_TSQUERY('english', 'test')"
|
177
|
+
expect(result.to_sql).to eql(parts)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'can include the order' do
|
181
|
+
result = Course.full_text_search('test', order: true)
|
182
|
+
parts = 'SELECT "courses".* FROM "courses"'
|
183
|
+
parts << ' WHERE "courses"."search_vector" @@'
|
184
|
+
parts << " PHRASETO_TSQUERY('english', 'test')"
|
185
|
+
parts << ' ORDER BY TS_RANK("courses"."search_vector",'
|
186
|
+
parts << " PHRASETO_TSQUERY('english', 'test')) ASC"
|
187
|
+
expect(result.to_sql).to eql(parts)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'can include the order descending' do
|
191
|
+
result = Course.full_text_search('test', order: :desc)
|
192
|
+
parts = 'SELECT "courses".* FROM "courses"'
|
193
|
+
parts << ' WHERE "courses"."search_vector" @@'
|
194
|
+
parts << " PHRASETO_TSQUERY('english', 'test')"
|
195
|
+
parts << ' ORDER BY TS_RANK("courses"."search_vector",'
|
196
|
+
parts << " PHRASETO_TSQUERY('english', 'test')) DESC"
|
197
|
+
expect(result.to_sql).to eql(parts)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'can include the rank' do
|
201
|
+
result = Course.full_text_search('test', rank: true)
|
202
|
+
parts = 'SELECT "courses".*, TS_RANK("courses"."search_vector",'
|
203
|
+
parts << " PHRASETO_TSQUERY('english', 'test')) AS rank"
|
204
|
+
parts << ' FROM "courses" WHERE "courses"."search_vector" @@'
|
205
|
+
parts << " PHRASETO_TSQUERY('english', 'test')"
|
206
|
+
expect(result.to_sql).to eql(parts)
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'can include the rank named differently' do
|
210
|
+
result = Course.full_text_search('test', rank: :custom_rank)
|
211
|
+
parts = 'SELECT "courses".*, TS_RANK("courses"."search_vector",'
|
212
|
+
parts << " PHRASETO_TSQUERY('english', 'test')) AS custom_rank"
|
213
|
+
parts << ' FROM "courses" WHERE "courses"."search_vector" @@'
|
214
|
+
parts << " PHRASETO_TSQUERY('english', 'test')"
|
215
|
+
expect(result.to_sql).to eql(parts)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'can use regular query mode' do
|
219
|
+
result = Course.full_text_search('test', phrase: false)
|
220
|
+
parts = 'SELECT "courses".* FROM "courses"'
|
221
|
+
parts << ' WHERE "courses"."search_vector" @@'
|
222
|
+
parts << " TO_TSQUERY('english', 'test')"
|
223
|
+
expect(result.to_sql).to eql(parts)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'can use a attribute as the language' do
|
227
|
+
result = Course.full_text_search('test', language: :lang)
|
228
|
+
parts = 'SELECT "courses".* FROM "courses"'
|
229
|
+
parts << ' WHERE "courses"."search_vector" @@'
|
230
|
+
parts << %{ PHRASETO_TSQUERY("courses"."lang", 'test')}
|
231
|
+
expect(result.to_sql).to eql(parts)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'can call a method to pull the language' do
|
235
|
+
Course.define_singleton_method(:search_language) { 'portuguese' }
|
236
|
+
result = Course.full_text_search('test', language: :search_language)
|
237
|
+
parts = 'SELECT "courses".* FROM "courses"'
|
238
|
+
parts << ' WHERE "courses"."search_vector" @@'
|
239
|
+
parts << " PHRASETO_TSQUERY('portuguese', 'test')"
|
240
|
+
expect(result.to_sql).to eql(parts)
|
241
|
+
Course.singleton_class.undef_method(:search_language)
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'properly binds all provided values' do
|
245
|
+
query = Course.full_text_search('test')
|
246
|
+
sql, binds = get_query_with_binds { query.load }
|
247
|
+
expect(sql).to include("PHRASETO_TSQUERY($1, $2)")
|
248
|
+
expect(binds.first.value).to eq('english')
|
249
|
+
expect(binds.second.value).to eq('test')
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Function' do
|
4
|
+
let(:helper) { Torque::PostgreSQL::FN }
|
5
|
+
let(:conn) { ActiveRecord::Base.connection }
|
6
|
+
let(:visitor) { ::Arel::Visitors::PostgreSQL.new(conn) }
|
7
|
+
let(:collector) { ::Arel::Collectors::SQLString }
|
8
|
+
|
9
|
+
context 'on helper' do
|
10
|
+
it 'helps creating a bind' do
|
11
|
+
type = ::ActiveRecord::Type::String.new
|
12
|
+
expect(helper.bind(:foo, 'test', type)).to be_a(::Arel::Nodes::BindParam)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'helps creating a bind for a model attribute' do
|
16
|
+
expect(helper.bind_for(Video, :title, 'test')).to be_a(::Arel::Nodes::BindParam)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'helps creating a bind for an arel attribute' do
|
20
|
+
attr = Video.arel_table['title']
|
21
|
+
expect(helper.bind_with(attr, 'test')).to be_a(::Arel::Nodes::BindParam)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'helps concatenating arguments' do
|
25
|
+
values = %w[a b c].map(&::Arel.method(:sql))
|
26
|
+
|
27
|
+
# Unable to just call .sql with a simple thing
|
28
|
+
visited = visitor.accept(helper.concat(values[0]), collector.new)
|
29
|
+
expect(visited.value).to eq("a")
|
30
|
+
|
31
|
+
# 2+ we can call .sql directly
|
32
|
+
expect(helper.concat(values[0], values[1]).to_sql).to eq("a || b")
|
33
|
+
expect(helper.concat(values[0], values[1], values[2]).to_sql).to eq("a || b || c")
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'helps building any other function' do
|
37
|
+
values = %w[a b c].map(&::Arel.method(:sql))
|
38
|
+
expect(helper).to respond_to(:coalesce)
|
39
|
+
expect(helper.coalesce(values[0], values[1]).to_sql).to eq("COALESCE(a, b)")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/tests/has_many_spec.rb
CHANGED
@@ -11,7 +11,7 @@ RSpec.describe 'HasMany' do
|
|
11
11
|
|
12
12
|
context 'on original' do
|
13
13
|
let(:other) { Text }
|
14
|
-
let(:key) {
|
14
|
+
let(:key) { :texts }
|
15
15
|
|
16
16
|
before { User.has_many :texts }
|
17
17
|
subject { User.create(name: 'User 1') }
|
@@ -247,7 +247,7 @@ RSpec.describe 'HasMany' do
|
|
247
247
|
|
248
248
|
context 'on array' do
|
249
249
|
let(:other) { Video }
|
250
|
-
let(:key) {
|
250
|
+
let(:key) { :videos }
|
251
251
|
|
252
252
|
before { Tag.has_many :videos, array: true }
|
253
253
|
subject { Tag.create(name: 'A') }
|
@@ -264,15 +264,21 @@ RSpec.describe 'HasMany' do
|
|
264
264
|
end
|
265
265
|
|
266
266
|
it 'loads associated records' do
|
267
|
-
expect(subject.videos.to_sql).to
|
268
|
-
SELECT "videos"
|
269
|
-
WHERE \\(?"videos"\\."tag_ids" && ARRAY\\[#{subject.id}\\]::bigint\\[\\]\\)?
|
267
|
+
expect(subject.videos.to_sql).to eq(<<~SQL.squish)
|
268
|
+
SELECT "videos".* FROM "videos" WHERE #{subject.id} = ANY("videos"."tag_ids")
|
270
269
|
SQL
|
271
270
|
|
272
271
|
expect(subject.videos.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
273
272
|
expect(subject.videos.to_a).to be_eql([])
|
274
273
|
end
|
275
274
|
|
275
|
+
it 'uses binds instead of the literal value' do
|
276
|
+
query = subject.videos
|
277
|
+
sql, binds = get_query_with_binds { query.load }
|
278
|
+
expect(sql).to include('WHERE $1 = ANY("videos"."tag_ids")')
|
279
|
+
expect(binds.first.value).to eq(subject.id)
|
280
|
+
end
|
281
|
+
|
276
282
|
it 'can be marked as loaded' do
|
277
283
|
expect(subject.videos.loaded?).to be_eql(false)
|
278
284
|
expect(subject.videos).to respond_to(:load_target)
|
@@ -474,15 +480,22 @@ RSpec.describe 'HasMany' do
|
|
474
480
|
subject { player.create }
|
475
481
|
|
476
482
|
it 'loads associated records' do
|
477
|
-
expect(subject.games.to_sql).to
|
478
|
-
SELECT "games"
|
479
|
-
WHERE
|
483
|
+
expect(subject.games.to_sql).to eq(<<~SQL.squish)
|
484
|
+
SELECT "games".* FROM "games"
|
485
|
+
WHERE '#{subject.id}' = ANY("games"."player_ids")
|
480
486
|
SQL
|
481
487
|
|
482
488
|
expect(subject.games.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
483
489
|
expect(subject.games.to_a).to be_eql([])
|
484
490
|
end
|
485
491
|
|
492
|
+
it 'uses binds instead of the literal value' do
|
493
|
+
query = subject.games
|
494
|
+
sql, binds = get_query_with_binds { query.load }
|
495
|
+
expect(sql).to include('WHERE $1 = ANY("games"."player_ids")')
|
496
|
+
expect(binds.first.value).to eq(subject.id)
|
497
|
+
end
|
498
|
+
|
486
499
|
it 'can preload records' do
|
487
500
|
5.times { game.create(player_ids: [subject.id]) }
|
488
501
|
entries = player.all.includes(:games).load
|
data/spec/tests/interval_spec.rb
CHANGED
@@ -3,13 +3,7 @@ require 'spec_helper'
|
|
3
3
|
RSpec.describe 'Interval' do
|
4
4
|
let(:table_definition) { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition }
|
5
5
|
let(:connection) { ActiveRecord::Base.connection }
|
6
|
-
let(:source)
|
7
|
-
if Torque::PostgreSQL::AR720
|
8
|
-
ActiveRecord::Base.connection_pool
|
9
|
-
else
|
10
|
-
ActiveRecord::Base.connection
|
11
|
-
end
|
12
|
-
end
|
6
|
+
let(:source) { ActiveRecord::Base.connection_pool }
|
13
7
|
|
14
8
|
context 'on settings' do
|
15
9
|
it 'must be set to ISO 8601' do
|