acts_as_list 0.7.4 → 1.1.0
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 +5 -13
- data/.github/FUNDING.yml +3 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +123 -0
- data/.gitignore +1 -0
- data/.travis.yml +50 -12
- data/Appraisals +39 -6
- data/CHANGELOG.md +565 -148
- data/Gemfile +19 -14
- data/README.md +206 -19
- data/Rakefile +4 -4
- data/acts_as_list.gemspec +16 -11
- data/gemfiles/rails_4_2.gemfile +18 -9
- data/gemfiles/rails_5_0.gemfile +31 -0
- data/gemfiles/rails_5_1.gemfile +31 -0
- data/gemfiles/rails_5_2.gemfile +31 -0
- data/gemfiles/rails_6_0.gemfile +31 -0
- data/gemfiles/rails_6_1.gemfile +31 -0
- data/gemfiles/rails_7_0.gemfile +31 -0
- data/init.rb +2 -0
- data/lib/acts_as_list/active_record/acts/active_record.rb +5 -0
- data/lib/acts_as_list/active_record/acts/add_new_at_method_definer.rb +11 -0
- data/lib/acts_as_list/active_record/acts/aux_method_definer.rb +11 -0
- data/lib/acts_as_list/active_record/acts/callback_definer.rb +19 -0
- data/lib/acts_as_list/active_record/acts/list.rb +299 -306
- data/lib/acts_as_list/active_record/acts/no_update.rb +125 -0
- data/lib/acts_as_list/active_record/acts/position_column_method_definer.rb +101 -0
- data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +77 -0
- data/lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb +28 -0
- data/lib/acts_as_list/active_record/acts/top_of_list_method_definer.rb +15 -0
- data/lib/acts_as_list/version.rb +3 -1
- data/lib/acts_as_list.rb +11 -14
- data/test/database.yml +18 -0
- data/test/helper.rb +50 -2
- data/test/shared.rb +3 -0
- data/test/shared_array_scope_list.rb +21 -4
- data/test/shared_list.rb +86 -12
- data/test/shared_list_sub.rb +63 -2
- data/test/shared_no_addition.rb +50 -2
- data/test/shared_quoting.rb +23 -0
- data/test/shared_top_addition.rb +36 -13
- data/test/shared_zero_based.rb +13 -0
- data/test/test_default_scope_with_select.rb +33 -0
- data/test/test_joined_list.rb +61 -0
- data/test/test_list.rb +601 -84
- data/test/test_no_update_for_extra_classes.rb +131 -0
- data/test/test_no_update_for_scope_destruction.rb +69 -0
- data/test/test_no_update_for_subclasses.rb +56 -0
- data/test/test_scope_with_user_defined_foreign_key.rb +42 -0
- metadata +56 -22
- data/gemfiles/rails_3_2.gemfile +0 -24
- data/gemfiles/rails_4_1.gemfile +0 -24
data/test/test_list.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# NOTE: following now done in helper.rb (better Readability)
|
2
4
|
require 'helper'
|
3
5
|
|
4
|
-
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
5
|
-
ActiveRecord::Schema.verbose = false
|
6
|
-
|
7
6
|
def setup_db(position_options = {})
|
7
|
+
$default_position = position_options[:default]
|
8
|
+
|
9
|
+
# sqlite cannot drop/rename/alter columns and add constraints after table creation
|
10
|
+
sqlite = ENV.fetch("DB", "sqlite") == "sqlite"
|
11
|
+
|
8
12
|
# AR caches columns options like defaults etc. Clear them!
|
9
13
|
ActiveRecord::Base.connection.create_table :mixins do |t|
|
10
|
-
t.column :pos, :integer, position_options
|
14
|
+
t.column :pos, :integer, **position_options unless position_options[:positive] && sqlite
|
11
15
|
t.column :active, :boolean, default: true
|
12
16
|
t.column :parent_id, :integer
|
13
17
|
t.column :parent_type, :string
|
@@ -16,11 +20,37 @@ def setup_db(position_options = {})
|
|
16
20
|
t.column :state, :integer
|
17
21
|
end
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
if position_options[:unique] && !(sqlite && position_options[:positive])
|
24
|
+
ActiveRecord::Base.connection.add_index :mixins, :pos, unique: true
|
25
|
+
end
|
26
|
+
|
27
|
+
if position_options[:positive]
|
28
|
+
if sqlite
|
29
|
+
# SQLite cannot add constraint after table creation, also cannot add unique inside ADD COLUMN
|
30
|
+
ActiveRecord::Base.connection.execute('ALTER TABLE mixins ADD COLUMN pos integer8 NOT NULL CHECK (pos > 0) DEFAULT 1')
|
31
|
+
ActiveRecord::Base.connection.execute('CREATE UNIQUE INDEX index_mixins_on_pos ON mixins(pos)')
|
32
|
+
else
|
33
|
+
ActiveRecord::Base.connection.execute('ALTER TABLE mixins ADD CONSTRAINT pos_check CHECK (pos > 0)')
|
34
|
+
end
|
35
|
+
end
|
22
36
|
|
23
|
-
|
37
|
+
# This table is used to test table names and column names quoting
|
38
|
+
ActiveRecord::Base.connection.create_table 'table-name' do |t|
|
39
|
+
t.column :order, :integer
|
40
|
+
end
|
41
|
+
|
42
|
+
# This table is used to test table names with different primary_key columns
|
43
|
+
ActiveRecord::Base.connection.create_table 'altid-table', primary_key: 'altid' do |t|
|
44
|
+
t.column :pos, :integer
|
45
|
+
t.column :created_at, :datetime
|
46
|
+
t.column :updated_at, :datetime
|
47
|
+
end
|
48
|
+
|
49
|
+
ActiveRecord::Base.connection.add_index 'altid-table', :pos, unique: true
|
50
|
+
|
51
|
+
mixins = [ Mixin, ListMixin, ListMixinSub1, ListMixinSub2, ListWithStringScopeMixin,
|
52
|
+
ArrayScopeListMixin, ZeroBasedMixin, DefaultScopedMixin, EnumArrayScopeListMixin,
|
53
|
+
DefaultScopedWhereMixin, TopAdditionMixin, NoAdditionMixin, QuotedList, TouchDisabledMixin ]
|
24
54
|
|
25
55
|
ActiveRecord::Base.connection.schema_cache.clear!
|
26
56
|
mixins.each do |klass|
|
@@ -32,21 +62,6 @@ def setup_db_with_default
|
|
32
62
|
setup_db default: 0
|
33
63
|
end
|
34
64
|
|
35
|
-
# Returns true if ActiveRecord is rails3,4 version
|
36
|
-
def rails_3
|
37
|
-
defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 3
|
38
|
-
end
|
39
|
-
|
40
|
-
def rails_4
|
41
|
-
defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 4
|
42
|
-
end
|
43
|
-
|
44
|
-
def teardown_db
|
45
|
-
ActiveRecord::Base.connection.tables.each do |table|
|
46
|
-
ActiveRecord::Base.connection.drop_table(table)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
65
|
class Mixin < ActiveRecord::Base
|
51
66
|
self.table_name = 'mixins'
|
52
67
|
end
|
@@ -55,15 +70,19 @@ class ListMixin < Mixin
|
|
55
70
|
acts_as_list column: "pos", scope: :parent
|
56
71
|
end
|
57
72
|
|
73
|
+
class TouchDisabledMixin < Mixin
|
74
|
+
acts_as_list column: "pos", touch_on_update: false
|
75
|
+
end
|
76
|
+
|
58
77
|
class ListMixinSub1 < ListMixin
|
59
78
|
end
|
60
79
|
|
61
80
|
class ListMixinSub2 < ListMixin
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
81
|
+
validates :pos, presence: true
|
82
|
+
end
|
83
|
+
|
84
|
+
class ListMixinError < ListMixin
|
85
|
+
validates :state, presence: true
|
67
86
|
end
|
68
87
|
|
69
88
|
class ListWithStringScopeMixin < Mixin
|
@@ -74,13 +93,15 @@ class ArrayScopeListMixin < Mixin
|
|
74
93
|
acts_as_list column: "pos", scope: [:parent_id, :parent_type]
|
75
94
|
end
|
76
95
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
enum state: STATE_VALUES
|
96
|
+
class ArrayScopeListWithHashMixin < Mixin
|
97
|
+
acts_as_list column: "pos", scope: [:parent_id, state: nil]
|
98
|
+
end
|
81
99
|
|
82
|
-
|
83
|
-
|
100
|
+
class EnumArrayScopeListMixin < Mixin
|
101
|
+
STATE_VALUES = %w(active archived)
|
102
|
+
enum state: STATE_VALUES
|
103
|
+
|
104
|
+
acts_as_list column: "pos", scope: [:parent_id, :state]
|
84
105
|
end
|
85
106
|
|
86
107
|
class ZeroBasedMixin < Mixin
|
@@ -97,10 +118,29 @@ class DefaultScopedWhereMixin < Mixin
|
|
97
118
|
default_scope { order('pos ASC').where(active: true) }
|
98
119
|
|
99
120
|
def self.for_active_false_tests
|
100
|
-
|
121
|
+
unscope(:where).where(active: false)
|
101
122
|
end
|
102
123
|
end
|
103
124
|
|
125
|
+
class SequentialUpdatesDefault < Mixin
|
126
|
+
acts_as_list column: "pos"
|
127
|
+
end
|
128
|
+
|
129
|
+
class SequentialUpdatesAltId < ActiveRecord::Base
|
130
|
+
self.table_name = "altid-table"
|
131
|
+
self.primary_key = "altid"
|
132
|
+
|
133
|
+
acts_as_list column: "pos"
|
134
|
+
end
|
135
|
+
|
136
|
+
class SequentialUpdatesAltIdTouchDisabled < SequentialUpdatesAltId
|
137
|
+
acts_as_list column: "pos", touch_on_update: false
|
138
|
+
end
|
139
|
+
|
140
|
+
class SequentialUpdatesFalseMixin < Mixin
|
141
|
+
acts_as_list column: "pos", sequential_updates: false
|
142
|
+
end
|
143
|
+
|
104
144
|
class TopAdditionMixin < Mixin
|
105
145
|
acts_as_list column: "pos", add_new_at: :top, scope: :parent_id
|
106
146
|
end
|
@@ -112,7 +152,7 @@ end
|
|
112
152
|
##
|
113
153
|
# The way we track changes to
|
114
154
|
# scope and position can get tripped up
|
115
|
-
# by someone using
|
155
|
+
# by someone using update within
|
116
156
|
# a callback because it causes multiple passes
|
117
157
|
# through the callback chain
|
118
158
|
module CallbackMixin
|
@@ -127,7 +167,7 @@ module CallbackMixin
|
|
127
167
|
# doesn't matter what column changes, just
|
128
168
|
# need to change something
|
129
169
|
|
130
|
-
self.
|
170
|
+
self.update active: !self.active
|
131
171
|
end
|
132
172
|
end
|
133
173
|
end
|
@@ -149,6 +189,11 @@ end
|
|
149
189
|
class TheBaseSubclass < TheBaseClass
|
150
190
|
end
|
151
191
|
|
192
|
+
class QuotedList < ActiveRecord::Base
|
193
|
+
self.table_name = 'table-name'
|
194
|
+
acts_as_list column: :order
|
195
|
+
end
|
196
|
+
|
152
197
|
class ActsAsListTestCase < Minitest::Test
|
153
198
|
# No default test required as this class is abstract.
|
154
199
|
# Need for test/unit.
|
@@ -184,6 +229,37 @@ class ListTest < ActsAsListTestCase
|
|
184
229
|
setup_db
|
185
230
|
super
|
186
231
|
end
|
232
|
+
|
233
|
+
def test_insert_race_condition
|
234
|
+
# the bigger n is the more likely we will have a race condition
|
235
|
+
n = 1000
|
236
|
+
(1..n).each do |counter|
|
237
|
+
node = ListMixin.new parent_id: 1
|
238
|
+
node.pos = counter
|
239
|
+
node.save!
|
240
|
+
end
|
241
|
+
|
242
|
+
wait_for_it = true
|
243
|
+
threads = []
|
244
|
+
4.times do |i|
|
245
|
+
threads << Thread.new do
|
246
|
+
true while wait_for_it
|
247
|
+
ActiveRecord::Base.connection_pool.with_connection do |c|
|
248
|
+
n.times do
|
249
|
+
begin
|
250
|
+
ListMixin.where(parent_id: 1).order('pos').last.insert_at(1)
|
251
|
+
rescue Exception
|
252
|
+
# ignore SQLite3::SQLException due to table locking
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
wait_for_it = false
|
259
|
+
threads.each(&:join)
|
260
|
+
|
261
|
+
assert_equal((1..n).to_a, ListMixin.where(parent_id: 1).order('pos').map(&:pos))
|
262
|
+
end
|
187
263
|
end
|
188
264
|
|
189
265
|
class ListWithCallbackTest < ActsAsListTestCase
|
@@ -243,6 +319,15 @@ class ArrayScopeListTestWithDefault < ActsAsListTestCase
|
|
243
319
|
end
|
244
320
|
end
|
245
321
|
|
322
|
+
class QuotingTestList < ActsAsListTestCase
|
323
|
+
include Shared::Quoting
|
324
|
+
|
325
|
+
def setup
|
326
|
+
setup_db_with_default
|
327
|
+
super
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
246
331
|
class DefaultScopedTest < ActsAsListTestCase
|
247
332
|
def setup
|
248
333
|
setup_db
|
@@ -260,6 +345,11 @@ class DefaultScopedTest < ActsAsListTestCase
|
|
260
345
|
assert !new.first?
|
261
346
|
assert new.last?
|
262
347
|
|
348
|
+
new = DefaultScopedMixin.acts_as_list_no_update { DefaultScopedMixin.create }
|
349
|
+
assert_equal_or_nil $default_position, new.pos
|
350
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
351
|
+
assert !new.last?
|
352
|
+
|
263
353
|
new = DefaultScopedMixin.create
|
264
354
|
assert_equal 7, new.pos
|
265
355
|
assert !new.first?
|
@@ -295,6 +385,9 @@ class DefaultScopedTest < ActsAsListTestCase
|
|
295
385
|
new = DefaultScopedMixin.create
|
296
386
|
assert_equal 6, new.pos
|
297
387
|
|
388
|
+
new_noup = DefaultScopedMixin.acts_as_list_no_update { DefaultScopedMixin.create }
|
389
|
+
assert_equal_or_nil $default_position, new_noup.pos
|
390
|
+
|
298
391
|
new = DefaultScopedMixin.create
|
299
392
|
assert_equal 7, new.pos
|
300
393
|
|
@@ -321,6 +414,15 @@ class DefaultScopedTest < ActsAsListTestCase
|
|
321
414
|
|
322
415
|
new4.reload
|
323
416
|
assert_equal 4, new4.pos
|
417
|
+
|
418
|
+
new_noup.reload
|
419
|
+
assert_equal_or_nil $default_position, new_noup.pos
|
420
|
+
end
|
421
|
+
|
422
|
+
def test_find_or_create_doesnt_raise_deprecation_warning
|
423
|
+
assert_no_deprecation_warning_raised_by('ActiveRecord deprecation warning raised when using `find_or_create_by` when we didn\'t expect it') do
|
424
|
+
DefaultScopedMixin.find_or_create_by(pos: 5)
|
425
|
+
end
|
324
426
|
end
|
325
427
|
|
326
428
|
def test_update_position
|
@@ -353,6 +455,11 @@ class DefaultScopedWhereTest < ActsAsListTestCase
|
|
353
455
|
assert !new.first?
|
354
456
|
assert new.last?
|
355
457
|
|
458
|
+
new = DefaultScopedWhereMixin.acts_as_list_no_update { DefaultScopedWhereMixin.create }
|
459
|
+
assert_equal_or_nil $default_position, new.pos
|
460
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
461
|
+
assert !new.last?
|
462
|
+
|
356
463
|
new = DefaultScopedWhereMixin.create
|
357
464
|
assert_equal 7, new.pos
|
358
465
|
assert !new.first?
|
@@ -391,6 +498,9 @@ class DefaultScopedWhereTest < ActsAsListTestCase
|
|
391
498
|
new = DefaultScopedWhereMixin.create
|
392
499
|
assert_equal 7, new.pos
|
393
500
|
|
501
|
+
new_noup = DefaultScopedWhereMixin.acts_as_list_no_update { DefaultScopedWhereMixin.create }
|
502
|
+
assert_equal_or_nil $default_position, new_noup.pos
|
503
|
+
|
394
504
|
new4 = DefaultScopedWhereMixin.create
|
395
505
|
assert_equal 8, new4.pos
|
396
506
|
|
@@ -414,6 +524,15 @@ class DefaultScopedWhereTest < ActsAsListTestCase
|
|
414
524
|
|
415
525
|
new4.reload
|
416
526
|
assert_equal 4, new4.pos
|
527
|
+
|
528
|
+
new_noup.reload
|
529
|
+
assert_equal_or_nil $default_position, new_noup.pos
|
530
|
+
end
|
531
|
+
|
532
|
+
def test_find_or_create_doesnt_raise_deprecation_warning
|
533
|
+
assert_no_deprecation_warning_raised_by('ActiveRecord deprecation warning raised when using `find_or_create_by` when we didn\'t expect it') do
|
534
|
+
DefaultScopedWhereMixin.find_or_create_by(pos: 5)
|
535
|
+
end
|
417
536
|
end
|
418
537
|
|
419
538
|
def test_update_position
|
@@ -468,10 +587,56 @@ class MultiDestroyTest < ActsAsListTestCase
|
|
468
587
|
new3 = DefaultScopedMixin.create
|
469
588
|
assert_equal 3, new3.pos
|
470
589
|
|
590
|
+
new4 = DefaultScopedMixin.create
|
591
|
+
assert_equal 4, new4.pos
|
592
|
+
|
471
593
|
new1.destroy
|
472
594
|
new2.destroy
|
473
595
|
new3.reload
|
596
|
+
new4.reload
|
474
597
|
assert_equal 1, new3.pos
|
598
|
+
assert_equal 2, new4.pos
|
599
|
+
|
600
|
+
DefaultScopedMixin.acts_as_list_no_update { new3.destroy }
|
601
|
+
|
602
|
+
new4.reload
|
603
|
+
assert_equal 2, new4.pos
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
class MultiUpdateTest < ActsAsListTestCase
|
608
|
+
|
609
|
+
def setup
|
610
|
+
setup_db
|
611
|
+
end
|
612
|
+
|
613
|
+
def test_multiple_updates_within_transaction
|
614
|
+
@page = ListMixin.create! id: 100, parent_id: nil, pos: 1
|
615
|
+
@row = ListMixin.create! parent_id: @page.id, pos: 1
|
616
|
+
@column1 = ListMixin.create! parent_id: @row.id, pos: 1
|
617
|
+
@column2 = ListMixin.create! parent_id: @row.id, pos: 2
|
618
|
+
@rich_text1 = ListMixin.create! parent_id: @column1.id, pos: 1
|
619
|
+
@rich_text2 = ListMixin.create! parent_id: @column2.id, pos: 1
|
620
|
+
|
621
|
+
ActiveRecord::Base.transaction do
|
622
|
+
@rich_text1.update!(parent_id: @column2.id, pos: 1)
|
623
|
+
|
624
|
+
assert_equal [@rich_text1.id, @rich_text2.id], ListMixin.where(parent_id: @column2.id).order('pos').map(&:id)
|
625
|
+
assert_equal [1, 2], ListMixin.where(parent_id: @column2.id).order('pos').map(&:pos)
|
626
|
+
|
627
|
+
@column1.destroy!
|
628
|
+
assert_equal [@column2.id], ListMixin.where(parent_id: @row.id).order('pos').map(&:id)
|
629
|
+
assert_equal [1], ListMixin.where(parent_id: @row.id).order('pos').map(&:pos)
|
630
|
+
|
631
|
+
@rich_text1.update!(parent_id: @page.id, pos: 1)
|
632
|
+
@rich_text2.update!(parent_id: @page.id, pos: 2)
|
633
|
+
@row.destroy!
|
634
|
+
@column2.destroy!
|
635
|
+
end
|
636
|
+
|
637
|
+
assert_equal(1, @page.reload.pos)
|
638
|
+
assert_equal [@rich_text1.id, @rich_text2.id], ListMixin.where(parent_id: @page.id).order('pos').map(&:id)
|
639
|
+
assert_equal [1, 2], ListMixin.where(parent_id: @page.id).order('pos').map(&:pos)
|
475
640
|
end
|
476
641
|
end
|
477
642
|
|
@@ -512,38 +677,48 @@ class MultipleListsTest < ActsAsListTestCase
|
|
512
677
|
end
|
513
678
|
|
514
679
|
def test_check_scope_order
|
515
|
-
assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).order(
|
516
|
-
assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order(
|
517
|
-
ListMixin.find(4).
|
518
|
-
assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order(
|
519
|
-
assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order(
|
680
|
+
assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).order('pos').map(&:id)
|
681
|
+
assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order('pos').map(&:id)
|
682
|
+
ListMixin.find(4).update :parent_id => 2, :pos => 2
|
683
|
+
assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order('pos').map(&:id)
|
684
|
+
assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order('pos').map(&:id)
|
520
685
|
end
|
521
686
|
|
522
687
|
def test_check_scope_position
|
523
688
|
assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).map(&:pos)
|
524
689
|
assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 2).map(&:pos)
|
525
|
-
ListMixin.find(4).
|
526
|
-
assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order(
|
527
|
-
assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order(
|
690
|
+
ListMixin.find(4).update :parent_id => 2, :pos => 2
|
691
|
+
assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order('pos').map(&:pos)
|
692
|
+
assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order('pos').map(&:pos)
|
528
693
|
end
|
529
|
-
end
|
530
694
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
setup_db
|
535
|
-
EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['active']
|
536
|
-
EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['archived']
|
537
|
-
EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["active"]
|
538
|
-
EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["archived"]
|
695
|
+
def test_find_or_create_doesnt_raise_deprecation_warning
|
696
|
+
assert_no_deprecation_warning_raised_by('ActiveRecord deprecation warning raised when using `find_or_create_by` when we didn\'t expect it') do
|
697
|
+
ListMixin.where(:parent_id => 1).find_or_create_by(pos: 5)
|
539
698
|
end
|
699
|
+
end
|
700
|
+
end
|
540
701
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
702
|
+
class EnumArrayScopeListMixinTest < ActsAsListTestCase
|
703
|
+
def setup
|
704
|
+
setup_db
|
705
|
+
EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['active']
|
706
|
+
EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['archived']
|
707
|
+
EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["active"]
|
708
|
+
EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["archived"]
|
709
|
+
end
|
710
|
+
|
711
|
+
def test_positions
|
712
|
+
assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 1, :state => EnumArrayScopeListMixin.states['active']).map(&:pos)
|
713
|
+
assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 1, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos)
|
714
|
+
assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active']).map(&:pos)
|
715
|
+
assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos)
|
716
|
+
end
|
717
|
+
|
718
|
+
def test_update_state
|
719
|
+
active_item = EnumArrayScopeListMixin.find_by(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active'])
|
720
|
+
active_item.update(state: EnumArrayScopeListMixin.states['archived'])
|
721
|
+
assert_equal [1, 2], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos).sort
|
547
722
|
end
|
548
723
|
end
|
549
724
|
|
@@ -556,50 +731,392 @@ class MultipleListsArrayScopeTest < ActsAsListTestCase
|
|
556
731
|
end
|
557
732
|
|
558
733
|
def test_order_after_all_scope_properties_are_changed
|
559
|
-
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(
|
560
|
-
assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(
|
561
|
-
ArrayScopeListMixin.find(2).
|
562
|
-
assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(
|
563
|
-
assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order(
|
734
|
+
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
|
735
|
+
assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order('pos').map(&:id)
|
736
|
+
ArrayScopeListMixin.find(2).update :parent_id => 2, :pos => 2,:parent_type => 'something'
|
737
|
+
assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
|
738
|
+
assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order('pos').map(&:id)
|
564
739
|
end
|
565
740
|
|
566
741
|
def test_position_after_all_scope_properties_are_changed
|
567
742
|
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
|
568
743
|
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').map(&:pos)
|
569
|
-
ArrayScopeListMixin.find(4).
|
570
|
-
assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(
|
571
|
-
assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(
|
744
|
+
ArrayScopeListMixin.find(4).update :parent_id => 2, :pos => 2, :parent_type => 'something'
|
745
|
+
assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
|
746
|
+
assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order('pos').map(&:pos)
|
572
747
|
end
|
573
748
|
|
574
749
|
def test_order_after_one_scope_property_is_changed
|
575
|
-
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(
|
576
|
-
assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(
|
577
|
-
ArrayScopeListMixin.find(2).
|
578
|
-
assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(
|
579
|
-
assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order(
|
750
|
+
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
|
751
|
+
assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order('pos').map(&:id)
|
752
|
+
ArrayScopeListMixin.find(2).update :parent_id => 3, :pos => 2
|
753
|
+
assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
|
754
|
+
assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order('pos').map(&:id)
|
580
755
|
end
|
581
756
|
|
582
757
|
def test_position_after_one_scope_property_is_changed
|
583
758
|
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
|
584
759
|
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').map(&:pos)
|
585
|
-
ArrayScopeListMixin.find(4).
|
586
|
-
assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(
|
587
|
-
assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(
|
760
|
+
ArrayScopeListMixin.find(4).update :parent_id => 3, :pos => 2
|
761
|
+
assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
|
762
|
+
assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order('pos').map(&:pos)
|
588
763
|
end
|
589
764
|
|
590
765
|
def test_order_after_moving_to_empty_list
|
591
|
-
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(
|
592
|
-
assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(
|
593
|
-
ArrayScopeListMixin.find(2).
|
594
|
-
assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(
|
595
|
-
assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order(
|
766
|
+
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
|
767
|
+
assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order('pos').map(&:id)
|
768
|
+
ArrayScopeListMixin.find(2).update :parent_id => 4, :pos => 1
|
769
|
+
assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
|
770
|
+
assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order('pos').map(&:id)
|
596
771
|
end
|
597
772
|
|
598
773
|
def test_position_after_moving_to_empty_list
|
599
774
|
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
|
600
775
|
assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').map(&:pos)
|
601
|
-
ArrayScopeListMixin.find(2).
|
602
|
-
assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(
|
603
|
-
assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(
|
776
|
+
ArrayScopeListMixin.find(2).update :parent_id => 4, :pos => 1
|
777
|
+
assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
|
778
|
+
assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order('pos').map(&:pos)
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
class ArrayScopeListWithHashTest
|
783
|
+
def setup
|
784
|
+
setup_db
|
785
|
+
@obj1 = ArrayScopeListWithHashMixin.create! :pos => counter, :parent_id => 1, :state => nil
|
786
|
+
@obj2 = ArrayScopeListWithHashMixin.create! :pos => counter, :parent_id => 1, :state => 'anything'
|
787
|
+
end
|
788
|
+
|
789
|
+
def test_scope_condition_correct
|
790
|
+
[@obj1, @obj2].each do |obj|
|
791
|
+
assert_equal({ :parent_id => 1, :state => nil }, obj.scope_condition)
|
792
|
+
end
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
require 'timecop'
|
797
|
+
|
798
|
+
class TouchTest < ActsAsListTestCase
|
799
|
+
def setup
|
800
|
+
setup_db
|
801
|
+
Timecop.freeze(yesterday) do
|
802
|
+
4.times { ListMixin.create! }
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
def now
|
807
|
+
@now ||= Time.current.change(usec: 0)
|
808
|
+
end
|
809
|
+
|
810
|
+
def yesterday
|
811
|
+
@yesterday ||= 1.day.ago
|
812
|
+
end
|
813
|
+
|
814
|
+
def updated_ats
|
815
|
+
ListMixin.order(:id).pluck(:updated_at)
|
816
|
+
end
|
817
|
+
|
818
|
+
def test_moving_item_lower_touches_self_and_lower_item
|
819
|
+
Timecop.freeze(now) do
|
820
|
+
ListMixin.first.move_lower
|
821
|
+
updated_ats[0..1].each do |updated_at|
|
822
|
+
assert_equal updated_at.to_i, now.to_i
|
823
|
+
end
|
824
|
+
updated_ats[2..3].each do |updated_at|
|
825
|
+
assert_equal updated_at.to_i, yesterday.to_i
|
826
|
+
end
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
def test_moving_item_higher_touches_self_and_higher_item
|
831
|
+
Timecop.freeze(now) do
|
832
|
+
ListMixin.all.second.move_higher
|
833
|
+
updated_ats[0..1].each do |updated_at|
|
834
|
+
assert_equal updated_at.to_i, now.to_i
|
835
|
+
end
|
836
|
+
updated_ats[2..3].each do |updated_at|
|
837
|
+
assert_equal updated_at.to_i, yesterday.to_i
|
838
|
+
end
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
def test_moving_item_to_bottom_touches_all_other_items
|
843
|
+
Timecop.freeze(now) do
|
844
|
+
ListMixin.first.move_to_bottom
|
845
|
+
updated_ats.each do |updated_at|
|
846
|
+
assert_equal updated_at.to_i, now.to_i
|
847
|
+
end
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
def test_moving_item_to_top_touches_all_other_items
|
852
|
+
Timecop.freeze(now) do
|
853
|
+
ListMixin.last.move_to_top
|
854
|
+
updated_ats.each do |updated_at|
|
855
|
+
assert_equal updated_at.to_i, now.to_i
|
856
|
+
end
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
def test_removing_item_touches_all_lower_items
|
861
|
+
Timecop.freeze(now) do
|
862
|
+
ListMixin.all.third.remove_from_list
|
863
|
+
updated_ats[0..1].each do |updated_at|
|
864
|
+
assert_equal updated_at.to_i, yesterday.to_i
|
865
|
+
end
|
866
|
+
updated_ats[2..2].each do |updated_at|
|
867
|
+
assert_equal updated_at.to_i, now.to_i
|
868
|
+
end
|
869
|
+
end
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
class TouchDisabledTest < ActsAsListTestCase
|
874
|
+
def setup
|
875
|
+
setup_db
|
876
|
+
Timecop.freeze(yesterday) do
|
877
|
+
4.times { TouchDisabledMixin.create! }
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
def now
|
882
|
+
@now ||= Time.current.change(usec: 0)
|
883
|
+
end
|
884
|
+
|
885
|
+
def yesterday
|
886
|
+
@yesterday ||= 1.day.ago
|
887
|
+
end
|
888
|
+
|
889
|
+
def updated_ats
|
890
|
+
TouchDisabledMixin.order(:id).pluck(:updated_at)
|
891
|
+
end
|
892
|
+
|
893
|
+
def positions
|
894
|
+
ListMixin.order(:id).pluck(:pos)
|
895
|
+
end
|
896
|
+
|
897
|
+
def test_deleting_item_does_not_touch_higher_items
|
898
|
+
Timecop.freeze(now) do
|
899
|
+
TouchDisabledMixin.first.destroy
|
900
|
+
updated_ats.each do |updated_at|
|
901
|
+
assert_equal updated_at.to_i, yesterday.to_i
|
902
|
+
end
|
903
|
+
assert_equal positions, [1, 2, 3]
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
class ActsAsListTopTest < ActsAsListTestCase
|
909
|
+
def setup
|
910
|
+
setup_db
|
911
|
+
end
|
912
|
+
|
913
|
+
def test_acts_as_list_top
|
914
|
+
assert_equal 1, TheBaseSubclass.new.acts_as_list_top
|
915
|
+
assert_equal 0, ZeroBasedMixin.new.acts_as_list_top
|
916
|
+
end
|
917
|
+
|
918
|
+
def test_class_acts_as_list_top
|
919
|
+
assert_equal 1, TheBaseSubclass.acts_as_list_top
|
920
|
+
assert_equal 0, ZeroBasedMixin.acts_as_list_top
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
class NilPositionTest < ActsAsListTestCase
|
925
|
+
def setup
|
926
|
+
setup_db
|
927
|
+
end
|
928
|
+
|
929
|
+
def test_nil_position_ordering
|
930
|
+
new1 = DefaultScopedMixin.create pos: nil
|
931
|
+
new2 = DefaultScopedMixin.create pos: nil
|
932
|
+
new3 = DefaultScopedMixin.create pos: nil
|
933
|
+
DefaultScopedMixin.update_all(pos: nil)
|
934
|
+
|
935
|
+
assert_equal [nil, nil, nil], DefaultScopedMixin.all.map(&:pos)
|
936
|
+
|
937
|
+
new1.reload.pos = 1
|
938
|
+
new1.save
|
939
|
+
|
940
|
+
new3.reload.pos = 1
|
941
|
+
new3.save
|
942
|
+
|
943
|
+
assert_equal [1, 2], DefaultScopedMixin.where("pos IS NOT NULL").map(&:pos)
|
944
|
+
assert_equal [3, 1], DefaultScopedMixin.where("pos IS NOT NULL").map(&:id)
|
945
|
+
assert_nil new2.reload.pos
|
946
|
+
|
947
|
+
new2.reload.pos = 1
|
948
|
+
new2.save
|
949
|
+
|
950
|
+
assert_equal [1, 2, 3], DefaultScopedMixin.all.map(&:pos)
|
951
|
+
assert_equal [2, 3, 1], DefaultScopedMixin.all.map(&:id)
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
class SequentialUpdatesOptionDefaultTest < ActsAsListTestCase
|
956
|
+
def setup
|
957
|
+
setup_db
|
958
|
+
end
|
959
|
+
|
960
|
+
def test_sequential_updates_default_to_false_without_unique_index
|
961
|
+
assert_equal false, SequentialUpdatesDefault.new.send(:sequential_updates?)
|
962
|
+
end
|
963
|
+
end
|
964
|
+
|
965
|
+
class SequentialUpdatesMixinNotNullUniquePositiveConstraintsTest < ActsAsListTestCase
|
966
|
+
def setup
|
967
|
+
setup_db null: false, unique: true, positive: true
|
968
|
+
(1..4).each { |counter| SequentialUpdatesDefault.create!({pos: counter}) }
|
969
|
+
end
|
970
|
+
|
971
|
+
def test_sequential_updates_default_to_true_with_unique_index
|
972
|
+
assert_equal true, SequentialUpdatesDefault.new.send(:sequential_updates?)
|
973
|
+
end
|
974
|
+
|
975
|
+
def test_sequential_updates_option_override_with_false
|
976
|
+
assert_equal false, SequentialUpdatesFalseMixin.new.send(:sequential_updates?)
|
977
|
+
end
|
978
|
+
|
979
|
+
def test_insert_at
|
980
|
+
new = SequentialUpdatesDefault.create
|
981
|
+
assert_equal 5, new.pos
|
982
|
+
|
983
|
+
new.insert_at(1)
|
984
|
+
assert_equal 1, new.pos
|
985
|
+
|
986
|
+
new.insert_at(5)
|
987
|
+
assert_equal 5, new.pos
|
988
|
+
|
989
|
+
new.insert_at(3)
|
990
|
+
assert_equal 3, new.pos
|
991
|
+
end
|
992
|
+
|
993
|
+
def test_move_to_bottom
|
994
|
+
item = SequentialUpdatesDefault.order(:pos).first
|
995
|
+
item.move_to_bottom
|
996
|
+
assert_equal 4, item.pos
|
997
|
+
end
|
998
|
+
|
999
|
+
def test_move_to_top
|
1000
|
+
new_item = SequentialUpdatesDefault.create!
|
1001
|
+
assert_equal 5, new_item.pos
|
1002
|
+
|
1003
|
+
new_item.move_to_top
|
1004
|
+
assert_equal 1, new_item.pos
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def test_destroy
|
1008
|
+
new_item = SequentialUpdatesDefault.create
|
1009
|
+
assert_equal 5, new_item.pos
|
1010
|
+
|
1011
|
+
new_item.insert_at(2)
|
1012
|
+
assert_equal 2, new_item.pos
|
1013
|
+
|
1014
|
+
new_item.destroy
|
1015
|
+
assert_equal [1,2,3,4], SequentialUpdatesDefault.all.map(&:pos).sort
|
1016
|
+
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def test_exception_on_wrong_position
|
1020
|
+
new_item = SequentialUpdatesDefault.create
|
1021
|
+
|
1022
|
+
assert_raises ArgumentError do
|
1023
|
+
new_item.insert_at(0)
|
1024
|
+
end
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
|
1028
|
+
class SequentialUpdatesMixinNotNullUniquePositiveConstraintsTest < ActsAsListTestCase
|
1029
|
+
def setup
|
1030
|
+
setup_db null: false, unique: true, positive: true
|
1031
|
+
(1..4).each { |counter| SequentialUpdatesAltId.create!({pos: counter}) }
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def test_sequential_updates_default_to_true_with_unique_index
|
1035
|
+
assert_equal true, SequentialUpdatesAltId.new.send(:sequential_updates?)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def test_insert_at
|
1039
|
+
new = SequentialUpdatesAltId.create
|
1040
|
+
assert_equal 5, new.pos
|
1041
|
+
|
1042
|
+
new.insert_at(1)
|
1043
|
+
assert_equal 1, new.pos
|
1044
|
+
|
1045
|
+
new.insert_at(5)
|
1046
|
+
assert_equal 5, new.pos
|
1047
|
+
|
1048
|
+
new.insert_at(3)
|
1049
|
+
assert_equal 3, new.pos
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
def test_create_at_top
|
1053
|
+
new = SequentialUpdatesAltId.create!(pos: 1)
|
1054
|
+
assert_equal 1, new.pos
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def test_move_to_bottom
|
1058
|
+
item = SequentialUpdatesAltId.order(:pos).first
|
1059
|
+
item.move_to_bottom
|
1060
|
+
assert_equal 4, item.pos
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
def test_move_to_top
|
1064
|
+
new_item = SequentialUpdatesAltId.create!
|
1065
|
+
assert_equal 5, new_item.pos
|
1066
|
+
|
1067
|
+
new_item.move_to_top
|
1068
|
+
assert_equal 1, new_item.pos
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
def test_destroy
|
1072
|
+
new_item = SequentialUpdatesAltId.create
|
1073
|
+
assert_equal 5, new_item.pos
|
1074
|
+
|
1075
|
+
new_item.insert_at(2)
|
1076
|
+
assert_equal 2, new_item.pos
|
1077
|
+
|
1078
|
+
new_item.destroy
|
1079
|
+
assert_equal [1,2,3,4], SequentialUpdatesAltId.all.map(&:pos).sort
|
1080
|
+
|
1081
|
+
end
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
class SequentialUpdatesAltIdTouchDisabledTest < ActsAsListTestCase
|
1085
|
+
def setup
|
1086
|
+
setup_db
|
1087
|
+
Timecop.freeze(yesterday) do
|
1088
|
+
4.times { SequentialUpdatesAltIdTouchDisabled.create! }
|
1089
|
+
end
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
def now
|
1093
|
+
@now ||= Time.current.change(usec: 0)
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def yesterday
|
1097
|
+
@yesterday ||= 1.day.ago
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
def updated_ats
|
1101
|
+
SequentialUpdatesAltIdTouchDisabled.order(:altid).pluck(:updated_at)
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
def positions
|
1105
|
+
SequentialUpdatesAltIdTouchDisabled.order(:altid).pluck(:pos)
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
def test_sequential_updates_default_to_true_with_unique_index
|
1109
|
+
assert_equal true, SequentialUpdatesAltIdTouchDisabled.new.send(:sequential_updates?)
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
def test_deleting_item_does_not_touch_higher_items
|
1113
|
+
Timecop.freeze(now) do
|
1114
|
+
SequentialUpdatesAltIdTouchDisabled.first.destroy
|
1115
|
+
updated_ats.each do |updated_at|
|
1116
|
+
assert_equal updated_at.to_i, yesterday.to_i
|
1117
|
+
end
|
1118
|
+
assert_equal positions, [1, 2, 3]
|
1119
|
+
end
|
1120
|
+
end
|
604
1121
|
end
|
605
1122
|
end
|