acts_as_list 0.7.4 → 0.9.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.
@@ -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