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.
- checksums.yaml +5 -13
- data/.gitignore +1 -0
- data/.travis.yml +23 -3
- data/Appraisals +13 -3
- data/CHANGELOG.md +131 -2
- data/Gemfile +18 -11
- data/README.md +49 -6
- data/gemfiles/rails_3_2.gemfile +16 -7
- data/gemfiles/rails_4_1.gemfile +16 -7
- data/gemfiles/rails_4_2.gemfile +16 -7
- data/gemfiles/rails_5_0.gemfile +32 -0
- data/lib/acts_as_list/active_record/acts/add_new_at_method_definer.rb +9 -0
- data/lib/acts_as_list/active_record/acts/aux_method_definer.rb +9 -0
- data/lib/acts_as_list/active_record/acts/callback_definer.rb +19 -0
- data/lib/acts_as_list/active_record/acts/column_method_definer.rb +50 -0
- data/lib/acts_as_list/active_record/acts/list.rb +247 -291
- data/lib/acts_as_list/active_record/acts/no_update.rb +50 -0
- data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +49 -0
- data/lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb +21 -0
- data/lib/acts_as_list/active_record/acts/top_of_list_method_definer.rb +13 -0
- data/lib/acts_as_list/version.rb +1 -1
- data/lib/acts_as_list.rb +8 -14
- data/test/database.yml +16 -0
- data/test/helper.rb +14 -2
- data/test/shared.rb +1 -0
- data/test/shared_array_scope_list.rb +19 -4
- data/test/shared_list.rb +44 -8
- data/test/shared_list_sub.rb +61 -2
- data/test/shared_no_addition.rb +13 -2
- data/test/shared_quoting.rb +21 -0
- data/test/shared_top_addition.rb +34 -13
- data/test/shared_zero_based.rb +11 -0
- data/test/test_joined_list.rb +70 -0
- data/test/test_list.rb +307 -28
- metadata +26 -11
@@ -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
|
-
|
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::
|
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(
|
516
|
-
assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 2).order(
|
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(
|
519
|
-
assert_equal [5, 4, 6, 7, 8], ListMixin.where(:parent_id => 2).order(
|
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(
|
527
|
-
assert_equal [1, 2, 3, 4, 5], ListMixin.where(:parent_id => 2).order(
|
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(
|
560
|
-
assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(
|
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(
|
563
|
-
assert_equal [5, 2, 6, 7, 8], ArrayScopeListMixin.where(:parent_id => 2,:parent_type => 'something').order(
|
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(
|
571
|
-
assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 2, :parent_type => 'something').order(
|
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(
|
576
|
-
assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(
|
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(
|
579
|
-
assert_equal [9, 2, 10, 11, 12], ArrayScopeListMixin.where(:parent_id => 3,:parent_type => 'anything').order(
|
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(
|
587
|
-
assert_equal [1, 2, 3, 4, 5], ArrayScopeListMixin.where(:parent_id => 3, :parent_type => 'anything').order(
|
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(
|
592
|
-
assert_equal [], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(
|
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(
|
595
|
-
assert_equal [2], ArrayScopeListMixin.where(:parent_id => 4,:parent_type => 'anything').order(
|
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(
|
603
|
-
assert_equal [1], ArrayScopeListMixin.where(:parent_id => 4, :parent_type => 'anything').order(
|
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.
|
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:
|
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
|