acts_as_list 1.0.4 → 1.2.4

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -4
  3. data/README.md +17 -9
  4. data/Rakefile +0 -8
  5. data/lib/acts_as_list/active_record/acts/callback_definer.rb +2 -4
  6. data/lib/acts_as_list/active_record/acts/list.rb +33 -34
  7. data/lib/acts_as_list/active_record/acts/position_column_method_definer.rb +15 -6
  8. data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +1 -2
  9. data/lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb +15 -15
  10. data/lib/acts_as_list/active_record/acts/with_connection.rb +25 -0
  11. data/lib/acts_as_list/version.rb +1 -1
  12. metadata +102 -61
  13. data/.github/FUNDING.yml +0 -3
  14. data/.gitignore +0 -12
  15. data/.travis.yml +0 -55
  16. data/Appraisals +0 -40
  17. data/Gemfile +0 -28
  18. data/acts_as_list.gemspec +0 -34
  19. data/gemfiles/rails_4_2.gemfile +0 -32
  20. data/gemfiles/rails_5_0.gemfile +0 -31
  21. data/gemfiles/rails_5_1.gemfile +0 -31
  22. data/gemfiles/rails_5_2.gemfile +0 -31
  23. data/gemfiles/rails_6_0.gemfile +0 -31
  24. data/gemfiles/rails_6_1.gemfile +0 -31
  25. data/test/database.yml +0 -16
  26. data/test/helper.rb +0 -69
  27. data/test/shared.rb +0 -12
  28. data/test/shared_array_scope_list.rb +0 -177
  29. data/test/shared_list.rb +0 -324
  30. data/test/shared_list_sub.rb +0 -188
  31. data/test/shared_no_addition.rb +0 -38
  32. data/test/shared_quoting.rb +0 -23
  33. data/test/shared_top_addition.rb +0 -110
  34. data/test/shared_zero_based.rb +0 -104
  35. data/test/test_default_scope_with_select.rb +0 -33
  36. data/test/test_joined_list.rb +0 -61
  37. data/test/test_list.rb +0 -1086
  38. data/test/test_no_update_for_extra_classes.rb +0 -131
  39. data/test/test_no_update_for_scope_destruction.rb +0 -69
  40. data/test/test_no_update_for_subclasses.rb +0 -56
  41. data/test/test_scope_with_user_defined_foreign_key.rb +0 -42
data/test/test_list.rb DELETED
@@ -1,1086 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # NOTE: following now done in helper.rb (better Readability)
4
- require 'helper'
5
-
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
-
12
- # AR caches columns options like defaults etc. Clear them!
13
- ActiveRecord::Base.connection.create_table :mixins do |t|
14
- t.column :pos, :integer, **position_options unless position_options[:positive] && sqlite
15
- t.column :active, :boolean, default: true
16
- t.column :parent_id, :integer
17
- t.column :parent_type, :string
18
- t.column :created_at, :datetime
19
- t.column :updated_at, :datetime
20
- t.column :state, :integer
21
- end
22
-
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
36
-
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 ]
54
-
55
- ActiveRecord::Base.connection.schema_cache.clear!
56
- mixins.each do |klass|
57
- klass.reset_column_information
58
- end
59
- end
60
-
61
- def setup_db_with_default
62
- setup_db default: 0
63
- end
64
-
65
- class Mixin < ActiveRecord::Base
66
- self.table_name = 'mixins'
67
- end
68
-
69
- class ListMixin < Mixin
70
- acts_as_list column: "pos", scope: :parent
71
- end
72
-
73
- class TouchDisabledMixin < Mixin
74
- acts_as_list column: "pos", touch_on_update: false
75
- end
76
-
77
- class ListMixinSub1 < ListMixin
78
- end
79
-
80
- class ListMixinSub2 < ListMixin
81
- validates :pos, presence: true
82
- end
83
-
84
- class ListMixinError < ListMixin
85
- validates :state, presence: true
86
- end
87
-
88
- class ListWithStringScopeMixin < Mixin
89
- acts_as_list column: "pos", scope: 'parent_id = #{parent_id}'
90
- end
91
-
92
- class ArrayScopeListMixin < Mixin
93
- acts_as_list column: "pos", scope: [:parent_id, :parent_type]
94
- end
95
-
96
- class ArrayScopeListWithHashMixin < Mixin
97
- acts_as_list column: "pos", scope: [:parent_id, state: nil]
98
- end
99
-
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]
105
- end
106
-
107
- class ZeroBasedMixin < Mixin
108
- acts_as_list column: "pos", top_of_list: 0, scope: [:parent_id]
109
- end
110
-
111
- class DefaultScopedMixin < Mixin
112
- acts_as_list column: "pos"
113
- default_scope { order('pos ASC') }
114
- end
115
-
116
- class DefaultScopedWhereMixin < Mixin
117
- acts_as_list column: "pos"
118
- default_scope { order('pos ASC').where(active: true) }
119
-
120
- def self.for_active_false_tests
121
- unscope(:where).where(active: false)
122
- end
123
- end
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
-
144
- class TopAdditionMixin < Mixin
145
- acts_as_list column: "pos", add_new_at: :top, scope: :parent_id
146
- end
147
-
148
- class NoAdditionMixin < Mixin
149
- acts_as_list column: "pos", add_new_at: nil, scope: :parent_id
150
- end
151
-
152
- ##
153
- # The way we track changes to
154
- # scope and position can get tripped up
155
- # by someone using update within
156
- # a callback because it causes multiple passes
157
- # through the callback chain
158
- module CallbackMixin
159
-
160
- def self.included(base)
161
- base.send :include, InstanceMethods
162
- base.after_create :change_field
163
- end
164
-
165
- module InstanceMethods
166
- def change_field
167
- # doesn't matter what column changes, just
168
- # need to change something
169
-
170
- self.update active: !self.active
171
- end
172
- end
173
- end
174
-
175
- class TheAbstractClass < ActiveRecord::Base
176
- self.abstract_class = true
177
- self.table_name = 'mixins'
178
- end
179
-
180
- class TheAbstractSubclass < TheAbstractClass
181
- acts_as_list column: "pos", scope: :parent
182
- end
183
-
184
- class TheBaseClass < ActiveRecord::Base
185
- self.table_name = 'mixins'
186
- acts_as_list column: "pos", scope: :parent
187
- end
188
-
189
- class TheBaseSubclass < TheBaseClass
190
- end
191
-
192
- class QuotedList < ActiveRecord::Base
193
- self.table_name = 'table-name'
194
- acts_as_list column: :order
195
- end
196
-
197
- class ActsAsListTestCase < Minitest::Test
198
- # No default test required as this class is abstract.
199
- # Need for test/unit.
200
- undef_method :default_test if method_defined?(:default_test)
201
-
202
- def teardown
203
- teardown_db
204
- end
205
- end
206
-
207
- class ZeroBasedTest < ActsAsListTestCase
208
- include Shared::ZeroBased
209
-
210
- def setup
211
- setup_db
212
- super
213
- end
214
- end
215
-
216
- class ZeroBasedTestWithDefault < ActsAsListTestCase
217
- include Shared::ZeroBased
218
-
219
- def setup
220
- setup_db_with_default
221
- super
222
- end
223
- end
224
-
225
- class ListTest < ActsAsListTestCase
226
- include Shared::List
227
-
228
- def setup
229
- setup_db
230
- super
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
263
- end
264
-
265
- class ListWithCallbackTest < ActsAsListTestCase
266
-
267
- include Shared::List
268
-
269
- def setup
270
- ListMixin.send(:include, CallbackMixin)
271
- setup_db
272
- super
273
- end
274
-
275
- end
276
-
277
- class ListTestWithDefault < ActsAsListTestCase
278
- include Shared::List
279
-
280
- def setup
281
- setup_db_with_default
282
- super
283
- end
284
- end
285
-
286
- class ListSubTest < ActsAsListTestCase
287
- include Shared::ListSub
288
-
289
- def setup
290
- setup_db
291
- super
292
- end
293
- end
294
-
295
- class ListSubTestWithDefault < ActsAsListTestCase
296
- include Shared::ListSub
297
-
298
- def setup
299
- setup_db_with_default
300
- super
301
- end
302
- end
303
-
304
- class ArrayScopeListTest < ActsAsListTestCase
305
- include Shared::ArrayScopeList
306
-
307
- def setup
308
- setup_db
309
- super
310
- end
311
- end
312
-
313
- class ArrayScopeListTestWithDefault < ActsAsListTestCase
314
- include Shared::ArrayScopeList
315
-
316
- def setup
317
- setup_db_with_default
318
- super
319
- end
320
- end
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
-
331
- class DefaultScopedTest < ActsAsListTestCase
332
- def setup
333
- setup_db
334
- (1..4).each { |counter| DefaultScopedMixin.create!({pos: counter}) }
335
- end
336
-
337
- def test_insert
338
- new = DefaultScopedMixin.create
339
- assert_equal 5, new.pos
340
- assert !new.first?
341
- assert new.last?
342
-
343
- new = DefaultScopedMixin.create
344
- assert_equal 6, new.pos
345
- assert !new.first?
346
- assert new.last?
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
-
353
- new = DefaultScopedMixin.create
354
- assert_equal 7, new.pos
355
- assert !new.first?
356
- assert new.last?
357
- end
358
-
359
- def test_reordering
360
- assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id)
361
-
362
- DefaultScopedMixin.where(id: 2).first.move_lower
363
- assert_equal [1, 3, 2, 4], DefaultScopedMixin.all.map(&:id)
364
-
365
- DefaultScopedMixin.where(id: 2).first.move_higher
366
- assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id)
367
-
368
- DefaultScopedMixin.where(id: 1).first.move_to_bottom
369
- assert_equal [2, 3, 4, 1], DefaultScopedMixin.all.map(&:id)
370
-
371
- DefaultScopedMixin.where(id: 1).first.move_to_top
372
- assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id)
373
-
374
- DefaultScopedMixin.where(id: 2).first.move_to_bottom
375
- assert_equal [1, 3, 4, 2], DefaultScopedMixin.all.map(&:id)
376
-
377
- DefaultScopedMixin.where(id: 4).first.move_to_top
378
- assert_equal [4, 1, 3, 2], DefaultScopedMixin.all.map(&:id)
379
- end
380
-
381
- def test_insert_at
382
- new = DefaultScopedMixin.create
383
- assert_equal 5, new.pos
384
-
385
- new = DefaultScopedMixin.create
386
- assert_equal 6, new.pos
387
-
388
- new_noup = DefaultScopedMixin.acts_as_list_no_update { DefaultScopedMixin.create }
389
- assert_equal_or_nil $default_position, new_noup.pos
390
-
391
- new = DefaultScopedMixin.create
392
- assert_equal 7, new.pos
393
-
394
- new4 = DefaultScopedMixin.create
395
- assert_equal 8, new4.pos
396
-
397
- new4.insert_at(2)
398
- assert_equal 2, new4.pos
399
-
400
- new.reload
401
- assert_equal 8, new.pos
402
-
403
- new.insert_at(2)
404
- assert_equal 2, new.pos
405
-
406
- new4.reload
407
- assert_equal 3, new4.pos
408
-
409
- new5 = DefaultScopedMixin.create
410
- assert_equal 9, new5.pos
411
-
412
- new5.insert_at(1)
413
- assert_equal 1, new5.pos
414
-
415
- new4.reload
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
426
- end
427
-
428
- def test_update_position
429
- assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id)
430
- DefaultScopedMixin.where(id: 2).first.set_list_position(4)
431
- assert_equal [1, 3, 4, 2], DefaultScopedMixin.all.map(&:id)
432
- DefaultScopedMixin.where(id: 2).first.set_list_position(2)
433
- assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id)
434
- DefaultScopedMixin.where(id: 1).first.set_list_position(4)
435
- assert_equal [2, 3, 4, 1], DefaultScopedMixin.all.map(&:id)
436
- DefaultScopedMixin.where(id: 1).first.set_list_position(1)
437
- assert_equal [1, 2, 3, 4], DefaultScopedMixin.all.map(&:id)
438
- end
439
- end
440
-
441
- class DefaultScopedWhereTest < ActsAsListTestCase
442
- def setup
443
- setup_db
444
- (1..4).each { |counter| DefaultScopedWhereMixin.create! pos: counter, active: false }
445
- end
446
-
447
- def test_insert
448
- new = DefaultScopedWhereMixin.create
449
- assert_equal 5, new.pos
450
- assert !new.first?
451
- assert new.last?
452
-
453
- new = DefaultScopedWhereMixin.create
454
- assert_equal 6, new.pos
455
- assert !new.first?
456
- assert new.last?
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
-
463
- new = DefaultScopedWhereMixin.create
464
- assert_equal 7, new.pos
465
- assert !new.first?
466
- assert new.last?
467
- end
468
-
469
- def test_reordering
470
- assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
471
-
472
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.move_lower
473
- assert_equal [1, 3, 2, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
474
-
475
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.move_higher
476
- assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
477
-
478
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.move_to_bottom
479
- assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
480
-
481
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.move_to_top
482
- assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
483
-
484
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.move_to_bottom
485
- assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
486
-
487
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 4).first.move_to_top
488
- assert_equal [4, 1, 3, 2], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
489
- end
490
-
491
- def test_insert_at
492
- new = DefaultScopedWhereMixin.create
493
- assert_equal 5, new.pos
494
-
495
- new = DefaultScopedWhereMixin.create
496
- assert_equal 6, new.pos
497
-
498
- new = DefaultScopedWhereMixin.create
499
- assert_equal 7, new.pos
500
-
501
- new_noup = DefaultScopedWhereMixin.acts_as_list_no_update { DefaultScopedWhereMixin.create }
502
- assert_equal_or_nil $default_position, new_noup.pos
503
-
504
- new4 = DefaultScopedWhereMixin.create
505
- assert_equal 8, new4.pos
506
-
507
- new4.insert_at(2)
508
- assert_equal 2, new4.pos
509
-
510
- new.reload
511
- assert_equal 8, new.pos
512
-
513
- new.insert_at(2)
514
- assert_equal 2, new.pos
515
-
516
- new4.reload
517
- assert_equal 3, new4.pos
518
-
519
- new5 = DefaultScopedWhereMixin.create
520
- assert_equal 9, new5.pos
521
-
522
- new5.insert_at(1)
523
- assert_equal 1, new5.pos
524
-
525
- new4.reload
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
536
- end
537
-
538
- def test_update_position
539
- assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
540
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.set_list_position(4)
541
- assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
542
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 2).first.set_list_position(2)
543
- assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
544
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.set_list_position(4)
545
- assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
546
- DefaultScopedWhereMixin.for_active_false_tests.where(id: 1).first.set_list_position(1)
547
- assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.for_active_false_tests.map(&:id)
548
- end
549
-
550
- end
551
-
552
- class MultiDestroyTest < ActsAsListTestCase
553
-
554
- def setup
555
- setup_db
556
- end
557
-
558
- # example:
559
- #
560
- # class TodoList < ActiveRecord::Base
561
- # has_many :todo_items, order: "position"
562
- # accepts_nested_attributes_for :todo_items, allow_destroy: true
563
- # end
564
- #
565
- # class TodoItem < ActiveRecord::Base
566
- # belongs_to :todo_list
567
- # acts_as_list scope: :todo_list
568
- # end
569
- #
570
- # Assume that there are three items.
571
- # The user mark two items as deleted, click save button, form will be post:
572
- #
573
- # todo_list.todo_items_attributes = [
574
- # {id: 1, _destroy: true},
575
- # {id: 2, _destroy: true}
576
- # ]
577
- #
578
- # Save toto_list, the position of item #3 should eql 1.
579
- #
580
- def test_destroy
581
- new1 = DefaultScopedMixin.create
582
- assert_equal 1, new1.pos
583
-
584
- new2 = DefaultScopedMixin.create
585
- assert_equal 2, new2.pos
586
-
587
- new3 = DefaultScopedMixin.create
588
- assert_equal 3, new3.pos
589
-
590
- new4 = DefaultScopedMixin.create
591
- assert_equal 4, new4.pos
592
-
593
- new1.destroy
594
- new2.destroy
595
- new3.reload
596
- new4.reload
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 TopAdditionMixin < Mixin
608
-
609
- class TopAdditionTest < ActsAsListTestCase
610
- include Shared::TopAddition
611
-
612
- def setup
613
- setup_db
614
- super
615
- end
616
- end
617
-
618
- class TopAdditionTestWithDefault < ActsAsListTestCase
619
- include Shared::TopAddition
620
-
621
- def setup
622
- setup_db_with_default
623
- super
624
- end
625
- end
626
-
627
- class NoAdditionTest < ActsAsListTestCase
628
- include Shared::NoAddition
629
-
630
- def setup
631
- setup_db
632
- super
633
- end
634
- end
635
-
636
- class MultipleListsTest < ActsAsListTestCase
637
- def setup
638
- setup_db
639
- (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 1}
640
- (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 2}
641
- end
642
-
643
- def test_check_scope_order
644
- assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).order('pos').map(&:id)
645
- assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order('pos').map(&:id)
646
- ListMixin.find(4).update :parent_id => 2, :pos => 2
647
- assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order('pos').map(&:id)
648
- assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order('pos').map(&:id)
649
- end
650
-
651
- def test_check_scope_position
652
- assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).map(&:pos)
653
- assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 2).map(&:pos)
654
- ListMixin.find(4).update :parent_id => 2, :pos => 2
655
- assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order('pos').map(&:pos)
656
- assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order('pos').map(&:pos)
657
- end
658
-
659
- def test_find_or_create_doesnt_raise_deprecation_warning
660
- assert_no_deprecation_warning_raised_by('ActiveRecord deprecation warning raised when using `find_or_create_by` when we didn\'t expect it') do
661
- ListMixin.where(:parent_id => 1).find_or_create_by(pos: 5)
662
- end
663
- end
664
- end
665
-
666
- class EnumArrayScopeListMixinTest < ActsAsListTestCase
667
- def setup
668
- setup_db
669
- EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['active']
670
- EnumArrayScopeListMixin.create! :parent_id => 1, :state => EnumArrayScopeListMixin.states['archived']
671
- EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["active"]
672
- EnumArrayScopeListMixin.create! :parent_id => 2, :state => EnumArrayScopeListMixin.states["archived"]
673
- end
674
-
675
- def test_positions
676
- assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 1, :state => EnumArrayScopeListMixin.states['active']).map(&:pos)
677
- assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 1, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos)
678
- assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active']).map(&:pos)
679
- assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos)
680
- end
681
-
682
- def test_update_state
683
- active_item = EnumArrayScopeListMixin.find_by(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active'])
684
- active_item.update(state: EnumArrayScopeListMixin.states['archived'])
685
- assert_equal [1, 2], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos).sort
686
- end
687
- end
688
-
689
- class MultipleListsArrayScopeTest < ActsAsListTestCase
690
- def setup
691
- setup_db
692
- (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter,:parent_id => 1, :parent_type => 'anything'}
693
- (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter,:parent_id => 2, :parent_type => 'something'}
694
- (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter,:parent_id => 3, :parent_type => 'anything'}
695
- end
696
-
697
- def test_order_after_all_scope_properties_are_changed
698
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
699
- assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order('pos').map(&:id)
700
- ArrayScopeListMixin.find(2).update :parent_id => 2, :pos => 2,:parent_type => 'something'
701
- assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
702
- assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order('pos').map(&:id)
703
- end
704
-
705
- def test_position_after_all_scope_properties_are_changed
706
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
707
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').map(&:pos)
708
- ArrayScopeListMixin.find(4).update :parent_id => 2, :pos => 2, :parent_type => 'something'
709
- assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
710
- assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order('pos').map(&:pos)
711
- end
712
-
713
- def test_order_after_one_scope_property_is_changed
714
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
715
- assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order('pos').map(&:id)
716
- ArrayScopeListMixin.find(2).update :parent_id => 3, :pos => 2
717
- assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
718
- assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order('pos').map(&:id)
719
- end
720
-
721
- def test_position_after_one_scope_property_is_changed
722
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
723
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').map(&:pos)
724
- ArrayScopeListMixin.find(4).update :parent_id => 3, :pos => 2
725
- assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
726
- assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order('pos').map(&:pos)
727
- end
728
-
729
- def test_order_after_moving_to_empty_list
730
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
731
- assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order('pos').map(&:id)
732
- ArrayScopeListMixin.find(2).update :parent_id => 4, :pos => 1
733
- assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
734
- assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order('pos').map(&:id)
735
- end
736
-
737
- def test_position_after_moving_to_empty_list
738
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
739
- assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').map(&:pos)
740
- ArrayScopeListMixin.find(2).update :parent_id => 4, :pos => 1
741
- assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
742
- assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order('pos').map(&:pos)
743
- end
744
- end
745
-
746
- class ArrayScopeListWithHashTest
747
- def setup
748
- setup_db
749
- @obj1 = ArrayScopeListWithHashMixin.create! :pos => counter, :parent_id => 1, :state => nil
750
- @obj2 = ArrayScopeListWithHashMixin.create! :pos => counter, :parent_id => 1, :state => 'anything'
751
- end
752
-
753
- def test_scope_condition_correct
754
- [@obj1, @obj2].each do |obj|
755
- assert_equal({ :parent_id => 1, :state => nil }, obj.scope_condition)
756
- end
757
- end
758
- end
759
-
760
- require 'timecop'
761
-
762
- class TouchTest < ActsAsListTestCase
763
- def setup
764
- setup_db
765
- Timecop.freeze(yesterday) do
766
- 4.times { ListMixin.create! }
767
- end
768
- end
769
-
770
- def now
771
- @now ||= Time.current.change(usec: 0)
772
- end
773
-
774
- def yesterday
775
- @yesterday ||= 1.day.ago
776
- end
777
-
778
- def updated_ats
779
- ListMixin.order(:id).pluck(:updated_at)
780
- end
781
-
782
- def test_moving_item_lower_touches_self_and_lower_item
783
- Timecop.freeze(now) do
784
- ListMixin.first.move_lower
785
- updated_ats[0..1].each do |updated_at|
786
- assert_equal updated_at.to_i, now.to_i
787
- end
788
- updated_ats[2..3].each do |updated_at|
789
- assert_equal updated_at.to_i, yesterday.to_i
790
- end
791
- end
792
- end
793
-
794
- def test_moving_item_higher_touches_self_and_higher_item
795
- Timecop.freeze(now) do
796
- ListMixin.all.second.move_higher
797
- updated_ats[0..1].each do |updated_at|
798
- assert_equal updated_at.to_i, now.to_i
799
- end
800
- updated_ats[2..3].each do |updated_at|
801
- assert_equal updated_at.to_i, yesterday.to_i
802
- end
803
- end
804
- end
805
-
806
- def test_moving_item_to_bottom_touches_all_other_items
807
- Timecop.freeze(now) do
808
- ListMixin.first.move_to_bottom
809
- updated_ats.each do |updated_at|
810
- assert_equal updated_at.to_i, now.to_i
811
- end
812
- end
813
- end
814
-
815
- def test_moving_item_to_top_touches_all_other_items
816
- Timecop.freeze(now) do
817
- ListMixin.last.move_to_top
818
- updated_ats.each do |updated_at|
819
- assert_equal updated_at.to_i, now.to_i
820
- end
821
- end
822
- end
823
-
824
- def test_removing_item_touches_all_lower_items
825
- Timecop.freeze(now) do
826
- ListMixin.all.third.remove_from_list
827
- updated_ats[0..1].each do |updated_at|
828
- assert_equal updated_at.to_i, yesterday.to_i
829
- end
830
- updated_ats[2..2].each do |updated_at|
831
- assert_equal updated_at.to_i, now.to_i
832
- end
833
- end
834
- end
835
- end
836
-
837
- class TouchDisabledTest < ActsAsListTestCase
838
- def setup
839
- setup_db
840
- Timecop.freeze(yesterday) do
841
- 4.times { TouchDisabledMixin.create! }
842
- end
843
- end
844
-
845
- def now
846
- @now ||= Time.current.change(usec: 0)
847
- end
848
-
849
- def yesterday
850
- @yesterday ||= 1.day.ago
851
- end
852
-
853
- def updated_ats
854
- TouchDisabledMixin.order(:id).pluck(:updated_at)
855
- end
856
-
857
- def positions
858
- ListMixin.order(:id).pluck(:pos)
859
- end
860
-
861
- def test_deleting_item_does_not_touch_higher_items
862
- Timecop.freeze(now) do
863
- TouchDisabledMixin.first.destroy
864
- updated_ats.each do |updated_at|
865
- assert_equal updated_at.to_i, yesterday.to_i
866
- end
867
- assert_equal positions, [1, 2, 3]
868
- end
869
- end
870
- end
871
-
872
- class ActsAsListTopTest < ActsAsListTestCase
873
- def setup
874
- setup_db
875
- end
876
-
877
- def test_acts_as_list_top
878
- assert_equal 1, TheBaseSubclass.new.acts_as_list_top
879
- assert_equal 0, ZeroBasedMixin.new.acts_as_list_top
880
- end
881
-
882
- def test_class_acts_as_list_top
883
- assert_equal 1, TheBaseSubclass.acts_as_list_top
884
- assert_equal 0, ZeroBasedMixin.acts_as_list_top
885
- end
886
- end
887
-
888
- class NilPositionTest < ActsAsListTestCase
889
- def setup
890
- setup_db
891
- end
892
-
893
- def test_nil_position_ordering
894
- new1 = DefaultScopedMixin.create pos: nil
895
- new2 = DefaultScopedMixin.create pos: nil
896
- new3 = DefaultScopedMixin.create pos: nil
897
- DefaultScopedMixin.update_all(pos: nil)
898
-
899
- assert_equal [nil, nil, nil], DefaultScopedMixin.all.map(&:pos)
900
-
901
- new1.reload.pos = 1
902
- new1.save
903
-
904
- new3.reload.pos = 1
905
- new3.save
906
-
907
- assert_equal [1, 2], DefaultScopedMixin.where("pos IS NOT NULL").map(&:pos)
908
- assert_equal [3, 1], DefaultScopedMixin.where("pos IS NOT NULL").map(&:id)
909
- assert_nil new2.reload.pos
910
-
911
- new2.reload.pos = 1
912
- new2.save
913
-
914
- assert_equal [1, 2, 3], DefaultScopedMixin.all.map(&:pos)
915
- assert_equal [2, 3, 1], DefaultScopedMixin.all.map(&:id)
916
- end
917
- end
918
-
919
- class SequentialUpdatesOptionDefaultTest < ActsAsListTestCase
920
- def setup
921
- setup_db
922
- end
923
-
924
- def test_sequential_updates_default_to_false_without_unique_index
925
- assert_equal false, SequentialUpdatesDefault.new.send(:sequential_updates?)
926
- end
927
- end
928
-
929
- class SequentialUpdatesMixinNotNullUniquePositiveConstraintsTest < ActsAsListTestCase
930
- def setup
931
- setup_db null: false, unique: true, positive: true
932
- (1..4).each { |counter| SequentialUpdatesDefault.create!({pos: counter}) }
933
- end
934
-
935
- def test_sequential_updates_default_to_true_with_unique_index
936
- assert_equal true, SequentialUpdatesDefault.new.send(:sequential_updates?)
937
- end
938
-
939
- def test_sequential_updates_option_override_with_false
940
- assert_equal false, SequentialUpdatesFalseMixin.new.send(:sequential_updates?)
941
- end
942
-
943
- def test_insert_at
944
- new = SequentialUpdatesDefault.create
945
- assert_equal 5, new.pos
946
-
947
- new.insert_at(1)
948
- assert_equal 1, new.pos
949
-
950
- new.insert_at(5)
951
- assert_equal 5, new.pos
952
-
953
- new.insert_at(3)
954
- assert_equal 3, new.pos
955
- end
956
-
957
- def test_move_to_bottom
958
- item = SequentialUpdatesDefault.order(:pos).first
959
- item.move_to_bottom
960
- assert_equal 4, item.pos
961
- end
962
-
963
- def test_move_to_top
964
- new_item = SequentialUpdatesDefault.create!
965
- assert_equal 5, new_item.pos
966
-
967
- new_item.move_to_top
968
- assert_equal 1, new_item.pos
969
- end
970
-
971
- def test_destroy
972
- new_item = SequentialUpdatesDefault.create
973
- assert_equal 5, new_item.pos
974
-
975
- new_item.insert_at(2)
976
- assert_equal 2, new_item.pos
977
-
978
- new_item.destroy
979
- assert_equal [1,2,3,4], SequentialUpdatesDefault.all.map(&:pos).sort
980
-
981
- end
982
-
983
- def test_exception_on_wrong_position
984
- new_item = SequentialUpdatesDefault.create
985
-
986
- assert_raises ArgumentError do
987
- new_item.insert_at(0)
988
- end
989
- end
990
-
991
-
992
- class SequentialUpdatesMixinNotNullUniquePositiveConstraintsTest < ActsAsListTestCase
993
- def setup
994
- setup_db null: false, unique: true, positive: true
995
- (1..4).each { |counter| SequentialUpdatesAltId.create!({pos: counter}) }
996
- end
997
-
998
- def test_sequential_updates_default_to_true_with_unique_index
999
- assert_equal true, SequentialUpdatesAltId.new.send(:sequential_updates?)
1000
- end
1001
-
1002
- def test_insert_at
1003
- new = SequentialUpdatesAltId.create
1004
- assert_equal 5, new.pos
1005
-
1006
- new.insert_at(1)
1007
- assert_equal 1, new.pos
1008
-
1009
- new.insert_at(5)
1010
- assert_equal 5, new.pos
1011
-
1012
- new.insert_at(3)
1013
- assert_equal 3, new.pos
1014
- end
1015
-
1016
- def test_create_at_top
1017
- new = SequentialUpdatesAltId.create!(pos: 1)
1018
- assert_equal 1, new.pos
1019
- end
1020
-
1021
- def test_move_to_bottom
1022
- item = SequentialUpdatesAltId.order(:pos).first
1023
- item.move_to_bottom
1024
- assert_equal 4, item.pos
1025
- end
1026
-
1027
- def test_move_to_top
1028
- new_item = SequentialUpdatesAltId.create!
1029
- assert_equal 5, new_item.pos
1030
-
1031
- new_item.move_to_top
1032
- assert_equal 1, new_item.pos
1033
- end
1034
-
1035
- def test_destroy
1036
- new_item = SequentialUpdatesAltId.create
1037
- assert_equal 5, new_item.pos
1038
-
1039
- new_item.insert_at(2)
1040
- assert_equal 2, new_item.pos
1041
-
1042
- new_item.destroy
1043
- assert_equal [1,2,3,4], SequentialUpdatesAltId.all.map(&:pos).sort
1044
-
1045
- end
1046
- end
1047
-
1048
- class SequentialUpdatesAltIdTouchDisabledTest < ActsAsListTestCase
1049
- def setup
1050
- setup_db
1051
- Timecop.freeze(yesterday) do
1052
- 4.times { SequentialUpdatesAltIdTouchDisabled.create! }
1053
- end
1054
- end
1055
-
1056
- def now
1057
- @now ||= Time.current.change(usec: 0)
1058
- end
1059
-
1060
- def yesterday
1061
- @yesterday ||= 1.day.ago
1062
- end
1063
-
1064
- def updated_ats
1065
- SequentialUpdatesAltIdTouchDisabled.order(:altid).pluck(:updated_at)
1066
- end
1067
-
1068
- def positions
1069
- SequentialUpdatesAltIdTouchDisabled.order(:altid).pluck(:pos)
1070
- end
1071
-
1072
- def test_sequential_updates_default_to_true_with_unique_index
1073
- assert_equal true, SequentialUpdatesAltIdTouchDisabled.new.send(:sequential_updates?)
1074
- end
1075
-
1076
- def test_deleting_item_does_not_touch_higher_items
1077
- Timecop.freeze(now) do
1078
- SequentialUpdatesAltIdTouchDisabled.first.destroy
1079
- updated_ats.each do |updated_at|
1080
- assert_equal updated_at.to_i, yesterday.to_i
1081
- end
1082
- assert_equal positions, [1, 2, 3]
1083
- end
1084
- end
1085
- end
1086
- end