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 +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -0
- data/README.md +60 -0
- data/Rakefile +1 -3
- data/acts_as_list.gemspec +1 -1
- data/init.rb +2 -0
- data/lib/acts_as_list/active_record/acts/list.rb +89 -29
- data/lib/acts_as_list/version.rb +1 -1
- data/lib/acts_as_list.rb +23 -1
- data/test/helper.rb +2 -0
- data/test/shared.rb +8 -0
- data/test/shared_array_scope_list.rb +160 -0
- data/test/shared_list.rb +222 -0
- data/test/shared_list_sub.rb +102 -0
- data/test/shared_top_addition.rb +87 -0
- data/test/shared_zero_based.rb +86 -0
- data/test/test_list.rb +198 -478
- metadata +69 -91
- data/README.rdoc +0 -35
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
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
|
+
[](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 =
|
|
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", ["
|
|
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
|
@@ -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
|
|
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
|
-
|
|
78
|
-
before_create
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
!
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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
|
data/lib/acts_as_list/version.rb
CHANGED
data/lib/acts_as_list.rb
CHANGED
|
@@ -1,2 +1,24 @@
|
|
|
1
1
|
require 'acts_as_list/active_record/acts/list'
|
|
2
|
-
|
|
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
data/test/shared.rb
ADDED
|
@@ -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
|