acts_as_list 0.1.4 → 0.1.8

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.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .rvmrc
6
+ *.tmproj
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in acts_as_list-rails3.gemspec
4
4
  gemspec
5
+
6
+ gem 'rake'
data/README.md ADDED
@@ -0,0 +1,60 @@
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
+ ## Installation
8
+
9
+ In your Gemfile:
10
+
11
+ gem 'acts_as_list'
12
+
13
+ Or, from the command line:
14
+
15
+ gem install acts_as_list
16
+
17
+ ## Example
18
+
19
+ class TodoList < ActiveRecord::Base
20
+ has_many :todo_items, :order => "position"
21
+ end
22
+
23
+ class TodoItem < ActiveRecord::Base
24
+ belongs_to :todo_list
25
+ acts_as_list :scope => :todo_list
26
+ end
27
+
28
+ todo_list.first.move_to_bottom
29
+ todo_list.last.move_higher
30
+
31
+ ## Notes
32
+ 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.
33
+
34
+ All `position` queries (select, update, etc.) inside gem methods are executed without the default scope (i.e. `Model.unscoped`), this will prevent nasty issues when the default scope is different from `acts_as_list` scope.
35
+
36
+ ## Versions
37
+ All versions `0.1.5` onwards require Rails 3.0.x and higher.
38
+
39
+ ## Build Status
40
+ [![Build Status](https://secure.travis-ci.org/swanandp/acts_as_list.png)](https://secure.travis-ci.org/swanandp/acts_as_list)
41
+
42
+ ## Roadmap
43
+
44
+ 1. Sort based feature
45
+ 2. Rails 4 compatibility and bye bye Rails 2! Older versions would of course continue to work with Rails 2, but there won't be any support on those.
46
+
47
+ ## Contributing to `acts_as_list`
48
+
49
+ - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
50
+ - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
51
+ - Fork the project
52
+ - Start a feature/bugfix branch
53
+ - Commit and push until you are happy with your contribution
54
+ - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
55
+ - 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.
56
+ - I would recommend using Rails 3.1.x and higher for testing the build before a pull request. The current test harness does not quite work with 3.0.x. The plugin itself works, but the issue lies with testing infrastructure.
57
+
58
+ ## Copyright
59
+
60
+ Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
data/Rakefile CHANGED
@@ -12,11 +12,9 @@ desc 'Test the acts_as_list plugin.'
12
12
  Rake::TestTask.new(:test) do |t|
13
13
  t.libs << 'lib' << 'test'
14
14
  t.pattern = 'test/**/test_*.rb'
15
- t.verbose = true
15
+ t.verbose = false
16
16
  end
17
17
 
18
-
19
-
20
18
  # Run the rdoc task to generate rdocs for this gem
21
19
  require 'rdoc/task'
22
20
  RDoc::Task.new do |rdoc|
data/acts_as_list.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
 
25
25
 
26
26
  # Dependencies (installed via 'bundle install')...
27
- s.add_development_dependency("bundler", ["~> 1.0.0"])
27
+ s.add_development_dependency("bundler", [">= 1.0.0"])
28
28
  s.add_development_dependency("activerecord", [">= 1.15.4.7794"])
29
29
  s.add_development_dependency("rdoc")
30
30
  s.add_development_dependency("sqlite3")
data/init.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  $:.unshift "#{File.dirname(__FILE__)}/lib"
2
2
  require 'acts_as_list'
3
+
4
+ ActsAsList::Railtie.insert
@@ -26,14 +26,15 @@ module ActiveRecord
26
26
  # Configuration options are:
27
27
  #
28
28
  # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
29
- # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
30
- # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
29
+ # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
30
+ # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
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
+ # * +add_new_at+ - specifies whether objects get added to the :top or :bottom of the list. (default: +bottom+)
35
36
  def acts_as_list(options = {})
36
- configuration = { :column => "position", :scope => "1 = 1", :top_of_list => 1}
37
+ configuration = { :column => "position", :scope => "1 = 1", :top_of_list => 1, :add_new_at => :bottom}
37
38
  configuration.update(options) if options.is_a?(Hash)
38
39
 
39
40
  configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
@@ -47,7 +48,7 @@ module ActiveRecord
47
48
  elsif configuration[:scope].is_a?(Array)
48
49
  scope_condition_method = %(
49
50
  def scope_condition
50
- attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
51
+ attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
51
52
  memo[column.intern] = send(column.intern); memo
52
53
  end
53
54
  self.class.send(:sanitize_sql_hash_for_conditions, attrs)
@@ -58,7 +59,7 @@ module ActiveRecord
58
59
  end
59
60
 
60
61
  class_eval <<-EOV
61
- include ActiveRecord::Acts::List::InstanceMethods
62
+ include ::ActiveRecord::Acts::List::InstanceMethods
62
63
 
63
64
  def acts_as_list_top
64
65
  #{configuration[:top_of_list]}.to_i
@@ -74,8 +75,9 @@ module ActiveRecord
74
75
 
75
76
  #{scope_condition_method}
76
77
 
77
- before_destroy :decrement_positions_on_lower_items
78
- before_create :add_to_list_bottom
78
+ after_destroy :decrement_positions_on_lower_items
79
+ before_create :add_to_list_#{configuration[:add_new_at]}
80
+ after_update :update_positions
79
81
  EOV
80
82
  end
81
83
  end
@@ -134,20 +136,20 @@ module ActiveRecord
134
136
  def remove_from_list
135
137
  if in_list?
136
138
  decrement_positions_on_lower_items
137
- update_attribute position_column, nil
139
+ update_attributes! position_column => nil
138
140
  end
139
141
  end
140
142
 
141
143
  # Increase the position of this item without adjusting the rest of the list.
142
144
  def increment_position
143
145
  return unless in_list?
144
- update_attribute position_column, self.send(position_column).to_i + 1
146
+ update_attributes! position_column => self.send(position_column).to_i + 1
145
147
  end
146
148
 
147
149
  # Decrease the position of this item without adjusting the rest of the list.
148
150
  def decrement_position
149
151
  return unless in_list?
150
- update_attribute position_column, self.send(position_column).to_i - 1
152
+ update_attributes! position_column => self.send(position_column).to_i - 1
151
153
  end
152
154
 
153
155
  # Return +true+ if this object is the first in the list.
@@ -165,7 +167,7 @@ module ActiveRecord
165
167
  # Return the next higher item in the list.
166
168
  def higher_item
167
169
  return nil unless in_list?
168
- acts_as_list_class.find(:first, :conditions =>
170
+ acts_as_list_class.unscoped.find(:first, :conditions =>
169
171
  "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
170
172
  )
171
173
  end
@@ -173,23 +175,36 @@ module ActiveRecord
173
175
  # Return the next lower item in the list.
174
176
  def lower_item
175
177
  return nil unless in_list?
176
- acts_as_list_class.find(:first, :conditions =>
178
+ acts_as_list_class.unscoped.find(:first, :conditions =>
177
179
  "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
178
180
  )
179
181
  end
180
182
 
181
183
  # Test if this record is in a list
182
184
  def in_list?
183
- !send(position_column).nil?
185
+ !not_in_list?
186
+ end
187
+
188
+ def not_in_list?
189
+ send(position_column).nil?
190
+ end
191
+
192
+ def default_position
193
+ acts_as_list_class.columns_hash[position_column.to_s].default
194
+ end
195
+
196
+ def default_position?
197
+ default_position == send(position_column)
184
198
  end
185
199
 
186
200
  private
187
201
  def add_to_list_top
188
202
  increment_positions_on_all_items
203
+ self[position_column] = acts_as_list_top
189
204
  end
190
205
 
191
206
  def add_to_list_bottom
192
- if self[position_column].nil?
207
+ if not_in_list? || default_position?
193
208
  self[position_column] = bottom_position_in_list.to_i + 1
194
209
  else
195
210
  increment_positions_on_lower_items(self[position_column])
@@ -210,62 +225,107 @@ module ActiveRecord
210
225
  def bottom_item(except = nil)
211
226
  conditions = scope_condition
212
227
  conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
213
- acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
228
+ acts_as_list_class.unscoped.find(:first, :conditions => conditions, :order => "#{acts_as_list_class.table_name}.#{position_column} DESC")
214
229
  end
215
230
 
216
231
  # Forces item to assume the bottom position in the list.
217
232
  def assume_bottom_position
218
- update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
233
+ update_attributes!(position_column => bottom_position_in_list(self).to_i + 1)
219
234
  end
220
235
 
221
236
  # Forces item to assume the top position in the list.
222
237
  def assume_top_position
223
- update_attribute(position_column, acts_as_list_top)
238
+ update_attributes!(position_column => acts_as_list_top)
224
239
  end
225
240
 
226
241
  # This has the effect of moving all the higher items up one.
227
242
  def decrement_positions_on_higher_items(position)
228
- acts_as_list_class.update_all(
243
+ acts_as_list_class.unscoped.update_all(
229
244
  "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
230
245
  )
231
246
  end
232
247
 
233
248
  # This has the effect of moving all the lower items up one.
234
- def decrement_positions_on_lower_items
249
+ def decrement_positions_on_lower_items(position=nil)
235
250
  return unless in_list?
236
- acts_as_list_class.update_all(
237
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
251
+ position ||= send(position_column).to_i
252
+ acts_as_list_class.unscoped.update_all(
253
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{position}"
238
254
  )
239
255
  end
240
256
 
241
257
  # This has the effect of moving all the higher items down one.
242
258
  def increment_positions_on_higher_items
243
259
  return unless in_list?
244
- acts_as_list_class.update_all(
260
+ acts_as_list_class.unscoped.update_all(
245
261
  "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
246
262
  )
247
263
  end
248
264
 
249
265
  # This has the effect of moving all the lower items down one.
250
266
  def increment_positions_on_lower_items(position)
251
- acts_as_list_class.update_all(
267
+ acts_as_list_class.unscoped.update_all(
252
268
  "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
253
269
  )
254
270
  end
255
271
 
256
272
  # Increments position (<tt>position_column</tt>) of all items in the list.
257
273
  def increment_positions_on_all_items
258
- acts_as_list_class.update_all(
274
+ acts_as_list_class.unscoped.update_all(
259
275
  "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
260
276
  )
261
277
  end
262
278
 
279
+ # Reorders intermediate items to support moving an item from old_position to new_position.
280
+ def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil)
281
+ return if old_position == new_position
282
+ avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{avoid_id}" : ''
283
+ if old_position < new_position
284
+ # Decrement position of intermediate items
285
+ #
286
+ # e.g., if moving an item from 2 to 5,
287
+ # move [3, 4, 5] to [2, 3, 4]
288
+ acts_as_list_class.unscoped.update_all(
289
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}#{avoid_id_condition}"
290
+ )
291
+ else
292
+ # Increment position of intermediate items
293
+ #
294
+ # e.g., if moving an item from 5 to 2,
295
+ # move [2, 3, 4] to [3, 4, 5]
296
+ acts_as_list_class.unscoped.update_all(
297
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}#{avoid_id_condition}"
298
+ )
299
+ end
300
+ end
301
+
263
302
  def insert_at_position(position)
264
- remove_from_list
265
- increment_positions_on_lower_items(position)
266
- self.update_attribute(position_column, position)
303
+ if in_list?
304
+ old_position = send(position_column).to_i
305
+ return if position == old_position
306
+ shuffle_positions_on_intermediate_items(old_position, position)
307
+ else
308
+ increment_positions_on_lower_items(position)
309
+ end
310
+ self.update_attributes!(position_column => position)
267
311
  end
268
- end
312
+
313
+ # used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint
314
+ def store_at_0
315
+ if in_list?
316
+ old_position = send(position_column).to_i
317
+ update_attributes!(position_column => 0)
318
+ decrement_positions_on_lower_items(old_position)
319
+ end
320
+ end
321
+
322
+ def update_positions
323
+ old_position = send("#{position_column}_was").to_i
324
+ new_position = send(position_column).to_i
325
+ return unless acts_as_list_class.unscoped.where("#{scope_condition} AND #{position_column} = #{new_position}").count > 1
326
+ shuffle_positions_on_intermediate_items old_position, new_position, id
327
+ end
328
+ end
269
329
  end
270
330
  end
271
331
  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.8"
5
5
  end
6
6
  end
7
7
  end
data/lib/acts_as_list.rb CHANGED
@@ -1,2 +1,24 @@
1
1
  require 'acts_as_list/active_record/acts/list'
2
- ActiveRecord::Base.class_eval { include ActiveRecord::Acts::List }
2
+
3
+ module ActsAsList
4
+ if defined? Rails::Railtie
5
+ require 'rails'
6
+ class Railtie < Rails::Railtie
7
+ initializer 'acts_as_list.insert_into_active_record' do
8
+ ActiveSupport.on_load :active_record do
9
+ ActsAsList::Railtie.insert
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ class Railtie
16
+ def self.insert
17
+ if defined?(ActiveRecord)
18
+ ActiveRecord::Base.send(:include, ActiveRecord::Acts::List)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ ActsAsList::Railtie.insert
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,8 @@
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
+ autoload :TopAddition, 'shared_top_addition'
8
+ end
@@ -0,0 +1,160 @@
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
+ (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter, :parent_id => 6, :parent_type => 'ParentClass' }
6
+ end
7
+
8
+ def test_reordering
9
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
10
+
11
+ ArrayScopeListMixin.find(2).move_lower
12
+ assert_equal [1, 3, 2, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
13
+
14
+ ArrayScopeListMixin.find(2).move_higher
15
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
16
+
17
+ ArrayScopeListMixin.find(1).move_to_bottom
18
+ assert_equal [2, 3, 4, 1], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
19
+
20
+ ArrayScopeListMixin.find(1).move_to_top
21
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
22
+
23
+ ArrayScopeListMixin.find(2).move_to_bottom
24
+ assert_equal [1, 3, 4, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
25
+
26
+ ArrayScopeListMixin.find(4).move_to_top
27
+ assert_equal [4, 1, 3, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
28
+
29
+ ArrayScopeListMixin.find(4).insert_at(4)
30
+ assert_equal [1, 3, 2, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
31
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:pos)
32
+ end
33
+
34
+ def test_move_to_bottom_with_next_to_last_item
35
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
36
+ ArrayScopeListMixin.find(3).move_to_bottom
37
+ assert_equal [1, 2, 4, 3], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
38
+ end
39
+
40
+ def test_next_prev
41
+ assert_equal ArrayScopeListMixin.find(2), ArrayScopeListMixin.find(1).lower_item
42
+ assert_nil ArrayScopeListMixin.find(1).higher_item
43
+ assert_equal ArrayScopeListMixin.find(3), ArrayScopeListMixin.find(4).higher_item
44
+ assert_nil ArrayScopeListMixin.find(4).lower_item
45
+ end
46
+
47
+ def test_injection
48
+ item = ArrayScopeListMixin.new(:parent_id => 1, :parent_type => 'ParentClass')
49
+ assert_equal "pos", item.position_column
50
+ end
51
+
52
+ def test_insert
53
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
54
+ assert_equal 1, new.pos
55
+ assert new.first?
56
+ assert new.last?
57
+
58
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
59
+ assert_equal 2, new.pos
60
+ assert !new.first?
61
+ assert new.last?
62
+
63
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
64
+ assert_equal 3, new.pos
65
+ assert !new.first?
66
+ assert new.last?
67
+
68
+ new = ArrayScopeListMixin.create(:parent_id => 0, :parent_type => 'ParentClass')
69
+ assert_equal 1, new.pos
70
+ assert new.first?
71
+ assert new.last?
72
+ end
73
+
74
+ def test_insert_at
75
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
76
+ assert_equal 1, new.pos
77
+
78
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
79
+ assert_equal 2, new.pos
80
+
81
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
82
+ assert_equal 3, new.pos
83
+
84
+ new4 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
85
+ assert_equal 4, new4.pos
86
+
87
+ new4.insert_at(3)
88
+ assert_equal 3, new4.pos
89
+
90
+ new.reload
91
+ assert_equal 4, new.pos
92
+
93
+ new.insert_at(2)
94
+ assert_equal 2, new.pos
95
+
96
+ new4.reload
97
+ assert_equal 4, new4.pos
98
+
99
+ new5 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
100
+ assert_equal 5, new5.pos
101
+
102
+ new5.insert_at(1)
103
+ assert_equal 1, new5.pos
104
+
105
+ new4.reload
106
+ assert_equal 5, new4.pos
107
+ end
108
+
109
+ def test_delete_middle
110
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
111
+
112
+ ArrayScopeListMixin.find(2).destroy
113
+
114
+ assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
115
+
116
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
117
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
118
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
119
+
120
+ ArrayScopeListMixin.find(1).destroy
121
+
122
+ assert_equal [3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
123
+
124
+ assert_equal 1, ArrayScopeListMixin.find(3).pos
125
+ assert_equal 2, ArrayScopeListMixin.find(4).pos
126
+ end
127
+
128
+ def test_remove_from_list_should_then_fail_in_list?
129
+ assert_equal true, ArrayScopeListMixin.find(1).in_list?
130
+ ArrayScopeListMixin.find(1).remove_from_list
131
+ assert_equal false, ArrayScopeListMixin.find(1).in_list?
132
+ end
133
+
134
+ def test_remove_from_list_should_set_position_to_nil
135
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
136
+
137
+ ArrayScopeListMixin.find(2).remove_from_list
138
+
139
+ assert_equal [2, 1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
140
+
141
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
142
+ assert_equal nil, ArrayScopeListMixin.find(2).pos
143
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
144
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
145
+ end
146
+
147
+ def test_remove_before_destroy_does_not_shift_lower_items_twice
148
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
149
+
150
+ ArrayScopeListMixin.find(2).remove_from_list
151
+ ArrayScopeListMixin.find(2).destroy
152
+
153
+ assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
154
+
155
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
156
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
157
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
158
+ end
159
+ end
160
+ end