order_query 0.3.3 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OrderQuery
2
- VERSION = '0.3.3'
4
+ VERSION = '0.5.1'
3
5
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../../'
6
+
7
+ gem 'activerecord', '~> 5.0.6'
8
+ gem 'activesupport', '~> 5.0.6'
9
+
10
+ platforms :mri, :rbx do
11
+ # https://github.com/rails/rails/blob/v5.0.6/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L4
12
+ gem 'mysql2', '< 0.5'
13
+
14
+ # https://github.com/rails/rails/blob/v5.0.6/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L2
15
+ gem 'pg', '~> 0.18'
16
+
17
+ # https://github.com/rails/rails/blob/v5.0.6/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L7
18
+ gem 'sqlite3', '~> 1.3.6'
19
+ end
20
+
21
+ eval_gemfile '../../shared.gemfile'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../../'
6
+
7
+ gem 'activerecord', '~> 5.1.3'
8
+ gem 'activesupport', '~> 5.1.3'
9
+
10
+ platforms :mri, :rbx do
11
+ # https://github.com/rails/rails/blob/v5.1.5/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L4
12
+ gem 'mysql2', '< 0.5'
13
+
14
+ # https://github.com/rails/rails/blob/v5.1.5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L2
15
+ gem 'pg', '>= 0.18', '< 2.0'
16
+
17
+ # https://github.com/rails/rails/blob/v5.1.5/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L10
18
+ gem 'sqlite3', '~> 1.3.6'
19
+ end
20
+
21
+ eval_gemfile '../../shared.gemfile'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../../'
6
+
7
+ gem 'activerecord', '~> 5.2.3'
8
+ gem 'activesupport', '~> 5.2.3'
9
+
10
+ platforms :mri, :rbx do
11
+ # https://github.com/rails/rails/blob/v5.2.3/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
12
+ gem 'mysql2', '>= 0.4.4', '< 0.6.0'
13
+
14
+ # https://github.com/rails/rails/blob/v5.2.3/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
15
+ gem 'pg', '>= 0.18', '< 2.0'
16
+
17
+ # https://github.com/rails/rails/blob/v5.2.3/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L12
18
+ gem 'sqlite3', '~> 1.3', '>= 1.3.6'
19
+ end
20
+
21
+ eval_gemfile '../../shared.gemfile'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../../'
6
+
7
+ gem 'activerecord', '~> 6.0.3'
8
+ gem 'activesupport', '~> 6.0.3'
9
+
10
+ platforms :mri, :rbx do
11
+ # https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
12
+ gem 'sqlite3', '~> 1.4'
13
+
14
+ # https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
15
+ gem 'pg', '>= 0.18', '< 2.0'
16
+
17
+ # https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
18
+ gem 'mysql2', '>= 0.4.4'
19
+ end
20
+
21
+ eval_gemfile '../../shared.gemfile'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../../'
6
+
7
+ gem 'activerecord', '~> 6.1.1'
8
+ gem 'activesupport', '~> 6.1.1'
9
+
10
+ platforms :mri, :rbx do
11
+ # https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
12
+ gem 'sqlite3', '~> 1.4'
13
+
14
+ # https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
15
+ gem 'pg', '>= 0.18', '< 2.0'
16
+
17
+ # https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
18
+ gem 'mysql2', '>= 0.4.4'
19
+ end
20
+
21
+ eval_gemfile '../../shared.gemfile'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ eval_gemfile '../../rubocop.gemfile'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  # Bare model
@@ -10,23 +12,23 @@ class Post < ActiveRecord::Base
10
12
  include OrderQuery
11
13
  order_query :order_list,
12
14
  [:pinned, [true, false]],
13
- [:published_at, :desc],
14
- [:id, :desc]
15
+ %i[published_at desc],
16
+ %i[id desc]
15
17
  end
16
18
 
17
19
  def create_post(attr = {})
18
- Post.create!({pinned: false, published_at: Time.now}.merge(attr))
20
+ Post.create!({ pinned: false, published_at: Time.now }.merge(attr))
19
21
  end
20
22
 
21
23
  # Advanced model
22
24
  class Issue < ActiveRecord::Base
23
25
  DISPLAY_ORDER = [
24
- [:pinned, [true, false]],
25
- [:priority, %w(high medium low)],
26
- [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
27
- [:updated_at, :desc],
28
- [:id, :desc]
29
- ]
26
+ [:pinned, [true, false]],
27
+ [:priority, %w[high medium low]],
28
+ [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
29
+ %i[updated_at desc],
30
+ %i[id desc]
31
+ ].freeze
30
32
 
31
33
  def valid_votes_count
32
34
  votes - suspicious_votes
@@ -34,11 +36,14 @@ class Issue < ActiveRecord::Base
34
36
 
35
37
  include OrderQuery
36
38
  order_query :display_order, DISPLAY_ORDER
37
- order_query :id_order_asc, [[:id, :asc]]
39
+ order_query :id_order_asc, [%i[id asc]]
38
40
  end
39
41
 
40
42
  def create_issue(attr = {})
41
- Issue.create!({priority: 'high', votes: 3, suspicious_votes: 0, updated_at: Time.now}.merge(attr))
43
+ Issue.create!(
44
+ { priority: 'high', votes: 3, suspicious_votes: 0, updated_at: Time.now }
45
+ .merge(attr)
46
+ )
42
47
  end
43
48
 
44
49
  def wrap_top_level_or(value)
@@ -54,53 +59,84 @@ def wrap_top_level_or(value)
54
59
  end
55
60
  end
56
61
 
57
- describe 'OrderQuery' do
62
+ RSpec.describe 'OrderQuery' do
63
+ context 'Column' do
64
+ it 'fails with ArgumentError if invalid vals_and_or_dir is passed' do
65
+ expect do
66
+ OrderQuery::Column.new(Post.all, :pinned, :desc, :extra)
67
+ end.to raise_error(ArgumentError)
68
+ end
69
+ end
70
+
71
+ context 'Point' do
72
+ context '#value' do
73
+ it 'fails if nil on non-nullable column' do
74
+ post = OpenStruct.new
75
+ post.pinned = nil
76
+ space = Post.seek([:pinned])
77
+ expect do
78
+ OrderQuery::Point.new(post, space)
79
+ .value(space.columns.find { |c| c.name == :pinned })
80
+ end.to raise_error(OrderQuery::Errors::NonNullableColumnIsNullError)
81
+ end
82
+ end
83
+ end
58
84
 
59
85
  [false, true].each do |wrap_top_level_or|
60
86
  context "(wtlo: #{wrap_top_level_or})" do
61
87
  wrap_top_level_or wrap_top_level_or
62
88
 
63
89
  context 'Issue test model' do
64
- t = Time.now
65
- datasets = [
90
+ datasets = lambda {
91
+ t = Time.now
92
+ [
66
93
  [
67
- ['high', 5, 0, t, true],
68
- ['high', 5, 1, t, true],
69
- ['high', 5, 0, t],
70
- ['high', 5, 0, t - 1.day],
71
- ['high', 5, 1, t],
72
- ['medium', 10, 0, t],
73
- ['medium', 10, 5, t - 12.hours],
74
- ['low', 30, 0, t + 1.day]
94
+ ['high', 5, 0, t, true],
95
+ ['high', 5, 1, t, true],
96
+ ['high', 5, 0, t],
97
+ ['high', 5, 0, t - 1.day],
98
+ ['high', 5, 1, t],
99
+ ['medium', 10, 0, t],
100
+ ['medium', 10, 5, t - 12.hours],
101
+ ['low', 30, 0, t + 1.day]
75
102
  ],
76
103
  [
77
- ['high', 5, 0, t],
78
- ['high', 5, 1, t],
79
- ['high', 5, 1, t - 1.day],
80
- ['low', 30, 0, t + 1.day]
104
+ ['high', 5, 0, t],
105
+ ['high', 5, 1, t],
106
+ ['high', 5, 1, t - 1.day],
107
+ ['low', 30, 0, t + 1.day]
81
108
  ],
82
109
  [
83
- ['high', 5, 1, t - 1.day],
84
- ['low', 30, 0, t + 1.day]
85
- ],
86
- ]
110
+ ['high', 5, 1, t - 1.day],
111
+ ['low', 30, 0, t + 1.day]
112
+ ]
113
+ ]
114
+ }.call
87
115
 
88
116
  datasets.each_with_index do |ds, i|
89
117
  it "is ordered correctly (test data #{i})" do
90
118
  issues = ds.map do |attr|
91
- Issue.new(priority: attr[0], votes: attr[1], suspicious_votes: attr[2], updated_at: attr[3], pinned: attr[4] || false)
119
+ Issue.new(priority: attr[0], votes: attr[1],
120
+ suspicious_votes: attr[2], updated_at: attr[3],
121
+ pinned: attr[4] || false)
92
122
  end
93
123
  issues.shuffle.reverse_each(&:save!)
94
124
  expect(Issue.display_order.to_a).to eq(issues)
95
125
  expect(Issue.display_order_reverse.to_a).to eq(issues.reverse)
96
- issues.zip(issues.rotate).each_with_index do |(cur, nxt), i|
97
- expect(cur.display_order.position).to eq(i + 1)
126
+ issues.zip(issues.rotate).each_with_index do |(cur, nxt), j|
127
+ expect(cur.display_order.position).to eq(j + 1)
98
128
  expect(cur.display_order.next).to eq(nxt)
99
129
  expect(Issue.display_order_at(cur).next).to eq nxt
100
130
  expect(cur.display_order.space.count).to eq(Issue.count)
101
- expect(cur.display_order.before.count + 1 + cur.display_order.after.count).to eq(nxt.display_order.count)
131
+ expect(
132
+ cur.display_order.before.count + 1 +
133
+ cur.display_order.after.count
134
+ ).to eq(nxt.display_order.count)
102
135
  expect(nxt.display_order.previous).to eq(cur)
103
- expect(nxt.display_order.before.to_a.reverse + [nxt] + nxt.display_order.after.to_a).to eq(Issue.display_order.to_a)
136
+ expect(
137
+ nxt.display_order.before.to_a.reverse + [nxt] +
138
+ nxt.display_order.after.to_a
139
+ ).to eq(Issue.display_order.to_a)
104
140
  end
105
141
  end
106
142
  end
@@ -117,16 +153,20 @@ describe 'OrderQuery' do
117
153
  expect(a.id_order_asc.next).to eq b
118
154
  expect(b.id_order_asc.previous).to eq a
119
155
  expect([a] + a.id_order_asc.after.to_a).to eq(Issue.id_order_asc.to_a)
120
- expect(b.id_order_asc.before.reverse.to_a + [b]).to eq(Issue.id_order_asc.to_a)
156
+ expect(b.id_order_asc.before.reverse.to_a + [b]).to(
157
+ eq Issue.id_order_asc.to_a
158
+ )
121
159
  expect(Issue.id_order_asc.count).to eq(2)
122
160
  end
123
161
 
124
162
  it '.seek works on a list of ids' do
125
- ids = 3.times.map { create_issue.id }
163
+ ids = Array.new(3) { create_issue.id }
126
164
  expect(Issue.seek([[:id, ids]]).count).to eq ids.length
127
165
  expect(Issue.seek([:id, ids]).count).to eq ids.length
128
166
  expect(Issue.seek([:id, ids]).scope.pluck(:id)).to eq ids
129
- expect(Issue.seek([:id, ids]).scope_reverse.pluck(:id)).to eq ids.reverse
167
+ expect(Issue.seek([:id, ids]).scope_reverse.pluck(:id)).to(
168
+ eq(ids.reverse)
169
+ )
130
170
  end
131
171
 
132
172
  context 'partitioned on a boolean flag' do
@@ -136,7 +176,7 @@ describe 'OrderQuery' do
136
176
  create_issue(active: true)
137
177
  end
138
178
 
139
- let!(:order) { [[:id, :desc]] }
179
+ let!(:order) { [%i[id desc]] }
140
180
  let!(:active) { Issue.where(active: true).seek(order) }
141
181
  let!(:inactive) { Issue.where(active: false).seek(order) }
142
182
 
@@ -172,7 +212,38 @@ describe 'OrderQuery' do
172
212
  it '#seek falls back to scope when order column is missing self' do
173
213
  a = create_issue(priority: 'medium')
174
214
  b = create_issue(priority: 'high')
175
- expect(a.seek(Issue.display_order, [[:priority, ['wontfix', 'askbob']], [:id, :desc]]).next).to eq(b)
215
+ expect(
216
+ a.seek(
217
+ Issue.display_order,
218
+ [[:priority, %w[wontfix askbob]], %i[id desc]]
219
+ ).next
220
+ ).to eq(b)
221
+ end
222
+
223
+ context 'nil in string enum' do
224
+ display = ->(issue) { "##{issue.id}-#{issue.priority || 'NULL'}" }
225
+ priorities = [nil, 'low', 'medium', 'high']
226
+ let!(:issues) do
227
+ priorities.flat_map do |p|
228
+ [create_issue(priority: p), create_issue(priority: p)]
229
+ end
230
+ end
231
+ priorities.permutation do |perm|
232
+ it "works for #{perm} (desc)" do
233
+ expect_order(
234
+ Issue.seek([:priority, perm]),
235
+ issues.sort_by { |x| [perm.index(x.priority), x.id] },
236
+ &display
237
+ )
238
+ end
239
+ it "works for #{perm} (asc)" do
240
+ expect_order(
241
+ Issue.seek([:priority, perm, :asc]),
242
+ issues.sort_by { |x| [perm.index(x.priority), -x.id] }.reverse,
243
+ &display
244
+ )
245
+ end
246
+ end
176
247
  end
177
248
 
178
249
  before do
@@ -217,28 +288,33 @@ describe 'OrderQuery' do
217
288
 
218
289
  context '#inspect' do
219
290
  it 'Column' do
220
- expect(OrderQuery::Column.new([:id, :desc], Post).inspect).to eq '(id unique desc)'
221
- expect(OrderQuery::Column.new([:virtual, :desc, sql: 'SIN(id)'], Post).inspect).to eq '(virtual SIN(id) desc)'
291
+ expect(OrderQuery::Column.new(Post, :id, :desc).inspect)
292
+ .to eq '(id unique desc)'
293
+ expect(
294
+ OrderQuery::Column.new(Post, :virtual, :desc, sql: 'SIN(id)')
295
+ .inspect
296
+ ).to eq '(virtual SIN(id) desc)'
222
297
  end
223
298
 
224
- let(:space) {
299
+ let(:space) do
225
300
  OrderQuery::Space.new(Post, [[:pinned, [true, false]]])
226
- }
301
+ end
227
302
 
228
303
  it 'Point' do
229
- post = create_post
304
+ post = create_post
230
305
  point = OrderQuery::Point.new(post, space)
231
- expect(point.inspect).to(
232
- eq %Q(#<OrderQuery::Point @record=#<Post id: #{post.id}, pinned: false, published_at: #{post.attribute_for_inspect(:published_at)}> @space=#<OrderQuery::Space @columns=[(pinned [true, false] desc), (id unique asc)] @base_scope=Post(id: integer, pinned: boolean, published_at: datetime)>>)
233
- )
306
+ # rubocop:disable Metrics/LineLength
307
+ expect(point.inspect).to eq %(#<OrderQuery::Point @record=#<Post id: #{post.id}, title: nil, pinned: false, published_at: #{post.attribute_for_inspect(:published_at)}> @space=#<OrderQuery::Space @columns=[(pinned [true, false] desc), (id unique asc)] @base_scope=Post(id: integer, title: string, pinned: boolean, published_at: datetime)>>)
308
+ # rubocop:enable Metrics/LineLength
234
309
  end
235
310
 
236
311
  it 'Space' do
237
- expect(space.inspect).to eq '#<OrderQuery::Space @columns=[(pinned [true, false] desc), (id unique asc)] @base_scope=Post(id: integer, pinned: boolean, published_at: datetime)>'
312
+ # rubocop:disable Metrics/LineLength
313
+ expect(space.inspect).to eq '#<OrderQuery::Space @columns=[(pinned [true, false] desc), (id unique asc)] @base_scope=Post(id: integer, title: string, pinned: boolean, published_at: datetime)>'
314
+ # rubocop:enable Metrics/LineLength
238
315
  end
239
316
  end
240
317
 
241
-
242
318
  context 'boolean enum order' do
243
319
  before do
244
320
  create_post pinned: true
@@ -248,29 +324,141 @@ describe 'OrderQuery' do
248
324
  Post.delete_all
249
325
  end
250
326
  it 'ORDER BY is collapsed' do
251
- expect(Post.seek([:pinned, [true, false]]).scope.to_sql).to include('ORDER BY "posts"."pinned" DESC')
327
+ expect(Post.seek([:pinned, [true, false]]).scope.to_sql).to(
328
+ match(/ORDER BY .posts.\..pinned. DESC/)
329
+ )
252
330
  end
253
331
  it 'enum asc' do
254
- expect(Post.seek([:pinned, [false, true], :asc]).scope.pluck(:pinned)).to eq([true, false])
255
- expect(Post.seek([:pinned, [true, false], :asc]).scope.pluck(:pinned)).to eq([false, true])
332
+ expect(
333
+ Post.seek([:pinned, [false, true], :asc]).scope.pluck(:pinned)
334
+ ).to eq([true, false])
335
+ expect(
336
+ Post.seek([:pinned, [true, false], :asc]).scope.pluck(:pinned)
337
+ ).to eq([false, true])
256
338
  end
257
339
  it 'enum desc' do
258
- expect(Post.seek([:pinned, [false, true], :desc]).scope.pluck(:pinned)).to eq([false, true])
259
- expect(Post.seek([:pinned, [true, false], :desc]).scope.pluck(:pinned)).to eq([true, false])
340
+ expect(
341
+ Post.seek([:pinned, [false, true], :desc]).scope.pluck(:pinned)
342
+ ).to eq([false, true])
343
+ expect(
344
+ Post.seek([:pinned, [true, false], :desc]).scope.pluck(:pinned)
345
+ ).to eq([true, false])
260
346
  end
261
347
  end
262
348
 
263
- xcontext 'nil in enum' do
349
+ context 'nil in boolean enum' do
350
+ display = ->(post) { "##{post.id}-#{post.pinned || 'NULL'}" }
264
351
  states = [nil, false, true]
265
- let!(:posts) { states.map { |state| create_post(pinned: state) } }
266
- states.permutation do |p|
267
- # There is no cross-DB SQL that can be generated to position nil results
268
- # http://use-the-index-luke.com/sql/sorting-grouping/order-by-asc-desc-nulls-last
269
- next unless p.first.nil? || p.last.nil?
270
- # Positioning NULLs first or last can be achieved, but remains on the ToDo / contributions welcome list
271
- it "nil in enum works for #{p}" do
272
- expect(Post.seek([:pinned, p]).scope.all.map(&:pinned)).to eq(p)
273
- expect(Post.seek([:pinned, p, :asc]).scope.all.map(&:pinned)).to eq(p.reverse)
352
+ let!(:posts) do
353
+ states.flat_map do |state|
354
+ [create_post(pinned: state), create_post(pinned: state)]
355
+ end
356
+ end
357
+ states.permutation do |perm|
358
+ it "works for #{perm} (desc)" do
359
+ expect_order(
360
+ Post.seek([:pinned, perm]),
361
+ posts.sort_by { |x| [perm.index(x.pinned), x.id] },
362
+ &display
363
+ )
364
+ end
365
+ it "works for #{perm} (asc)" do
366
+ expect_order(
367
+ Post.seek([:pinned, perm, :asc]),
368
+ posts.sort_by { |x| [-perm.index(x.pinned), x.id] },
369
+ &display
370
+ )
371
+ end
372
+ end
373
+ end
374
+
375
+ context 'nil published_at' do
376
+ display = ->(post) { post.title }
377
+
378
+ let! :null_1 do
379
+ Post.create!(title: 'null_1', published_at: nil).reload
380
+ end
381
+ let! :null_2 do
382
+ Post.create!(title: 'null_2', published_at: nil).reload
383
+ end
384
+ let! :older do
385
+ Post.create!(title: 'older', published_at: Time.now + 1.hour)
386
+ end
387
+ let! :newer do
388
+ Post.create!(title: 'newer', published_at: Time.now - 1.hour)
389
+ end
390
+
391
+ it 'orders nulls first (desc)' do
392
+ space = Post.seek([:published_at, :desc, nulls: :first])
393
+ expect_order space, [null_1, null_2, older, newer], &display
394
+ end
395
+
396
+ it 'orders nulls first (asc)' do
397
+ space = Post.seek([:published_at, :asc, nulls: :first])
398
+ expect_order space, [null_1, null_2, newer, older], &display
399
+ end
400
+
401
+ it 'orders nulls last (desc)' do
402
+ space = Post.seek([:published_at, :desc, nulls: :last])
403
+ expect_order space, [older, newer, null_1, null_2], &display
404
+ end
405
+
406
+ it 'orders nulls last (asc)' do
407
+ space = Post.seek([:published_at, :asc, nulls: :last])
408
+ expect_order space, [newer, older, null_1, null_2], &display
409
+ end
410
+ end
411
+
412
+ context 'after/before no strict' do
413
+ context 'by middle attribute in search order' do
414
+ let! :base do
415
+ Post.create! pinned: true, published_at: Time.now
416
+ end
417
+ let! :older do
418
+ Post.create! pinned: true, published_at: Time.now + 1.hour
419
+ end
420
+ let! :newer do
421
+ Post.create! pinned: true, published_at: Time.now - 1.hour
422
+ end
423
+
424
+ it 'includes first element' do
425
+ point = Post.order_list_at(base)
426
+
427
+ expect(point.after.count).to eq 1
428
+ expect(point.after.to_a).to eq [newer]
429
+
430
+ expect(point.after(false).count).to eq 2
431
+ expect(point.after(false).to_a).to eq [base, newer]
432
+ expect(point.before(false).to_a).to eq [base, older]
433
+ end
434
+ end
435
+
436
+ context 'by last attribute in search order' do
437
+ let!(:base) do
438
+ Post.create! pinned: true,
439
+ published_at: Time.new(2016, 5, 1, 5, 4, 3),
440
+ id: 6
441
+ end
442
+ let!(:previous) do
443
+ Post.create! pinned: true,
444
+ published_at: Time.new(2016, 5, 1, 5, 4, 3),
445
+ id: 4
446
+ end
447
+ let!(:next_one) do
448
+ Post.create! pinned: true,
449
+ published_at: Time.new(2016, 5, 1, 5, 4, 3),
450
+ id: 9
451
+ end
452
+
453
+ it 'includes first element' do
454
+ point = Post.order_list_at(base)
455
+
456
+ expect(point.after.count).to eq 1
457
+ expect(point.after.to_a).to eq [previous]
458
+
459
+ expect(point.after(false).count).to eq 2
460
+ expect(point.after(false).to_a).to eq [base, previous]
461
+ expect(point.before(false).to_a).to eq [base, next_one]
274
462
  end
275
463
  end
276
464
  end
@@ -282,10 +470,12 @@ describe 'OrderQuery' do
282
470
  ActiveRecord::Schema.define do
283
471
  self.verbose = false
284
472
  create_table :posts do |t|
473
+ t.string :title
285
474
  t.boolean :pinned
286
475
  t.datetime :published_at
287
476
  end
288
477
  end
478
+ Post.reset_column_information
289
479
  end
290
480
  after :all do
291
481
  ActiveRecord::Migration.drop_table :posts
@@ -298,7 +488,8 @@ describe 'OrderQuery' do
298
488
  context 'wrap top-level OR on' do
299
489
  wrap_top_level_or true
300
490
  it 'wraps top-level OR' do
301
- after_scope = User.create!(updated_at: Date.parse('2014-09-06')).seek([[:updated_at, :desc], [:id, :desc]]).after
491
+ after_scope = User.create!(updated_at: Date.parse('2014-09-06'))
492
+ .seek([%i[updated_at desc], %i[id desc]]).after
302
493
  expect(after_scope.to_sql).to include('<=')
303
494
  end
304
495
  end
@@ -306,7 +497,8 @@ describe 'OrderQuery' do
306
497
  context 'wrap top-level OR off' do
307
498
  wrap_top_level_or false
308
499
  it 'does not wrap top-level OR' do
309
- after_scope = User.create!(updated_at: Date.parse('2014-09-06')).seek([[:updated_at, :desc], [:id, :desc]]).after
500
+ after_scope = User.create!(updated_at: Date.parse('2014-09-06'))
501
+ .seek([%i[updated_at desc], %i[id desc]]).after
310
502
  expect(after_scope.to_sql).to_not include('<=')
311
503
  end
312
504
  end
@@ -322,6 +514,7 @@ describe 'OrderQuery' do
322
514
  t.datetime :updated_at, null: false
323
515
  end
324
516
  end
517
+ User.reset_column_information
325
518
  end
326
519
 
327
520
  after :all do