acts_as_list 0.7.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ require 'helper'
2
+
3
+ db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
4
+ ActiveRecord::Base.establish_connection(db_config)
5
+ ActiveRecord::Schema.verbose = false
6
+
7
+ class Section < ActiveRecord::Base
8
+ has_many :items
9
+ acts_as_list
10
+
11
+ scope :visible, -> { where(visible: true) }
12
+ end
13
+
14
+ class Item < ActiveRecord::Base
15
+ belongs_to :section
16
+ acts_as_list scope: :section
17
+
18
+ scope :visible, -> { where(visible: true).joins(:section).merge(Section.visible) }
19
+ end
20
+
21
+ class JoinedTestCase < Minitest::Test
22
+ def setup
23
+ ActiveRecord::Base.connection.create_table :sections do |t|
24
+ t.column :position, :integer
25
+ t.column :visible, :boolean, default: true
26
+ end
27
+
28
+ ActiveRecord::Base.connection.create_table :items do |t|
29
+ t.column :position, :integer
30
+ t.column :section_id, :integer
31
+ t.column :visible, :boolean, default: true
32
+ end
33
+
34
+ ActiveRecord::Base.connection.schema_cache.clear!
35
+ [Section, Item].each(&:reset_column_information)
36
+ super
37
+ end
38
+
39
+ def teardown
40
+ if ActiveRecord::VERSION::MAJOR >= 5
41
+ tables = ActiveRecord::Base.connection.data_sources
42
+ else
43
+ tables = ActiveRecord::Base.connection.tables
44
+ end
45
+ tables.each do |table|
46
+ ActiveRecord::Base.connection.drop_table(table)
47
+ end
48
+ super
49
+ end
50
+ end
51
+
52
+ # joining the relation returned by `#higher_items` or `#lower_items` to another table
53
+ # previously could result in ambiguous column names in the query
54
+ class TestHigherLowerItems < JoinedTestCase
55
+ def test_higher_items
56
+ section = Section.create
57
+ item1 = Item.create section: section
58
+ item2 = Item.create section: section
59
+ item3 = Item.create section: section
60
+ assert_equal item3.higher_items.visible, [item2, item1]
61
+ end
62
+
63
+ def test_lower_items
64
+ section = Section.create
65
+ item1 = Item.create section: section
66
+ item2 = Item.create section: section
67
+ item3 = Item.create section: section
68
+ assert_equal item1.lower_items.visible, [item2, item3]
69
+ end
70
+ end
data/test/test_list.rb CHANGED
@@ -1,13 +1,19 @@
1
1
  # NOTE: following now done in helper.rb (better Readability)
2
2
  require 'helper'
3
3
 
4
- ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
4
+ db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
5
+ ActiveRecord::Base.establish_connection(db_config)
5
6
  ActiveRecord::Schema.verbose = false
6
7
 
7
8
  def setup_db(position_options = {})
9
+ $default_position = position_options[:default]
10
+
11
+ # sqlite cannot drop/rename/alter columns and add constraints after table creation
12
+ sqlite = ENV.fetch("DB", "sqlite") == "sqlite"
13
+
8
14
  # AR caches columns options like defaults etc. Clear them!
9
15
  ActiveRecord::Base.connection.create_table :mixins do |t|
10
- t.column :pos, :integer, position_options
16
+ t.column :pos, :integer, position_options unless position_options[:positive] && sqlite
11
17
  t.column :active, :boolean, default: true
12
18
  t.column :parent_id, :integer
13
19
  t.column :parent_type, :string
@@ -16,9 +22,28 @@ def setup_db(position_options = {})
16
22
  t.column :state, :integer
17
23
  end
18
24
 
25
+ if position_options[:unique] && !(sqlite && position_options[:positive])
26
+ ActiveRecord::Base.connection.add_index :mixins, :pos, unique: true
27
+ end
28
+
29
+ if position_options[:positive]
30
+ if sqlite
31
+ # SQLite cannot add constraint after table creation, also cannot add unique inside ADD COLUMN
32
+ ActiveRecord::Base.connection.execute('ALTER TABLE mixins ADD COLUMN pos integer8 NOT NULL CHECK (pos > 0) DEFAULT 1')
33
+ ActiveRecord::Base.connection.execute('CREATE UNIQUE INDEX index_mixins_on_pos ON mixins(pos)')
34
+ else
35
+ ActiveRecord::Base.connection.execute('ALTER TABLE mixins ADD CONSTRAINT pos_check CHECK (pos > 0)')
36
+ end
37
+ end
38
+
39
+ # This table is used to test table names and column names quoting
40
+ ActiveRecord::Base.connection.create_table 'table-name' do |t|
41
+ t.column :order, :integer
42
+ end
43
+
19
44
  mixins = [ Mixin, ListMixin, ListMixinSub1, ListMixinSub2, ListWithStringScopeMixin,
20
45
  ArrayScopeListMixin, ZeroBasedMixin, DefaultScopedMixin,
21
- DefaultScopedWhereMixin, TopAdditionMixin, NoAdditionMixin ]
46
+ DefaultScopedWhereMixin, TopAdditionMixin, NoAdditionMixin, QuotedList ]
22
47
 
23
48
  mixins << EnumArrayScopeListMixin if rails_4
24
49
 
@@ -42,7 +67,13 @@ def rails_4
42
67
  end
43
68
 
44
69
  def teardown_db
45
- ActiveRecord::Base.connection.tables.each do |table|
70
+ if ActiveRecord::VERSION::MAJOR >= 5
71
+ tables = ActiveRecord::Base.connection.data_sources
72
+ else
73
+ tables = ActiveRecord::Base.connection.tables
74
+ end
75
+
76
+ tables.each do |table|
46
77
  ActiveRecord::Base.connection.drop_table(table)
47
78
  end
48
79
  end
@@ -101,6 +132,14 @@ class DefaultScopedWhereMixin < Mixin
101
132
  end
102
133
  end
103
134
 
135
+ class SequentialUpdatesDefault < Mixin
136
+ acts_as_list column: "pos"
137
+ end
138
+
139
+ class SequentialUpdatesFalseMixin < Mixin
140
+ acts_as_list column: "pos", sequential_updates: false
141
+ end
142
+
104
143
  class TopAdditionMixin < Mixin
105
144
  acts_as_list column: "pos", add_new_at: :top, scope: :parent_id
106
145
  end
@@ -149,6 +188,11 @@ end
149
188
  class TheBaseSubclass < TheBaseClass
150
189
  end
151
190
 
191
+ class QuotedList < ActiveRecord::Base
192
+ self.table_name = 'table-name'
193
+ acts_as_list column: :order
194
+ end
195
+
152
196
  class ActsAsListTestCase < Minitest::Test
153
197
  # No default test required as this class is abstract.
154
198
  # Need for test/unit.
@@ -184,6 +228,37 @@ class ListTest < ActsAsListTestCase
184
228
  setup_db
185
229
  super
186
230
  end
231
+
232
+ def test_insert_race_condition
233
+ # the bigger n is the more likely we will have a race condition
234
+ n = 1000
235
+ (1..n).each do |counter|
236
+ node = ListMixin.new parent_id: 1
237
+ node.pos = counter
238
+ node.save!
239
+ end
240
+
241
+ wait_for_it = true
242
+ threads = []
243
+ 4.times do |i|
244
+ threads << Thread.new do
245
+ true while wait_for_it
246
+ ActiveRecord::Base.connection_pool.with_connection do |c|
247
+ n.times do
248
+ begin
249
+ ListMixin.where(parent_id: 1).order('pos').last.insert_at(1)
250
+ rescue Exception
251
+ # ignore SQLite3::SQLException due to table locking
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+ wait_for_it = false
258
+ threads.each(&:join)
259
+
260
+ assert_equal((1..n).to_a, ListMixin.where(parent_id: 1).order('pos').map(&:pos))
261
+ end
187
262
  end
188
263
 
189
264
  class ListWithCallbackTest < ActsAsListTestCase
@@ -243,6 +318,15 @@ class ArrayScopeListTestWithDefault < ActsAsListTestCase
243
318
  end
244
319
  end
245
320
 
321
+ class QuotingTestList < ActsAsListTestCase
322
+ include Shared::Quoting
323
+
324
+ def setup
325
+ setup_db_with_default
326
+ super
327
+ end
328
+ end
329
+
246
330
  class DefaultScopedTest < ActsAsListTestCase
247
331
  def setup
248
332
  setup_db
@@ -260,6 +344,11 @@ class DefaultScopedTest < ActsAsListTestCase
260
344
  assert !new.first?
261
345
  assert new.last?
262
346
 
347
+ new = DefaultScopedMixin.acts_as_list_no_update { DefaultScopedMixin.create }
348
+ assert_equal_or_nil $default_position, new.pos
349
+ assert_equal $default_position.is_a?(Fixnum), new.first?
350
+ assert !new.last?
351
+
263
352
  new = DefaultScopedMixin.create
264
353
  assert_equal 7, new.pos
265
354
  assert !new.first?
@@ -295,6 +384,9 @@ class DefaultScopedTest < ActsAsListTestCase
295
384
  new = DefaultScopedMixin.create
296
385
  assert_equal 6, new.pos
297
386
 
387
+ new_noup = DefaultScopedMixin.acts_as_list_no_update { DefaultScopedMixin.create }
388
+ assert_equal_or_nil $default_position, new_noup.pos
389
+
298
390
  new = DefaultScopedMixin.create
299
391
  assert_equal 7, new.pos
300
392
 
@@ -321,6 +413,9 @@ class DefaultScopedTest < ActsAsListTestCase
321
413
 
322
414
  new4.reload
323
415
  assert_equal 4, new4.pos
416
+
417
+ new_noup.reload
418
+ assert_equal_or_nil $default_position, new_noup.pos
324
419
  end
325
420
 
326
421
  def test_update_position
@@ -353,6 +448,11 @@ class DefaultScopedWhereTest < ActsAsListTestCase
353
448
  assert !new.first?
354
449
  assert new.last?
355
450
 
451
+ new = DefaultScopedWhereMixin.acts_as_list_no_update { DefaultScopedWhereMixin.create }
452
+ assert_equal_or_nil $default_position, new.pos
453
+ assert_equal $default_position.is_a?(Fixnum), new.first?
454
+ assert !new.last?
455
+
356
456
  new = DefaultScopedWhereMixin.create
357
457
  assert_equal 7, new.pos
358
458
  assert !new.first?
@@ -391,6 +491,9 @@ class DefaultScopedWhereTest < ActsAsListTestCase
391
491
  new = DefaultScopedWhereMixin.create
392
492
  assert_equal 7, new.pos
393
493
 
494
+ new_noup = DefaultScopedWhereMixin.acts_as_list_no_update { DefaultScopedWhereMixin.create }
495
+ assert_equal_or_nil $default_position, new_noup.pos
496
+
394
497
  new4 = DefaultScopedWhereMixin.create
395
498
  assert_equal 8, new4.pos
396
499
 
@@ -414,6 +517,9 @@ class DefaultScopedWhereTest < ActsAsListTestCase
414
517
 
415
518
  new4.reload
416
519
  assert_equal 4, new4.pos
520
+
521
+ new_noup.reload
522
+ assert_equal_or_nil $default_position, new_noup.pos
417
523
  end
418
524
 
419
525
  def test_update_position
@@ -468,10 +574,20 @@ class MultiDestroyTest < ActsAsListTestCase
468
574
  new3 = DefaultScopedMixin.create
469
575
  assert_equal 3, new3.pos
470
576
 
577
+ new4 = DefaultScopedMixin.create
578
+ assert_equal 4, new4.pos
579
+
471
580
  new1.destroy
472
581
  new2.destroy
473
582
  new3.reload
583
+ new4.reload
474
584
  assert_equal 1, new3.pos
585
+ assert_equal 2, new4.pos
586
+
587
+ DefaultScopedMixin.acts_as_list_no_update { new3.destroy }
588
+
589
+ new4.reload
590
+ assert_equal 2, new4.pos
475
591
  end
476
592
  end
477
593
 
@@ -512,19 +628,19 @@ class MultipleListsTest < ActsAsListTestCase
512
628
  end
513
629
 
514
630
  def test_check_scope_order
515
- assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).order(:pos).map(&:id)
516
- assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order(:pos).map(&:id)
631
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).order('pos').map(&:id)
632
+ assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order('pos').map(&:id)
517
633
  ListMixin.find(4).update_attributes(:parent_id => 2, :pos => 2)
518
- assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order(:pos).map(&:id)
519
- assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order(:pos).map(&:id)
634
+ assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order('pos').map(&:id)
635
+ assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order('pos').map(&:id)
520
636
  end
521
637
 
522
638
  def test_check_scope_position
523
639
  assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 1).map(&:pos)
524
640
  assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 2).map(&:pos)
525
641
  ListMixin.find(4).update_attributes(:parent_id => 2, :pos => 2)
526
- assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order(:pos).map(&:pos)
527
- assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order(:pos).map(&:pos)
642
+ assert_equal [1, 2, 3], ListMixin.where(:parent_id => 1).order('pos').map(&:pos)
643
+ assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order('pos').map(&:pos)
528
644
  end
529
645
  end
530
646
 
@@ -556,50 +672,213 @@ class MultipleListsArrayScopeTest < ActsAsListTestCase
556
672
  end
557
673
 
558
674
  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(:pos).map(&:id)
560
- assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(:pos).map(&:id)
675
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
676
+ assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order('pos').map(&:id)
561
677
  ArrayScopeListMixin.find(2).update_attributes(:parent_id => 2, :pos => 2,:parent_type => 'something')
562
- assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(:pos).map(&:id)
563
- assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order(:pos).map(&:id)
678
+ assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
679
+ assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order('pos').map(&:id)
564
680
  end
565
681
 
566
682
  def test_position_after_all_scope_properties_are_changed
567
683
  assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
568
684
  assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').map(&:pos)
569
685
  ArrayScopeListMixin.find(4).update_attributes(:parent_id => 2, :pos => 2, :parent_type => 'something')
570
- assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:pos)
571
- assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(:pos).map(&:pos)
686
+ assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
687
+ assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order('pos').map(&:pos)
572
688
  end
573
689
 
574
690
  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(:pos).map(&:id)
576
- assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(:pos).map(&:id)
691
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
692
+ assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order('pos').map(&:id)
577
693
  ArrayScopeListMixin.find(2).update_attributes(:parent_id => 3, :pos => 2)
578
- assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(:pos).map(&:id)
579
- assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order(:pos).map(&:id)
694
+ assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
695
+ assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order('pos').map(&:id)
580
696
  end
581
697
 
582
698
  def test_position_after_one_scope_property_is_changed
583
699
  assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
584
700
  assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').map(&:pos)
585
701
  ArrayScopeListMixin.find(4).update_attributes(:parent_id => 3, :pos => 2)
586
- assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:pos)
587
- assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(:pos).map(&:pos)
702
+ assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
703
+ assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order('pos').map(&:pos)
588
704
  end
589
705
 
590
706
  def test_order_after_moving_to_empty_list
591
- assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:id)
592
- assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(:pos).map(&:id)
707
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:id)
708
+ assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order('pos').map(&:id)
593
709
  ArrayScopeListMixin.find(2).update_attributes(:parent_id => 4, :pos => 1)
594
- assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order(:pos).map(&:id)
595
- assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order(:pos).map(&:id)
710
+ assert_equal [1, 3, 4], ArrayScopeListMixin.where(:parent_id => 1,:parent_type => 'anything').order('pos').map(&:id)
711
+ assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order('pos').map(&:id)
596
712
  end
597
713
 
598
714
  def test_position_after_moving_to_empty_list
599
715
  assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').map(&:pos)
600
716
  assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').map(&:pos)
601
717
  ArrayScopeListMixin.find(2).update_attributes(:parent_id => 4, :pos => 1)
602
- assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order(:pos).map(&:pos)
603
- assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(:pos).map(&:pos)
718
+ assert_equal [1, 2, 3], ArrayScopeListMixin.where(:parent_id => 1, :parent_type => 'anything').order('pos').map(&:pos)
719
+ assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order('pos').map(&:pos)
720
+ end
721
+ end
722
+
723
+ require 'timecop'
724
+
725
+ class TouchTest < ActsAsListTestCase
726
+ def setup
727
+ setup_db
728
+ Timecop.freeze(yesterday) do
729
+ 4.times { ListMixin.create! }
730
+ end
731
+ end
732
+
733
+ def now
734
+ @now ||= Time.current
735
+ end
736
+
737
+ def yesterday
738
+ @yesterday ||= 1.day.ago
739
+ end
740
+
741
+ def updated_ats
742
+ ListMixin.order(:id).pluck(:updated_at)
743
+ end
744
+
745
+ def test_moving_item_lower_touches_self_and_lower_item
746
+ Timecop.freeze(now) do
747
+ ListMixin.first.move_lower
748
+ updated_ats[0..1].each do |updated_at|
749
+ assert_equal updated_at.to_i, now.to_i
750
+ end
751
+ updated_ats[2..3].each do |updated_at|
752
+ assert_equal updated_at.to_i, yesterday.to_i
753
+ end
754
+ end
755
+ end
756
+
757
+ def test_moving_item_higher_touches_self_and_higher_item
758
+ Timecop.freeze(now) do
759
+ ListMixin.all.second.move_higher
760
+ updated_ats[0..1].each do |updated_at|
761
+ assert_equal updated_at.to_i, now.to_i
762
+ end
763
+ updated_ats[2..3].each do |updated_at|
764
+ assert_equal updated_at.to_i, yesterday.to_i
765
+ end
766
+ end
767
+ end
768
+
769
+ def test_moving_item_to_bottom_touches_all_other_items
770
+ Timecop.freeze(now) do
771
+ ListMixin.first.move_to_bottom
772
+ updated_ats.each do |updated_at|
773
+ assert_equal updated_at.to_i, now.to_i
774
+ end
775
+ end
776
+ end
777
+
778
+ def test_moving_item_to_top_touches_all_other_items
779
+ Timecop.freeze(now) do
780
+ ListMixin.last.move_to_top
781
+ updated_ats.each do |updated_at|
782
+ assert_equal updated_at.to_i, now.to_i
783
+ end
784
+ end
785
+ end
786
+
787
+ def test_removing_item_touches_all_lower_items
788
+ Timecop.freeze(now) do
789
+ ListMixin.all.third.remove_from_list
790
+ updated_ats[0..1].each do |updated_at|
791
+ assert_equal updated_at.to_i, yesterday.to_i
792
+ end
793
+ updated_ats[2..2].each do |updated_at|
794
+ assert_equal updated_at.to_i, now.to_i
795
+ end
796
+ end
797
+ end
798
+ end
799
+
800
+ class ActsAsListTopTest < ActsAsListTestCase
801
+ def setup
802
+ setup_db
803
+ end
804
+
805
+ def test_acts_as_list_top
806
+ assert_equal 1, TheBaseSubclass.new.acts_as_list_top
807
+ assert_equal 0, ZeroBasedMixin.new.acts_as_list_top
808
+ end
809
+
810
+ def test_class_acts_as_list_top
811
+ assert_equal 1, TheBaseSubclass.acts_as_list_top
812
+ assert_equal 0, ZeroBasedMixin.acts_as_list_top
813
+ end
814
+ end
815
+
816
+ class NilPositionTest < ActsAsListTestCase
817
+ def setup
818
+ setup_db
819
+ end
820
+
821
+ def test_nil_position_ordering
822
+ new1 = DefaultScopedMixin.create pos: nil
823
+ new2 = DefaultScopedMixin.create pos: nil
824
+ new3 = DefaultScopedMixin.create pos: nil
825
+ DefaultScopedMixin.update_all(pos: nil)
826
+
827
+ assert_equal [nil, nil, nil], DefaultScopedMixin.all.map(&:pos)
828
+
829
+ new1.reload.pos = 1
830
+ new1.save
831
+
832
+ new3.reload.pos = 1
833
+ new3.save
834
+
835
+ assert_equal [1, 2], DefaultScopedMixin.where("pos IS NOT NULL").map(&:pos)
836
+ assert_equal [3, 1], DefaultScopedMixin.where("pos IS NOT NULL").map(&:id)
837
+ assert_nil new2.reload.pos
838
+
839
+ new2.reload.pos = 1
840
+ new2.save
841
+
842
+ assert_equal [1, 2, 3], DefaultScopedMixin.all.map(&:pos)
843
+ assert_equal [2, 3, 1], DefaultScopedMixin.all.map(&:id)
844
+ end
845
+ end
846
+
847
+ class SequentialUpdatesOptionDefaultTest < ActsAsListTestCase
848
+ def setup
849
+ setup_db
850
+ end
851
+
852
+ def test_sequential_updates_default_to_false_without_unique_index
853
+ assert_equal false, SequentialUpdatesDefault.new.send(:sequential_updates?)
854
+ end
855
+ end
856
+
857
+ class SequentialUpdatesMixinNotNullUniquePositiveConstraintsTest < ActsAsListTestCase
858
+ def setup
859
+ setup_db null: false, unique: true, positive: true
860
+ (1..4).each { |counter| SequentialUpdatesDefault.create!({pos: counter}) }
861
+ end
862
+
863
+ def test_sequential_updates_default_to_true_with_unique_index
864
+ assert_equal true, SequentialUpdatesDefault.new.send(:sequential_updates?)
865
+ end
866
+
867
+ def test_sequential_updates_option_override_with_false
868
+ assert_equal false, SequentialUpdatesFalseMixin.new.send(:sequential_updates?)
869
+ end
870
+
871
+ def test_insert_at
872
+ new = SequentialUpdatesDefault.create
873
+ assert_equal 5, new.pos
874
+
875
+ new.insert_at(1)
876
+ assert_equal 1, new.pos
877
+
878
+ new.insert_at(5)
879
+ assert_equal 5, new.pos
880
+
881
+ new.insert_at(3)
882
+ assert_equal 3, new.pos
604
883
  end
605
884
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -10,34 +10,34 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-04-15 00:00:00.000000000 Z
13
+ date: 2017-01-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - ! '>='
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - ! '>='
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  version: '3.0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: bundler
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - ! '>='
33
+ - - ">="
34
34
  - !ruby/object:Gem::Version
35
35
  version: 1.0.0
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - ! '>='
40
+ - - ">="
41
41
  - !ruby/object:Gem::Version
42
42
  version: 1.0.0
43
43
  description: This "acts_as" extension provides the capabilities for sorting and reordering
@@ -49,9 +49,9 @@ executables: []
49
49
  extensions: []
50
50
  extra_rdoc_files: []
51
51
  files:
52
- - .gemtest
53
- - .gitignore
54
- - .travis.yml
52
+ - ".gemtest"
53
+ - ".gitignore"
54
+ - ".travis.yml"
55
55
  - Appraisals
56
56
  - CHANGELOG.md
57
57
  - Gemfile
@@ -62,18 +62,30 @@ files:
62
62
  - gemfiles/rails_3_2.gemfile
63
63
  - gemfiles/rails_4_1.gemfile
64
64
  - gemfiles/rails_4_2.gemfile
65
+ - gemfiles/rails_5_0.gemfile
65
66
  - init.rb
66
67
  - lib/acts_as_list.rb
68
+ - lib/acts_as_list/active_record/acts/add_new_at_method_definer.rb
69
+ - lib/acts_as_list/active_record/acts/aux_method_definer.rb
70
+ - lib/acts_as_list/active_record/acts/callback_definer.rb
71
+ - lib/acts_as_list/active_record/acts/column_method_definer.rb
67
72
  - lib/acts_as_list/active_record/acts/list.rb
73
+ - lib/acts_as_list/active_record/acts/no_update.rb
74
+ - lib/acts_as_list/active_record/acts/scope_method_definer.rb
75
+ - lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb
76
+ - lib/acts_as_list/active_record/acts/top_of_list_method_definer.rb
68
77
  - lib/acts_as_list/version.rb
78
+ - test/database.yml
69
79
  - test/helper.rb
70
80
  - test/shared.rb
71
81
  - test/shared_array_scope_list.rb
72
82
  - test/shared_list.rb
73
83
  - test/shared_list_sub.rb
74
84
  - test/shared_no_addition.rb
85
+ - test/shared_quoting.rb
75
86
  - test/shared_top_addition.rb
76
87
  - test/shared_zero_based.rb
88
+ - test/test_joined_list.rb
77
89
  - test/test_list.rb
78
90
  homepage: http://github.com/swanandp/acts_as_list
79
91
  licenses:
@@ -85,12 +97,12 @@ require_paths:
85
97
  - lib
86
98
  required_ruby_version: !ruby/object:Gem::Requirement
87
99
  requirements:
88
- - - ! '>='
100
+ - - ">="
89
101
  - !ruby/object:Gem::Version
90
102
  version: 1.9.2
91
103
  required_rubygems_version: !ruby/object:Gem::Requirement
92
104
  requirements:
93
- - - ! '>='
105
+ - - ">="
94
106
  - !ruby/object:Gem::Version
95
107
  version: '0'
96
108
  requirements: []
@@ -101,12 +113,15 @@ specification_version: 4
101
113
  summary: A gem adding sorting, reordering capabilities to an active_record model,
102
114
  allowing it to act as a list
103
115
  test_files:
116
+ - test/database.yml
104
117
  - test/helper.rb
105
118
  - test/shared.rb
106
119
  - test/shared_array_scope_list.rb
107
120
  - test/shared_list.rb
108
121
  - test/shared_list_sub.rb
109
122
  - test/shared_no_addition.rb
123
+ - test/shared_quoting.rb
110
124
  - test/shared_top_addition.rb
111
125
  - test/shared_zero_based.rb
126
+ - test/test_joined_list.rb
112
127
  - test/test_list.rb