acts_as_list 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .rvmrc
6
+ *.tmproj
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # ActsAsList
2
+
3
+ ## Description
4
+
5
+ This `acts_as extension` provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a `position` column defined as an integer on the mapped database table.
6
+
7
+
8
+ ## Example
9
+
10
+ class TodoList < ActiveRecord::Base
11
+ has_many :todo_items, :order => "position"
12
+ end
13
+
14
+ class TodoItem < ActiveRecord::Base
15
+ belongs_to :todo_list
16
+ acts_as_list :scope => :todo_list
17
+ end
18
+
19
+ todo_list.first.move_to_bottom
20
+ todo_list.last.move_higher
21
+
22
+ ## Notes
23
+ If the `position` column has a default value, then there is a slight change in behavior, i.e if you have 4 items in the list, and you insert 1, with a default position 0, it would be pushed to the bottom of the list. Please look at the tests for this and some recent pull requests for discussions related to this.
24
+
25
+ ## Contributing to `acts_as_list`
26
+
27
+ - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
28
+ - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
29
+ - Fork the project
30
+ - Start a feature/bugfix branch
31
+ - Commit and push until you are happy with your contribution
32
+ - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
33
+ - Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
34
+
35
+ ## Copyright
36
+
37
+ Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
32
32
  # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
33
33
  # * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection
34
- # act more line an array in it's indexing.
34
+ # act more like an array in its indexing.
35
35
  def acts_as_list(options = {})
36
36
  configuration = { :column => "position", :scope => "1 = 1", :top_of_list => 1}
37
37
  configuration.update(options) if options.is_a?(Hash)
@@ -74,8 +74,8 @@ module ActiveRecord
74
74
 
75
75
  #{scope_condition_method}
76
76
 
77
- before_destroy :decrement_positions_on_lower_items
78
- before_create :add_to_list_bottom
77
+ after_destroy :decrement_positions_on_lower_items
78
+ before_validation :add_to_list_bottom, :on => :create
79
79
  EOV
80
80
  end
81
81
  end
@@ -180,7 +180,19 @@ module ActiveRecord
180
180
 
181
181
  # Test if this record is in a list
182
182
  def in_list?
183
- !send(position_column).nil?
183
+ !not_in_list?
184
+ end
185
+
186
+ def not_in_list?
187
+ send(position_column).nil?
188
+ end
189
+
190
+ def default_position
191
+ acts_as_list_class.columns_hash[position_column.to_s].default
192
+ end
193
+
194
+ def default_position?
195
+ default_position == send(position_column)
184
196
  end
185
197
 
186
198
  private
@@ -189,7 +201,7 @@ module ActiveRecord
189
201
  end
190
202
 
191
203
  def add_to_list_bottom
192
- if self[position_column].nil?
204
+ if not_in_list? || default_position?
193
205
  self[position_column] = bottom_position_in_list.to_i + 1
194
206
  else
195
207
  increment_positions_on_lower_items(self[position_column])
@@ -210,7 +222,7 @@ module ActiveRecord
210
222
  def bottom_item(except = nil)
211
223
  conditions = scope_condition
212
224
  conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
213
- acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
225
+ acts_as_list_class.unscoped.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
214
226
  end
215
227
 
216
228
  # Forces item to assume the bottom position in the list.
@@ -231,10 +243,11 @@ module ActiveRecord
231
243
  end
232
244
 
233
245
  # This has the effect of moving all the lower items up one.
234
- def decrement_positions_on_lower_items
246
+ def decrement_positions_on_lower_items(position=nil)
235
247
  return unless in_list?
248
+ position ||= send(position_column).to_i
236
249
  acts_as_list_class.update_all(
237
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
250
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{position}"
238
251
  )
239
252
  end
240
253
 
@@ -260,11 +273,48 @@ module ActiveRecord
260
273
  )
261
274
  end
262
275
 
276
+ # Reorders intermediate items to support moving an item from old_position to new_position.
277
+ def shuffle_positions_on_intermediate_items(old_position, new_position)
278
+ return if old_position == new_position
279
+
280
+ if old_position < new_position
281
+ # Decrement position of intermediate items
282
+ #
283
+ # e.g., if moving an item from 2 to 5,
284
+ # move [3, 4, 5] to [2, 3, 4]
285
+ acts_as_list_class.update_all(
286
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}"
287
+ )
288
+ else
289
+ # Increment position of intermediate items
290
+ #
291
+ # e.g., if moving an item from 5 to 2,
292
+ # move [2, 3, 4] to [3, 4, 5]
293
+ acts_as_list_class.update_all(
294
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}"
295
+ )
296
+ end
297
+ end
298
+
263
299
  def insert_at_position(position)
264
- remove_from_list
265
- increment_positions_on_lower_items(position)
300
+ if in_list?
301
+ old_position = send(position_column).to_i
302
+ return if position == old_position
303
+ shuffle_positions_on_intermediate_items(old_position, position)
304
+ else
305
+ increment_positions_on_lower_items(position)
306
+ end
266
307
  self.update_attribute(position_column, position)
267
308
  end
309
+
310
+ # used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint
311
+ def store_at_0
312
+ if in_list?
313
+ old_position = send(position_column).to_i
314
+ update_attribute(position_column, 0)
315
+ decrement_positions_on_lower_items(old_position)
316
+ end
317
+ end
268
318
  end
269
319
  end
270
320
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module Acts
3
3
  module List
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
6
6
  end
7
7
  end
data/test/helper.rb CHANGED
@@ -10,3 +10,5 @@ end
10
10
  require 'test/unit'
11
11
  require 'active_record'
12
12
  require "#{File.dirname(__FILE__)}/../init"
13
+
14
+ require 'shared'
data/test/shared.rb ADDED
@@ -0,0 +1,7 @@
1
+ # Common shared behaviour.
2
+ module Shared
3
+ autoload :List, 'shared_list'
4
+ autoload :ListSub, 'shared_list_sub'
5
+ autoload :ZeroBased, 'shared_zero_based'
6
+ autoload :ArrayScopeList, 'shared_array_scope_list'
7
+ end
@@ -0,0 +1,156 @@
1
+ module Shared
2
+ module ArrayScopeList
3
+ def setup
4
+ (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter, :parent_id => 5, :parent_type => 'ParentClass' }
5
+ end
6
+
7
+ def test_reordering
8
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
9
+
10
+ ArrayScopeListMixin.find(2).move_lower
11
+ assert_equal [1, 3, 2, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
12
+
13
+ ArrayScopeListMixin.find(2).move_higher
14
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
15
+
16
+ ArrayScopeListMixin.find(1).move_to_bottom
17
+ assert_equal [2, 3, 4, 1], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
18
+
19
+ ArrayScopeListMixin.find(1).move_to_top
20
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
21
+
22
+ ArrayScopeListMixin.find(2).move_to_bottom
23
+ assert_equal [1, 3, 4, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
24
+
25
+ ArrayScopeListMixin.find(4).move_to_top
26
+ assert_equal [4, 1, 3, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
27
+ end
28
+
29
+ def test_move_to_bottom_with_next_to_last_item
30
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
31
+ ArrayScopeListMixin.find(3).move_to_bottom
32
+ assert_equal [1, 2, 4, 3], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
33
+ end
34
+
35
+ def test_next_prev
36
+ assert_equal ArrayScopeListMixin.find(2), ArrayScopeListMixin.find(1).lower_item
37
+ assert_nil ArrayScopeListMixin.find(1).higher_item
38
+ assert_equal ArrayScopeListMixin.find(3), ArrayScopeListMixin.find(4).higher_item
39
+ assert_nil ArrayScopeListMixin.find(4).lower_item
40
+ end
41
+
42
+ def test_injection
43
+ item = ArrayScopeListMixin.new(:parent_id => 1, :parent_type => 'ParentClass')
44
+ assert_equal '"mixins"."parent_id" = 1 AND "mixins"."parent_type" = \'ParentClass\'', item.scope_condition
45
+ assert_equal "pos", item.position_column
46
+ end
47
+
48
+ def test_insert
49
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
50
+ assert_equal 1, new.pos
51
+ assert new.first?
52
+ assert new.last?
53
+
54
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
55
+ assert_equal 2, new.pos
56
+ assert !new.first?
57
+ assert new.last?
58
+
59
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
60
+ assert_equal 3, new.pos
61
+ assert !new.first?
62
+ assert new.last?
63
+
64
+ new = ArrayScopeListMixin.create(:parent_id => 0, :parent_type => 'ParentClass')
65
+ assert_equal 1, new.pos
66
+ assert new.first?
67
+ assert new.last?
68
+ end
69
+
70
+ def test_insert_at
71
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
72
+ assert_equal 1, new.pos
73
+
74
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
75
+ assert_equal 2, new.pos
76
+
77
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
78
+ assert_equal 3, new.pos
79
+
80
+ new4 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
81
+ assert_equal 4, new4.pos
82
+
83
+ new4.insert_at(3)
84
+ assert_equal 3, new4.pos
85
+
86
+ new.reload
87
+ assert_equal 4, new.pos
88
+
89
+ new.insert_at(2)
90
+ assert_equal 2, new.pos
91
+
92
+ new4.reload
93
+ assert_equal 4, new4.pos
94
+
95
+ new5 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
96
+ assert_equal 5, new5.pos
97
+
98
+ new5.insert_at(1)
99
+ assert_equal 1, new5.pos
100
+
101
+ new4.reload
102
+ assert_equal 5, new4.pos
103
+ end
104
+
105
+ def test_delete_middle
106
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
107
+
108
+ ArrayScopeListMixin.find(2).destroy
109
+
110
+ assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
111
+
112
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
113
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
114
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
115
+
116
+ ArrayScopeListMixin.find(1).destroy
117
+
118
+ assert_equal [3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
119
+
120
+ assert_equal 1, ArrayScopeListMixin.find(3).pos
121
+ assert_equal 2, ArrayScopeListMixin.find(4).pos
122
+ end
123
+
124
+ def test_remove_from_list_should_then_fail_in_list?
125
+ assert_equal true, ArrayScopeListMixin.find(1).in_list?
126
+ ArrayScopeListMixin.find(1).remove_from_list
127
+ assert_equal false, ArrayScopeListMixin.find(1).in_list?
128
+ end
129
+
130
+ def test_remove_from_list_should_set_position_to_nil
131
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
132
+
133
+ ArrayScopeListMixin.find(2).remove_from_list
134
+
135
+ assert_equal [2, 1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
136
+
137
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
138
+ assert_equal nil, ArrayScopeListMixin.find(2).pos
139
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
140
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
141
+ end
142
+
143
+ def test_remove_before_destroy_does_not_shift_lower_items_twice
144
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
145
+
146
+ ArrayScopeListMixin.find(2).remove_from_list
147
+ ArrayScopeListMixin.find(2).destroy
148
+
149
+ assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
150
+
151
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
152
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
153
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,233 @@
1
+ module Shared
2
+ module List
3
+ def setup
4
+ (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 5 }
5
+ end
6
+
7
+ def test_reordering
8
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
9
+
10
+ ListMixin.find(2).move_lower
11
+ assert_equal [1, 3, 2, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
12
+
13
+ ListMixin.find(2).move_higher
14
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
15
+
16
+ ListMixin.find(1).move_to_bottom
17
+ assert_equal [2, 3, 4, 1], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
18
+
19
+ ListMixin.find(1).move_to_top
20
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
21
+
22
+ ListMixin.find(2).move_to_bottom
23
+ assert_equal [1, 3, 4, 2], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
24
+
25
+ ListMixin.find(4).move_to_top
26
+ assert_equal [4, 1, 3, 2], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
27
+ end
28
+
29
+ def test_move_to_bottom_with_next_to_last_item
30
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
31
+ ListMixin.find(3).move_to_bottom
32
+ assert_equal [1, 2, 4, 3], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
33
+ end
34
+
35
+ def test_next_prev
36
+ assert_equal ListMixin.find(2), ListMixin.find(1).lower_item
37
+ assert_nil ListMixin.find(1).higher_item
38
+ assert_equal ListMixin.find(3), ListMixin.find(4).higher_item
39
+ assert_nil ListMixin.find(4).lower_item
40
+ end
41
+
42
+ def test_injection
43
+ item = ListMixin.new(:parent_id => 1)
44
+ assert_equal '"mixins"."parent_id" = 1', item.scope_condition
45
+ assert_equal "pos", item.position_column
46
+ end
47
+
48
+ def test_insert
49
+ new = ListMixin.create(:parent_id => 20)
50
+ assert_equal 1, new.pos
51
+ assert new.first?
52
+ assert new.last?
53
+
54
+ new = ListMixin.create(:parent_id => 20)
55
+ assert_equal 2, new.pos
56
+ assert !new.first?
57
+ assert new.last?
58
+
59
+ new = ListMixin.create(:parent_id => 20)
60
+ assert_equal 3, new.pos
61
+ assert !new.first?
62
+ assert new.last?
63
+
64
+ new = ListMixin.create(:parent_id => 0)
65
+ assert_equal 1, new.pos
66
+ assert new.first?
67
+ assert new.last?
68
+ end
69
+
70
+ def test_insert_at
71
+ new = ListMixin.create(:parent_id => 20)
72
+ assert_equal 1, new.pos
73
+
74
+ new = ListMixin.create(:parent_id => 20)
75
+ assert_equal 2, new.pos
76
+
77
+ new = ListMixin.create(:parent_id => 20)
78
+ assert_equal 3, new.pos
79
+
80
+ new4 = ListMixin.create(:parent_id => 20)
81
+ assert_equal 4, new4.pos
82
+
83
+ new4.insert_at(3)
84
+ assert_equal 3, new4.pos
85
+
86
+ new.reload
87
+ assert_equal 4, new.pos
88
+
89
+ new.insert_at(2)
90
+ assert_equal 2, new.pos
91
+
92
+ new4.reload
93
+ assert_equal 4, new4.pos
94
+
95
+ new5 = ListMixin.create(:parent_id => 20)
96
+ assert_equal 5, new5.pos
97
+
98
+ new5.insert_at(1)
99
+ assert_equal 1, new5.pos
100
+
101
+ new4.reload
102
+ assert_equal 5, new4.pos
103
+ end
104
+
105
+ def test_insert_middle_with_unsaved_item
106
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
107
+
108
+ # new item, not saved yet
109
+ insert_this = ListMixin.new(:parent_id => 5)
110
+ insert_this.insert_at(2)
111
+
112
+ assert_equal [1, 2, 3, 4, 5], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:pos)
113
+ assert_equal [1, 5, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
114
+ end
115
+
116
+ def test_delete_middle
117
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
118
+
119
+ ListMixin.find(2).destroy
120
+
121
+ assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
122
+
123
+ assert_equal 1, ListMixin.find(1).pos
124
+ assert_equal 2, ListMixin.find(3).pos
125
+ assert_equal 3, ListMixin.find(4).pos
126
+
127
+ ListMixin.find(1).destroy
128
+
129
+ assert_equal [3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
130
+
131
+ assert_equal 1, ListMixin.find(3).pos
132
+ assert_equal 2, ListMixin.find(4).pos
133
+ end
134
+
135
+ def test_with_string_based_scope
136
+ new = ListWithStringScopeMixin.create(:parent_id => 500)
137
+ assert_equal 1, new.pos
138
+ assert new.first?
139
+ assert new.last?
140
+ end
141
+
142
+ def test_nil_scope
143
+ new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
144
+ new2.move_higher
145
+ assert_equal [new2, new1, new3], ListMixin.find(:all, :conditions => 'parent_id IS NULL', :order => 'pos')
146
+ end
147
+
148
+ def test_remove_from_list_should_then_fail_in_list?
149
+ assert_equal true, ListMixin.find(1).in_list?
150
+ ListMixin.find(1).remove_from_list
151
+ assert_equal false, ListMixin.find(1).in_list?
152
+ end
153
+
154
+ def test_remove_from_list_should_set_position_to_nil
155
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
156
+
157
+ ListMixin.find(2).remove_from_list
158
+
159
+ assert_equal [2, 1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
160
+
161
+ assert_equal 1, ListMixin.find(1).pos
162
+ assert_equal nil, ListMixin.find(2).pos
163
+ assert_equal 2, ListMixin.find(3).pos
164
+ assert_equal 3, ListMixin.find(4).pos
165
+ end
166
+
167
+ def test_remove_before_destroy_does_not_shift_lower_items_twice
168
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
169
+
170
+ ListMixin.find(2).remove_from_list
171
+ ListMixin.find(2).destroy
172
+
173
+ assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
174
+
175
+ assert_equal 1, ListMixin.find(1).pos
176
+ assert_equal 2, ListMixin.find(3).pos
177
+ assert_equal 3, ListMixin.find(4).pos
178
+ end
179
+
180
+ def test_before_destroy_callbacks_do_not_update_position_to_nil_before_deleting_the_record
181
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
182
+
183
+ # We need to trigger all the before_destroy callbacks without actually
184
+ # destroying the record so we can see the affect the callbacks have on
185
+ # the record.
186
+ # NOTE: Hotfix for rails3 ActiveRecord
187
+ list = ListMixin.find(2)
188
+ if list.respond_to?(:run_callbacks)
189
+ # Refactored to work according to Rails3 ActiveRSupport Callbacks <http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html>
190
+ list.run_callbacks :destroy, :before if rails_3
191
+ list.run_callbacks(:before_destroy) if !rails_3
192
+ else
193
+ list.send(:callback, :before_destroy)
194
+ end
195
+
196
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
197
+
198
+ assert_equal 1, ListMixin.find(1).pos
199
+ assert_equal 2, ListMixin.find(2).pos
200
+ assert_equal 2, ListMixin.find(3).pos
201
+ assert_equal 3, ListMixin.find(4).pos
202
+ end
203
+
204
+ def test_before_create_callback_adds_to_bottom
205
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
206
+
207
+ new = ListMixin.create(:parent_id => 5)
208
+ assert_equal 5, new.pos
209
+ assert !new.first?
210
+ assert new.last?
211
+
212
+ assert_equal [1, 2, 3, 4, 5], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
213
+ end
214
+
215
+ def test_before_create_callback_adds_to_given_position
216
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
217
+
218
+ new = ListMixin.create(:pos => 1, :parent_id => 5)
219
+ assert_equal 1, new.pos
220
+ assert new.first?
221
+ assert !new.last?
222
+
223
+ assert_equal [5, 1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
224
+
225
+ new = ListMixin.create(:pos => 3, :parent_id => 5)
226
+ assert_equal 3, new.pos
227
+ assert !new.first?
228
+ assert !new.last?
229
+
230
+ assert_equal [5, 1, 6, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
231
+ end
232
+ end
233
+ end