sortifiable 0.2.0 → 0.2.1

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/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ *0.2.1 (April 20th, 2011)
2
+
3
+ * Add checking for scope changes so items are moved between lists correctly [Manuel Meurer]
4
+
5
+
1
6
  *0.2.0 (April 7th, 2011)
2
7
 
3
8
  * Use pessimistic locking to reduce concurrency problems
data/README CHANGED
@@ -24,4 +24,17 @@ the scope methods and query interface introduced with Ruby on Rails 3.0
24
24
  todo_list.last.move_higher
25
25
 
26
26
 
27
+ === Contributions
28
+
29
+ Bug fixes and new feature patches are welcome. Please provide tests and
30
+ documentation wherever possible - without them it is unlikely your patch
31
+ will be accepted. If you're fixing a bug then a failing test for the bug
32
+ is essential. Once you have completed your patch please open a GitHub
33
+ pull request and I will review it and respond as quickly as possible.
34
+
35
+ Thanks to the following people for their contributions:
36
+
37
+ * Manuel Meurer
38
+
39
+
27
40
  Copyright (c) 2011 Andrew White, released under the MIT license
@@ -1,3 +1,3 @@
1
1
  module Sortifiable
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/lib/sortifiable.rb CHANGED
@@ -48,30 +48,35 @@ module Sortifiable
48
48
  def acts_as_list(options = {})
49
49
  options.reverse_merge!(:scope => [], :column => :position)
50
50
 
51
- if options[:scope].is_a?(Symbol) && reflections.key?(options[:scope])
52
- reflection = reflections[options.delete(:scope)]
53
-
54
- if reflection.belongs_to?
55
- if reflection.options[:polymorphic]
56
- options[:scope] = [
57
- reflection.association_foreign_key.to_sym,
58
- reflection.options[:foreign_type].to_sym
59
- ]
51
+ if options[:scope].is_a?(Symbol)
52
+ if reflections.key?(options[:scope])
53
+ reflection = reflections[options.delete(:scope)]
54
+
55
+ if reflection.belongs_to?
56
+ if reflection.options[:polymorphic]
57
+ options[:scope] = [
58
+ reflection.association_foreign_key.to_sym,
59
+ reflection.options[:foreign_type].to_sym
60
+ ]
61
+ else
62
+ options[:scope] = reflection.association_foreign_key.to_sym
63
+ end
60
64
  else
61
- options[:scope] = reflection.association_foreign_key.to_sym
65
+ raise ArgumentError, "Only belongs_to associations can be used as a scope"
62
66
  end
63
- else
64
- raise ArgumentError, "Only belongs_to associations can be used as a scope"
67
+ elsif options[:scope].to_s !~ /_id$/
68
+ options[:scope] = "#{options[:scope]}_id".to_sym
65
69
  end
66
- elsif options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
67
- options[:scope] = "#{options[:scope]}_id".to_sym
68
70
  end
69
71
 
70
72
  options[:class] = self
71
73
 
72
74
  include InstanceMethods
73
- before_create :add_to_list
74
- before_destroy :decrement_position_on_lower_items, :if => :in_list?
75
+
76
+ before_create :add_to_list
77
+ before_destroy :decrement_position_on_lower_items, :if => :in_list?
78
+ before_save :decrement_position_on_lower_items_in_old_list, :if => :will_leave_list?
79
+ before_save :add_to_bottom_of_new_list, :if => :will_leave_list?
75
80
 
76
81
  self.acts_as_list_options = options
77
82
  end
@@ -92,9 +97,9 @@ module Sortifiable
92
97
  ids = lock_list!
93
98
  last_position = ids.size
94
99
  if persisted?
95
- update_position(last_position + 1)
100
+ update_position last_position + 1
96
101
  else
97
- send("#{position_column}=".to_sym, last_position + 1)
102
+ set_position last_position + 1
98
103
  end
99
104
  end
100
105
  end
@@ -138,6 +143,11 @@ module Sortifiable
138
143
  !new_record? && !send(position_column).nil?
139
144
  end
140
145
 
146
+ # Test if this record is in a scoped list but will leave the scoped list when saved
147
+ def will_leave_list?
148
+ in_list? && scope_parts.any? { |scope_part| send("#{scope_part}_changed?") }
149
+ end
150
+
141
151
  # Increase the position of this item without adjusting the rest of the list.
142
152
  def increment_position
143
153
  in_list? && update_position(current_position + 1)
@@ -200,8 +210,7 @@ module Sortifiable
200
210
 
201
211
  # Returns the bottom position in the list.
202
212
  def last_position
203
- item = last_item
204
- item ? item.current_position : 0
213
+ last_item.try(:current_position) || 0
205
214
  end
206
215
  alias_method :bottom_position, :last_position
207
216
 
@@ -232,7 +241,7 @@ module Sortifiable
232
241
  END
233
242
  SQL
234
243
 
235
- send("#{position_column}=".to_sym, current_position - 1)
244
+ set_position current_position - 1
236
245
  list_scope.update_all(sql) > 0
237
246
  end
238
247
  end
@@ -258,7 +267,7 @@ module Sortifiable
258
267
  END
259
268
  SQL
260
269
 
261
- send("#{position_column}=".to_sym, current_position + 1)
270
+ set_position current_position + 1
262
271
  list_scope.update_all(sql) > 0
263
272
  end
264
273
  end
@@ -285,7 +294,7 @@ module Sortifiable
285
294
  END
286
295
  SQL
287
296
 
288
- send("#{position_column}=".to_sym, last_position)
297
+ set_position last_position
289
298
  list_scope.update_all(sql) > 0
290
299
  end
291
300
  end
@@ -311,7 +320,7 @@ module Sortifiable
311
320
  END
312
321
  SQL
313
322
 
314
- send("#{position_column}=".to_sym, 1)
323
+ set_position 1
315
324
  list_scope.update_all(sql) > 0
316
325
  end
317
326
  end
@@ -335,7 +344,7 @@ module Sortifiable
335
344
  END
336
345
  SQL
337
346
 
338
- send("#{position_column}=".to_sym, nil)
347
+ set_position nil
339
348
  list_scope.update_all(sql) > 0
340
349
  end
341
350
  else
@@ -354,6 +363,46 @@ module Sortifiable
354
363
  list_scope.update_all(update, conditions) > 0
355
364
  end
356
365
 
366
+ def decrement_position_on_lower_items_in_old_list #:nodoc:
367
+ with_old_scope do
368
+ decrement_position_on_lower_items
369
+ end
370
+ end
371
+
372
+ def with_old_scope #:nodoc:
373
+ # Save new scope in variable
374
+ new_scope = scope_parts.map { |scope_part| send(scope_part) }
375
+
376
+ # Set old scope
377
+ scope_parts.each { |scope_part| send "#{scope_part}=", send("#{scope_part}_was") }
378
+
379
+ yield
380
+
381
+ # Set new scope
382
+ scope_parts.each_with_index { |scope_part, i| send "#{scope_part}=", new_scope[i] }
383
+ end
384
+
385
+ def scope_parts_from_string(string) #:nodoc:
386
+ string.split('AND').map(&:strip).map { |condition| condition.split.first }
387
+ end
388
+
389
+ def scope_parts #:nodoc:
390
+ if acts_as_list_options[:scope].is_a?(String)
391
+ scope_parts_from_string(acts_as_list_options[:scope])
392
+ else
393
+ Array(acts_as_list_options[:scope])
394
+ end
395
+ end
396
+
397
+ def add_to_bottom_of_new_list #:nodoc:
398
+ set_position nil
399
+ add_to_list
400
+ end
401
+
402
+ def set_position(position) #:nodoc:
403
+ send "#{position_column}=", position
404
+ end
405
+
357
406
  def list_class #:nodoc:
358
407
  acts_as_list_options[:class]
359
408
  end
@@ -384,15 +433,15 @@ module Sortifiable
384
433
 
385
434
  def scope_condition #:nodoc:
386
435
  if acts_as_list_options[:scope].is_a?(String)
387
- instance_eval("\"#{acts_as_list_options[:scope]}\"")
436
+ instance_eval %("#{acts_as_list_options[:scope]}")
388
437
  else
389
- Array.wrap(acts_as_list_options[:scope]).inject({}){ |m,k| m[k] = send(k); m }
438
+ Array(acts_as_list_options[:scope]).each_with_object({}) { |k, m| m[k] = send(k) }
390
439
  end
391
440
  end
392
441
 
393
- def update_position(new_position)
394
- list_class.update_all(["#{quoted_position_column} = ?", new_position], list_class.primary_key => id)
395
- send("#{position_column}=".to_sym, new_position)
442
+ def update_position(new_position) #:nodoc:
443
+ list_class.update_all({ position_column => new_position }, { list_class.primary_key => id })
444
+ set_position new_position
396
445
  end
397
446
  end
398
447
  end
@@ -47,6 +47,7 @@ end
47
47
  class ListWithStringScopeMixin < ActiveRecord::Base
48
48
  acts_as_list :column => "pos", :scope => 'parent_id = #{parent_id}'
49
49
  set_table_name "mixins"
50
+ default_scope order(:pos)
50
51
  end
51
52
 
52
53
  class ArrayScopeListMixin < ActiveRecord::Base
@@ -100,7 +101,11 @@ class ListTest < Test::Unit::TestCase
100
101
 
101
102
  def setup
102
103
  setup_db
103
- (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 5 }
104
+ [5, 6].each do |parent_id|
105
+ (1..4).each do |i|
106
+ ListMixin.create! :pos => i, :parent_id => parent_id
107
+ end
108
+ end
104
109
  end
105
110
 
106
111
  def teardown
@@ -108,41 +113,42 @@ class ListTest < Test::Unit::TestCase
108
113
  end
109
114
 
110
115
  def test_reordering
111
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
116
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
112
117
 
113
118
  ListMixin.find(2).move_lower
114
- assert_equal [1, 3, 2, 4], ListMixin.where('parent_id = 5').map(&:id)
119
+ assert_equal [1, 3, 2, 4], ListMixin.where(:parent_id => 5).map(&:id)
115
120
 
116
121
  ListMixin.find(2).move_higher
117
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
122
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
118
123
 
119
124
  ListMixin.find(1).move_to_bottom
120
- assert_equal [2, 3, 4, 1], ListMixin.where('parent_id = 5').map(&:id)
125
+ assert_equal [2, 3, 4, 1], ListMixin.where(:parent_id => 5).map(&:id)
121
126
 
122
127
  ListMixin.find(1).move_to_top
123
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
128
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
124
129
 
125
130
  ListMixin.find(2).move_to_bottom
126
- assert_equal [1, 3, 4, 2], ListMixin.where('parent_id = 5').map(&:id)
131
+ assert_equal [1, 3, 4, 2], ListMixin.where(:parent_id => 5).map(&:id)
127
132
 
128
133
  ListMixin.find(4).move_to_top
129
- assert_equal [4, 1, 3, 2], ListMixin.where('parent_id = 5').map(&:id)
134
+ assert_equal [4, 1, 3, 2], ListMixin.where(:parent_id => 5).map(&:id)
130
135
  end
131
136
 
132
137
  def test_bounds_checking
133
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:pos)
138
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:pos)
134
139
 
135
140
  ListMixin.find(1).move_higher
136
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:pos)
141
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:pos)
137
142
 
138
143
  ListMixin.find(4).move_lower
139
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:pos)
144
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:pos)
140
145
  end
141
146
 
142
147
  def test_move_to_bottom_with_next_to_last_item
143
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
148
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
149
+
144
150
  ListMixin.find(3).move_to_bottom
145
- assert_equal [1, 2, 4, 3], ListMixin.where('parent_id = 5').map(&:id)
151
+ assert_equal [1, 2, 4, 3], ListMixin.where(:parent_id => 5).map(&:id)
146
152
  end
147
153
 
148
154
  def test_next_prev
@@ -216,11 +222,11 @@ class ListTest < Test::Unit::TestCase
216
222
  end
217
223
 
218
224
  def test_delete_middle
219
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
225
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
220
226
 
221
227
  ListMixin.find(2).destroy
222
228
 
223
- assert_equal [1, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
229
+ assert_equal [1, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
224
230
 
225
231
  assert_equal 1, ListMixin.find(1).pos
226
232
  assert_equal 2, ListMixin.find(3).pos
@@ -228,23 +234,16 @@ class ListTest < Test::Unit::TestCase
228
234
 
229
235
  ListMixin.find(1).destroy
230
236
 
231
- assert_equal [3, 4], ListMixin.where('parent_id = 5').map(&:id)
237
+ assert_equal [3, 4], ListMixin.where(:parent_id => 5).map(&:id)
232
238
 
233
239
  assert_equal 1, ListMixin.find(3).pos
234
240
  assert_equal 2, ListMixin.find(4).pos
235
241
  end
236
242
 
237
- def test_with_string_based_scope
238
- new = ListWithStringScopeMixin.create(:parent_id => 500)
239
- assert_equal 1, new.pos
240
- assert new.first?
241
- assert new.last?
242
- end
243
-
244
243
  def test_nil_scope
245
244
  new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
246
245
  new2.move_higher
247
- assert_equal [new2, new1, new3], ListMixin.where('parent_id IS NULL')
246
+ assert_equal [new2, new1, new3], ListMixin.where(:parent_id => nil)
248
247
  end
249
248
 
250
249
  def test_remove_from_list_should_then_fail_in_list?
@@ -254,11 +253,11 @@ class ListTest < Test::Unit::TestCase
254
253
  end
255
254
 
256
255
  def test_remove_from_list_should_set_position_to_nil
257
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
256
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
258
257
 
259
258
  ListMixin.find(2).remove_from_list
260
259
 
261
- assert_equal [2, 1, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
260
+ assert_equal [2, 1, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
262
261
 
263
262
  assert_equal 1, ListMixin.find(1).pos
264
263
  assert_equal nil, ListMixin.find(2).pos
@@ -267,20 +266,47 @@ class ListTest < Test::Unit::TestCase
267
266
  end
268
267
 
269
268
  def test_remove_before_destroy_does_not_shift_lower_items_twice
270
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
269
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
271
270
 
272
271
  ListMixin.find(2).remove_from_list
273
272
  ListMixin.find(2).destroy
274
273
 
275
- assert_equal [1, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
274
+ assert_equal [1, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
275
+
276
+ assert_equal 1, ListMixin.find(1).pos
277
+ assert_equal 2, ListMixin.find(3).pos
278
+ assert_equal 3, ListMixin.find(4).pos
279
+ end
280
+
281
+ def test_remove_from_list_by_updating_should_shift_lower_items
282
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
283
+
284
+ ListMixin.find(2).update_attributes! :parent_id => 6
285
+
286
+ assert_equal [1, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
276
287
 
277
288
  assert_equal 1, ListMixin.find(1).pos
278
289
  assert_equal 2, ListMixin.find(3).pos
279
290
  assert_equal 3, ListMixin.find(4).pos
280
291
  end
281
292
 
293
+ def test_move_to_new_list_by_updating_should_append
294
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
295
+ assert_equal [5, 6, 7, 8], ListMixin.where(:parent_id => 6).map(&:id)
296
+
297
+ ListMixin.find(2).update_attributes! :parent_id => 6
298
+
299
+ assert_equal [5, 6, 7, 8, 2], ListMixin.where(:parent_id => 6).map(&:id)
300
+
301
+ assert_equal 1, ListMixin.find(5).pos
302
+ assert_equal 2, ListMixin.find(6).pos
303
+ assert_equal 3, ListMixin.find(7).pos
304
+ assert_equal 4, ListMixin.find(8).pos
305
+ assert_equal 5, ListMixin.find(2).pos
306
+ end
307
+
282
308
  def test_before_destroy_callbacks_do_not_update_position_to_nil_before_deleting_the_record
283
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
309
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
284
310
 
285
311
  # We need to trigger all the before_destroy callbacks without actually
286
312
  # destroying the record so we can see the affect the callbacks have on
@@ -292,7 +318,7 @@ class ListTest < Test::Unit::TestCase
292
318
  list.send(:callback, :before_destroy)
293
319
  end
294
320
 
295
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5').map(&:id)
321
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5).map(&:id)
296
322
 
297
323
  assert_equal 1, ListMixin.find(1).pos
298
324
  assert_equal 2, ListMixin.find(2).pos
@@ -302,12 +328,78 @@ class ListTest < Test::Unit::TestCase
302
328
 
303
329
  def test_higher_items
304
330
  assert_equal [1, 2], ListMixin.find(3).higher_items.map(&:id)
305
- assert_equal [], ListMixin.find(1).higher_items.map(&:id)
331
+ assert_equal [], ListMixin.find(1).higher_items.map(&:id)
306
332
  end
307
333
 
308
334
  def test_lower_items
309
335
  assert_equal [3, 4], ListMixin.find(2).lower_items.map(&:id)
310
- assert_equal [], ListMixin.find(4).lower_items.map(&:id)
336
+ assert_equal [], ListMixin.find(4).lower_items.map(&:id)
337
+ end
338
+
339
+ end
340
+
341
+ class ListWithStringScopeTest < Test::Unit::TestCase
342
+
343
+ def setup
344
+ setup_db
345
+ [5, 6].each do |parent_id|
346
+ (1..4).each do |i|
347
+ ListWithStringScopeMixin.create! :parent_id => parent_id
348
+ end
349
+ end
350
+ end
351
+
352
+ def teardown
353
+ teardown_db
354
+ end
355
+
356
+ def test_insert
357
+ new = ListWithStringScopeMixin.create(:parent_id => 500)
358
+ assert_equal 1, new.pos
359
+ assert new.first?
360
+ assert new.last?
361
+
362
+ new = ListWithStringScopeMixin.create(:parent_id => 500)
363
+ assert_equal 2, new.pos
364
+ assert !new.first?
365
+ assert new.last?
366
+
367
+ new = ListWithStringScopeMixin.create(:parent_id => 500)
368
+ assert_equal 3, new.pos
369
+ assert !new.first?
370
+ assert new.last?
371
+
372
+ new = ListWithStringScopeMixin.create(:parent_id => 0)
373
+ assert_equal 1, new.pos
374
+ assert new.first?
375
+ assert new.last?
376
+ end
377
+
378
+ def test_remove_from_list_by_updating_should_shift_lower_items
379
+ assert_equal [1, 2, 3, 4], ListWithStringScopeMixin.where(:parent_id => 5).map(&:id)
380
+
381
+ ListWithStringScopeMixin.find(2).update_attributes! :parent_id => 6
382
+
383
+ assert_equal [1, 3, 4], ListWithStringScopeMixin.where(:parent_id => 5).map(&:id)
384
+
385
+ assert_equal 1, ListWithStringScopeMixin.find(1).pos
386
+ assert_equal 2, ListWithStringScopeMixin.find(3).pos
387
+ assert_equal 3, ListWithStringScopeMixin.find(4).pos
388
+ end
389
+
390
+ def test_move_to_new_list_by_updating_should_append
391
+ assert_equal [1, 2, 3, 4], ListWithStringScopeMixin.where(:parent_id => 5).map(&:id)
392
+ assert_equal [5, 6, 7, 8], ListWithStringScopeMixin.where(:parent_id => 6).map(&:id)
393
+
394
+ ListWithStringScopeMixin.find(2).update_attributes! :parent_id => 6
395
+
396
+ assert_equal [5, 6, 7, 8, 2], ListWithStringScopeMixin.where(:parent_id => 6).map(&:id)
397
+
398
+ assert_equal 1, ListWithStringScopeMixin.find(5).pos
399
+ assert_equal 2, ListWithStringScopeMixin.find(6).pos
400
+ assert_equal 3, ListWithStringScopeMixin.find(7).pos
401
+ assert_equal 4, ListWithStringScopeMixin.find(8).pos
402
+ assert_equal 5, ListWithStringScopeMixin.find(2).pos
311
403
  end
312
404
 
313
405
  end
@@ -334,41 +426,41 @@ class ListSubTest < Test::Unit::TestCase
334
426
  end
335
427
 
336
428
  def test_reordering
337
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:id)
429
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
338
430
 
339
431
  ListMixin.find(2).move_lower
340
- assert_equal [1, 3, 2, 4], ListMixin.where('parent_id = 5000').map(&:id)
432
+ assert_equal [1, 3, 2, 4], ListMixin.where(:parent_id => 5000).map(&:id)
341
433
 
342
434
  ListMixin.find(2).move_higher
343
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:id)
435
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
344
436
 
345
437
  ListMixin.find(1).move_to_bottom
346
- assert_equal [2, 3, 4, 1], ListMixin.where('parent_id = 5000').map(&:id)
438
+ assert_equal [2, 3, 4, 1], ListMixin.where(:parent_id => 5000).map(&:id)
347
439
 
348
440
  ListMixin.find(1).move_to_top
349
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:id)
441
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
350
442
 
351
443
  ListMixin.find(2).move_to_bottom
352
- assert_equal [1, 3, 4, 2], ListMixin.where('parent_id = 5000').map(&:id)
444
+ assert_equal [1, 3, 4, 2], ListMixin.where(:parent_id => 5000).map(&:id)
353
445
 
354
446
  ListMixin.find(4).move_to_top
355
- assert_equal [4, 1, 3, 2], ListMixin.where('parent_id = 5000').map(&:id)
447
+ assert_equal [4, 1, 3, 2], ListMixin.where(:parent_id => 5000).map(&:id)
356
448
  end
357
449
 
358
450
  def test_bounds_checking
359
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:pos)
451
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:pos)
360
452
 
361
453
  ListMixin.find(1).move_higher
362
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:pos)
454
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:pos)
363
455
 
364
456
  ListMixin.find(4).move_lower
365
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:pos)
457
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:pos)
366
458
  end
367
459
 
368
460
  def test_move_to_bottom_with_next_to_last_item
369
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:id)
461
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
370
462
  ListMixin.find(3).move_to_bottom
371
- assert_equal [1, 2, 4, 3], ListMixin.where('parent_id = 5000').map(&:id)
463
+ assert_equal [1, 2, 4, 3], ListMixin.where(:parent_id => 5000).map(&:id)
372
464
  end
373
465
 
374
466
  def test_next_prev
@@ -379,22 +471,22 @@ class ListSubTest < Test::Unit::TestCase
379
471
  end
380
472
 
381
473
  def test_injection
382
- item = ListMixin.new("parent_id"=>1)
474
+ item = ListMixin.new(:parent_id => 1)
383
475
  assert_equal({ :parent_id => 1 }, item.send(:scope_condition))
384
476
  assert_equal("pos", item.send(:position_column))
385
477
  end
386
478
 
387
479
  def test_insert_at
388
- new = ListMixin.create("parent_id" => 20)
480
+ new = ListMixin.create(:parent_id => 20)
389
481
  assert_equal 1, new.pos
390
482
 
391
- new = ListMixinSub1.create("parent_id" => 20)
483
+ new = ListMixinSub1.create(:parent_id => 20)
392
484
  assert_equal 2, new.pos
393
485
 
394
- new = ListMixinSub2.create("parent_id" => 20)
486
+ new = ListMixinSub2.create(:parent_id => 20)
395
487
  assert_equal 3, new.pos
396
488
 
397
- new4 = ListMixin.create("parent_id" => 20)
489
+ new4 = ListMixin.create(:parent_id => 20)
398
490
  assert_equal 4, new4.pos
399
491
 
400
492
  new4.insert_at(3)
@@ -409,7 +501,7 @@ class ListSubTest < Test::Unit::TestCase
409
501
  new4.reload
410
502
  assert_equal 4, new4.pos
411
503
 
412
- new5 = ListMixinSub1.create("parent_id" => 20)
504
+ new5 = ListMixinSub1.create(:parent_id => 20)
413
505
  assert_equal 5, new5.pos
414
506
 
415
507
  new5.insert_at(1)
@@ -420,11 +512,11 @@ class ListSubTest < Test::Unit::TestCase
420
512
  end
421
513
 
422
514
  def test_delete_middle
423
- assert_equal [1, 2, 3, 4], ListMixin.where('parent_id = 5000').map(&:id)
515
+ assert_equal [1, 2, 3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
424
516
 
425
517
  ListMixin.find(2).destroy
426
518
 
427
- assert_equal [1, 3, 4], ListMixin.where('parent_id = 5000').map(&:id)
519
+ assert_equal [1, 3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
428
520
 
429
521
  assert_equal 1, ListMixin.find(1).pos
430
522
  assert_equal 2, ListMixin.find(3).pos
@@ -432,7 +524,7 @@ class ListSubTest < Test::Unit::TestCase
432
524
 
433
525
  ListMixin.find(1).destroy
434
526
 
435
- assert_equal [3, 4], ListMixin.where('parent_id = 5000').map(&:id)
527
+ assert_equal [3, 4], ListMixin.where(:parent_id => 5000).map(&:id)
436
528
 
437
529
  assert_equal 1, ListMixin.find(3).pos
438
530
  assert_equal 2, ListMixin.find(4).pos
@@ -441,13 +533,13 @@ class ListSubTest < Test::Unit::TestCase
441
533
  def test_higher_items
442
534
  ListMixin.find(2).remove_from_list
443
535
  assert_equal [1], ListMixin.find(3).higher_items.map(&:id)
444
- assert_equal [], ListMixin.find(1).higher_items.map(&:id)
536
+ assert_equal [], ListMixin.find(1).higher_items.map(&:id)
445
537
  end
446
538
 
447
539
  def test_lower_items
448
540
  ListMixin.find(3).remove_from_list
449
541
  assert_equal [4], ListMixin.find(2).lower_items.map(&:id)
450
- assert_equal [], ListMixin.find(4).lower_items.map(&:id)
542
+ assert_equal [], ListMixin.find(4).lower_items.map(&:id)
451
543
  end
452
544
 
453
545
  def test_list_class
@@ -460,12 +552,16 @@ class ArrayScopeListTest < Test::Unit::TestCase
460
552
 
461
553
  def setup
462
554
  setup_db
463
- (1..4).each do |counter|
464
- ArrayScopeListMixin.create!(
465
- :pos => counter,
466
- :parent_id => 5,
467
- :parent_type => 'ParentClass'
468
- )
555
+ ['ParentClass', 'bananas'].each do |parent_type|
556
+ [5, 6].each do |parent_id|
557
+ (1..4).each do |i|
558
+ ArrayScopeListMixin.create!(
559
+ :pos => i,
560
+ :parent_id => parent_id,
561
+ :parent_type => parent_type
562
+ )
563
+ end
564
+ end
469
565
  end
470
566
  end
471
567
 
@@ -524,8 +620,8 @@ class ArrayScopeListTest < Test::Unit::TestCase
524
620
 
525
621
  def test_injection
526
622
  item = ArrayScopeListMixin.new(conditions)
527
- assert_equal(conditions, item.send(:scope_condition))
528
- assert_equal("pos", item.send(:position_column))
623
+ assert_equal conditions, item.send(:scope_condition)
624
+ assert_equal "pos", item.send(:position_column)
529
625
  end
530
626
 
531
627
  def test_insert
@@ -604,9 +700,92 @@ class ArrayScopeListTest < Test::Unit::TestCase
604
700
  assert_equal 2, ArrayScopeListMixin.find(4).pos
605
701
  end
606
702
 
703
+ def test_remove_from_list_by_updating_scope_part_1_should_shift_lower_items
704
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
705
+
706
+ ArrayScopeListMixin.find(2).update_attributes! :parent_id => 6
707
+
708
+ assert_equal [1, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
709
+
710
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
711
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
712
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
713
+ end
714
+
715
+ def test_remove_from_list_by_updating_scope_part_2_should_shift_lower_items
716
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
717
+
718
+ ArrayScopeListMixin.find(2).update_attributes! :parent_type => 'bananas'
719
+
720
+ assert_equal [1, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
721
+
722
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
723
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
724
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
725
+ end
726
+
727
+ def test_remove_from_list_by_updating_complete_scope_should_shift_lower_items
728
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
729
+
730
+ ArrayScopeListMixin.find(2).update_attributes! :parent_id => 6, :parent_type => 'bananas'
731
+
732
+ assert_equal [1, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
733
+
734
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
735
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
736
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
737
+ end
738
+
739
+ def test_move_to_new_list_by_updating_scope_part_1_should_append
740
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
741
+ assert_equal [5, 6, 7, 8], ArrayScopeListMixin.where(conditions(:parent_id => 6)).map(&:id)
742
+
743
+ ArrayScopeListMixin.find(2).update_attributes! :parent_id => 6
744
+
745
+ assert_equal [5, 6, 7, 8, 2], ArrayScopeListMixin.where(conditions(:parent_id => 6)).map(&:id)
746
+
747
+ assert_equal 1, ArrayScopeListMixin.find(5).pos
748
+ assert_equal 2, ArrayScopeListMixin.find(6).pos
749
+ assert_equal 3, ArrayScopeListMixin.find(7).pos
750
+ assert_equal 4, ArrayScopeListMixin.find(8).pos
751
+ assert_equal 5, ArrayScopeListMixin.find(2).pos
752
+ end
753
+
754
+ def test_move_to_new_list_by_updating_scope_part_2_should_append
755
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
756
+ assert_equal [9, 10, 11, 12], ArrayScopeListMixin.where(conditions(:parent_type => 'bananas')).map(&:id)
757
+
758
+ ArrayScopeListMixin.find(2).update_attributes! :parent_type => 'bananas'
759
+
760
+ assert_equal [9, 10, 11, 12, 2], ArrayScopeListMixin.where(conditions(:parent_type => 'bananas')).map(&:id)
761
+
762
+ assert_equal 1, ArrayScopeListMixin.find(9).pos
763
+ assert_equal 2, ArrayScopeListMixin.find(10).pos
764
+ assert_equal 3, ArrayScopeListMixin.find(11).pos
765
+ assert_equal 4, ArrayScopeListMixin.find(12).pos
766
+ assert_equal 5, ArrayScopeListMixin.find(2).pos
767
+ end
768
+
769
+ def test_move_to_new_list_by_updating_complete_scope_should_append
770
+ assert_equal [ 1, 2, 3, 4], ArrayScopeListMixin.where(conditions).map(&:id)
771
+ assert_equal [13, 14, 15, 16], ArrayScopeListMixin.where(:parent_id => 6, :parent_type => 'bananas').map(&:id)
772
+
773
+ ArrayScopeListMixin.find(2).update_attributes! :parent_id => 6, :parent_type => 'bananas'
774
+
775
+ assert_equal [13, 14, 15, 16, 2], ArrayScopeListMixin.where(:parent_id => 6, :parent_type => 'bananas').map(&:id)
776
+
777
+ assert_equal 1, ArrayScopeListMixin.find(13).pos
778
+ assert_equal 2, ArrayScopeListMixin.find(14).pos
779
+ assert_equal 3, ArrayScopeListMixin.find(15).pos
780
+ assert_equal 4, ArrayScopeListMixin.find(16).pos
781
+ assert_equal 5, ArrayScopeListMixin.find(2).pos
782
+ end
783
+
607
784
  def test_remove_from_list_should_then_fail_in_list?
608
785
  assert_equal true, ArrayScopeListMixin.find(1).in_list?
786
+
609
787
  ArrayScopeListMixin.find(1).remove_from_list
788
+
610
789
  assert_equal false, ArrayScopeListMixin.find(1).in_list?
611
790
  end
612
791
 
@@ -638,12 +817,12 @@ class ArrayScopeListTest < Test::Unit::TestCase
638
817
 
639
818
  def test_higher_items
640
819
  assert_equal [1, 2], ArrayScopeListMixin.find(3).higher_items.map(&:id)
641
- assert_equal [], ArrayScopeListMixin.find(1).higher_items.map(&:id)
820
+ assert_equal [], ArrayScopeListMixin.find(1).higher_items.map(&:id)
642
821
  end
643
822
 
644
823
  def test_lower_items
645
824
  assert_equal [3, 4], ArrayScopeListMixin.find(2).lower_items.map(&:id)
646
- assert_equal [], ArrayScopeListMixin.find(4).lower_items.map(&:id)
825
+ assert_equal [], ArrayScopeListMixin.find(4).lower_items.map(&:id)
647
826
  end
648
827
 
649
828
  end
@@ -652,16 +831,16 @@ class AssociationScopeListTest < Test::Unit::TestCase
652
831
 
653
832
  def setup
654
833
  setup_db
655
- (1..4).each do |counter|
834
+ (1..4).each do |i|
656
835
  AssociationScopeListMixin.create!(
657
- :pos => counter,
836
+ :pos => i,
658
837
  :parent_id => 5
659
838
  )
660
839
  end
661
840
 
662
- (1..4).each do |counter|
841
+ (1..4).each do |i|
663
842
  PolymorphicAssociationScopeListMixin.create!(
664
- :pos => counter,
843
+ :pos => i,
665
844
  :parent_id => 5,
666
845
  :parent_type => 'ParentClass'
667
846
  )
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sortifiable
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Andrew White
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-07 00:00:00 +01:00
18
+ date: 2011-04-20 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency