acts_as_list 0.1.8 → 0.1.9
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/lib/acts_as_list/active_record/acts/list.rb +29 -9
- data/lib/acts_as_list/version.rb +1 -1
- data/test/shared_list.rb +11 -3
- data/test/shared_list_sub.rb +5 -1
- data/test/test_list.rb +115 -9
- metadata +10 -10
@@ -75,6 +75,14 @@ module ActiveRecord
|
|
75
75
|
|
76
76
|
#{scope_condition_method}
|
77
77
|
|
78
|
+
# only add to attr_accessible
|
79
|
+
# if the class has some mass_assignment_protection
|
80
|
+
|
81
|
+
unless accessible_attributes.blank?
|
82
|
+
attr_accessible :#{configuration[:column]}
|
83
|
+
end
|
84
|
+
|
85
|
+
before_destroy :reload_position
|
78
86
|
after_destroy :decrement_positions_on_lower_items
|
79
87
|
before_create :add_to_list_#{configuration[:add_new_at]}
|
80
88
|
after_update :update_positions
|
@@ -136,20 +144,20 @@ module ActiveRecord
|
|
136
144
|
def remove_from_list
|
137
145
|
if in_list?
|
138
146
|
decrement_positions_on_lower_items
|
139
|
-
|
147
|
+
set_list_position(nil)
|
140
148
|
end
|
141
149
|
end
|
142
150
|
|
143
151
|
# Increase the position of this item without adjusting the rest of the list.
|
144
152
|
def increment_position
|
145
153
|
return unless in_list?
|
146
|
-
|
154
|
+
set_list_position(self.send(position_column).to_i + 1)
|
147
155
|
end
|
148
156
|
|
149
157
|
# Decrease the position of this item without adjusting the rest of the list.
|
150
158
|
def decrement_position
|
151
159
|
return unless in_list?
|
152
|
-
|
160
|
+
set_list_position(self.send(position_column).to_i - 1)
|
153
161
|
end
|
154
162
|
|
155
163
|
# Return +true+ if this object is the first in the list.
|
@@ -168,7 +176,8 @@ module ActiveRecord
|
|
168
176
|
def higher_item
|
169
177
|
return nil unless in_list?
|
170
178
|
acts_as_list_class.unscoped.find(:first, :conditions =>
|
171
|
-
"#{scope_condition} AND #{position_column}
|
179
|
+
"#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}",
|
180
|
+
:order => "#{acts_as_list_class.table_name}.#{position_column} DESC"
|
172
181
|
)
|
173
182
|
end
|
174
183
|
|
@@ -176,7 +185,8 @@ module ActiveRecord
|
|
176
185
|
def lower_item
|
177
186
|
return nil unless in_list?
|
178
187
|
acts_as_list_class.unscoped.find(:first, :conditions =>
|
179
|
-
"#{scope_condition} AND #{position_column}
|
188
|
+
"#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}",
|
189
|
+
:order => "#{acts_as_list_class.table_name}.#{position_column} ASC"
|
180
190
|
)
|
181
191
|
end
|
182
192
|
|
@@ -197,6 +207,12 @@ module ActiveRecord
|
|
197
207
|
default_position == send(position_column)
|
198
208
|
end
|
199
209
|
|
210
|
+
# Sets the new position and saves it
|
211
|
+
def set_list_position(new_position)
|
212
|
+
send("#{position_column}=", new_position)
|
213
|
+
save!
|
214
|
+
end
|
215
|
+
|
200
216
|
private
|
201
217
|
def add_to_list_top
|
202
218
|
increment_positions_on_all_items
|
@@ -230,12 +246,12 @@ module ActiveRecord
|
|
230
246
|
|
231
247
|
# Forces item to assume the bottom position in the list.
|
232
248
|
def assume_bottom_position
|
233
|
-
|
249
|
+
set_list_position(bottom_position_in_list(self).to_i + 1)
|
234
250
|
end
|
235
251
|
|
236
252
|
# Forces item to assume the top position in the list.
|
237
253
|
def assume_top_position
|
238
|
-
|
254
|
+
set_list_position(acts_as_list_top)
|
239
255
|
end
|
240
256
|
|
241
257
|
# This has the effect of moving all the higher items up one.
|
@@ -307,14 +323,14 @@ module ActiveRecord
|
|
307
323
|
else
|
308
324
|
increment_positions_on_lower_items(position)
|
309
325
|
end
|
310
|
-
|
326
|
+
set_list_position(position)
|
311
327
|
end
|
312
328
|
|
313
329
|
# used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint
|
314
330
|
def store_at_0
|
315
331
|
if in_list?
|
316
332
|
old_position = send(position_column).to_i
|
317
|
-
|
333
|
+
set_list_position(0)
|
318
334
|
decrement_positions_on_lower_items(old_position)
|
319
335
|
end
|
320
336
|
end
|
@@ -325,6 +341,10 @@ module ActiveRecord
|
|
325
341
|
return unless acts_as_list_class.unscoped.where("#{scope_condition} AND #{position_column} = #{new_position}").count > 1
|
326
342
|
shuffle_positions_on_intermediate_items old_position, new_position, id
|
327
343
|
end
|
344
|
+
|
345
|
+
def reload_position
|
346
|
+
self.reload
|
347
|
+
end
|
328
348
|
end
|
329
349
|
end
|
330
350
|
end
|
data/lib/acts_as_list/version.rb
CHANGED
data/test/shared_list.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
module Shared
|
2
2
|
module List
|
3
3
|
def setup
|
4
|
-
(1..4).each
|
4
|
+
(1..4).each do |counter|
|
5
|
+
node = ListMixin.new :parent_id => 5
|
6
|
+
node.pos = counter
|
7
|
+
node.save!
|
8
|
+
end
|
5
9
|
end
|
6
10
|
|
7
11
|
def test_reordering
|
@@ -204,14 +208,18 @@ module Shared
|
|
204
208
|
def test_before_create_callback_adds_to_given_position
|
205
209
|
assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
|
206
210
|
|
207
|
-
new = ListMixin.
|
211
|
+
new = ListMixin.new(:parent_id => 5)
|
212
|
+
new.pos = 1
|
213
|
+
new.save!
|
208
214
|
assert_equal 1, new.pos
|
209
215
|
assert new.first?
|
210
216
|
assert !new.last?
|
211
217
|
|
212
218
|
assert_equal [5, 1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
|
213
219
|
|
214
|
-
new = ListMixin.
|
220
|
+
new = ListMixin.new(:parent_id => 5)
|
221
|
+
new.pos = 3
|
222
|
+
new.save!
|
215
223
|
assert_equal 3, new.pos
|
216
224
|
assert !new.first?
|
217
225
|
assert !new.last?
|
data/test/shared_list_sub.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
module Shared
|
2
2
|
module ListSub
|
3
3
|
def setup
|
4
|
-
(1..4).each
|
4
|
+
(1..4).each do |i|
|
5
|
+
node = ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).new :parent_id => 5000
|
6
|
+
node.pos = i
|
7
|
+
node.save!
|
8
|
+
end
|
5
9
|
end
|
6
10
|
|
7
11
|
def test_reordering
|
data/test/test_list.rb
CHANGED
@@ -36,8 +36,27 @@ end
|
|
36
36
|
|
37
37
|
class Mixin < ActiveRecord::Base
|
38
38
|
self.table_name = 'mixins'
|
39
|
+
attr_accessible :active, :parent_id, :parent_type
|
39
40
|
end
|
40
41
|
|
42
|
+
class ProtectedMixin < ActiveRecord::Base
|
43
|
+
self.table_name = 'mixins'
|
44
|
+
attr_protected :active
|
45
|
+
end
|
46
|
+
|
47
|
+
class ProtectedListMixin < ProtectedMixin
|
48
|
+
acts_as_list :column => "pos"
|
49
|
+
end
|
50
|
+
|
51
|
+
class UnProtectedMixin < ActiveRecord::Base
|
52
|
+
self.table_name = 'mixins'
|
53
|
+
end
|
54
|
+
|
55
|
+
class UnProtectedListMixin < UnProtectedMixin
|
56
|
+
acts_as_list :column => "pos"
|
57
|
+
end
|
58
|
+
|
59
|
+
|
41
60
|
class ListMixin < Mixin
|
42
61
|
acts_as_list :column => "pos", :scope => :parent
|
43
62
|
end
|
@@ -164,7 +183,7 @@ end
|
|
164
183
|
class DefaultScopedTest < ActsAsListTestCase
|
165
184
|
def setup
|
166
185
|
setup_db
|
167
|
-
(1..4).each { |counter| DefaultScopedMixin.create!
|
186
|
+
(1..4).each { |counter| DefaultScopedMixin.create!({:pos => counter}) }
|
168
187
|
end
|
169
188
|
|
170
189
|
def test_insert
|
@@ -243,13 +262,13 @@ class DefaultScopedTest < ActsAsListTestCase
|
|
243
262
|
|
244
263
|
def test_update_position
|
245
264
|
assert_equal [1, 2, 3, 4], DefaultScopedMixin.find(:all).map(&:id)
|
246
|
-
DefaultScopedMixin.find(2).
|
265
|
+
DefaultScopedMixin.find(2).set_list_position(4)
|
247
266
|
assert_equal [1, 3, 4, 2], DefaultScopedMixin.find(:all).map(&:id)
|
248
|
-
DefaultScopedMixin.find(2).
|
267
|
+
DefaultScopedMixin.find(2).set_list_position(2)
|
249
268
|
assert_equal [1, 2, 3, 4], DefaultScopedMixin.find(:all).map(&:id)
|
250
|
-
DefaultScopedMixin.find(1).
|
269
|
+
DefaultScopedMixin.find(1).set_list_position(4)
|
251
270
|
assert_equal [2, 3, 4, 1], DefaultScopedMixin.find(:all).map(&:id)
|
252
|
-
DefaultScopedMixin.find(1).
|
271
|
+
DefaultScopedMixin.find(1).set_list_position(1)
|
253
272
|
assert_equal [1, 2, 3, 4], DefaultScopedMixin.find(:all).map(&:id)
|
254
273
|
end
|
255
274
|
|
@@ -337,18 +356,63 @@ class DefaultScopedWhereTest < ActsAsListTestCase
|
|
337
356
|
|
338
357
|
def test_update_position
|
339
358
|
assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
|
340
|
-
DefaultScopedWhereMixin.where(:active => false).find(2).
|
359
|
+
DefaultScopedWhereMixin.where(:active => false).find(2).set_list_position(4)
|
341
360
|
assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
|
342
|
-
DefaultScopedWhereMixin.where(:active => false).find(2).
|
361
|
+
DefaultScopedWhereMixin.where(:active => false).find(2).set_list_position(2)
|
343
362
|
assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
|
344
|
-
DefaultScopedWhereMixin.where(:active => false).find(1).
|
363
|
+
DefaultScopedWhereMixin.where(:active => false).find(1).set_list_position(4)
|
345
364
|
assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
|
346
|
-
DefaultScopedWhereMixin.where(:active => false).find(1).
|
365
|
+
DefaultScopedWhereMixin.where(:active => false).find(1).set_list_position(1)
|
347
366
|
assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
|
348
367
|
end
|
349
368
|
|
350
369
|
end
|
351
370
|
|
371
|
+
class MultiDestroyTest < ActsAsListTestCase
|
372
|
+
|
373
|
+
def setup
|
374
|
+
setup_db
|
375
|
+
end
|
376
|
+
|
377
|
+
# example:
|
378
|
+
#
|
379
|
+
# class TodoList < ActiveRecord::Base
|
380
|
+
# has_many :todo_items, :order => "position"
|
381
|
+
# accepts_nested_attributes_for :todo_items, :allow_destroy => true
|
382
|
+
# end
|
383
|
+
#
|
384
|
+
# class TodoItem < ActiveRecord::Base
|
385
|
+
# belongs_to :todo_list
|
386
|
+
# acts_as_list :scope => :todo_list
|
387
|
+
# end
|
388
|
+
#
|
389
|
+
# Assume that there are three items.
|
390
|
+
# The user mark two items as deleted, click save button, form will be post:
|
391
|
+
#
|
392
|
+
# todo_list.todo_items_attributes = [
|
393
|
+
# {id: 1, _destroy: true},
|
394
|
+
# {id: 2, _destroy: true}
|
395
|
+
# ]
|
396
|
+
#
|
397
|
+
# Save toto_list, the position of item #3 should eql 1.
|
398
|
+
#
|
399
|
+
def test_destroy
|
400
|
+
new1 = DefaultScopedMixin.create
|
401
|
+
assert_equal 1, new1.pos
|
402
|
+
|
403
|
+
new2 = DefaultScopedMixin.create
|
404
|
+
assert_equal 2, new2.pos
|
405
|
+
|
406
|
+
new3 = DefaultScopedMixin.create
|
407
|
+
assert_equal 3, new3.pos
|
408
|
+
|
409
|
+
new1.destroy
|
410
|
+
new2.destroy
|
411
|
+
new3.reload
|
412
|
+
assert_equal 1, new3.pos
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
352
416
|
#class TopAdditionMixin < Mixin
|
353
417
|
|
354
418
|
class TopAdditionTest < ActsAsListTestCase
|
@@ -360,6 +424,7 @@ class TopAdditionTest < ActsAsListTestCase
|
|
360
424
|
end
|
361
425
|
end
|
362
426
|
|
427
|
+
|
363
428
|
class TopAdditionTestWithDefault < ActsAsListTestCase
|
364
429
|
include Shared::TopAddition
|
365
430
|
|
@@ -368,3 +433,44 @@ class TopAdditionTestWithDefault < ActsAsListTestCase
|
|
368
433
|
super
|
369
434
|
end
|
370
435
|
end
|
436
|
+
|
437
|
+
class RespectMixinProtection < ActsAsListTestCase
|
438
|
+
def setup
|
439
|
+
setup_db_with_default
|
440
|
+
super
|
441
|
+
end
|
442
|
+
|
443
|
+
# if an attribute is set attr_protected
|
444
|
+
# it should be unchanged by update_attributes
|
445
|
+
def test_unmodified_protection
|
446
|
+
a = ProtectedMixin.new
|
447
|
+
a.update_attributes({:active => false})
|
448
|
+
assert_equal true, a.active
|
449
|
+
end
|
450
|
+
|
451
|
+
# even after the acts_as_list mixin is joined
|
452
|
+
# that protection should continue to exist
|
453
|
+
def test_still_protected
|
454
|
+
b = ProtectedListMixin.new
|
455
|
+
b.update_attributes({:active => false})
|
456
|
+
assert_equal true, b.active
|
457
|
+
end
|
458
|
+
|
459
|
+
# similarly, if a class lacks mass_assignment protection
|
460
|
+
# it should be able to be changed
|
461
|
+
def test_unprotected
|
462
|
+
a = UnProtectedMixin.new
|
463
|
+
a.update_attributes({:active => false})
|
464
|
+
assert_equal false, a.active
|
465
|
+
end
|
466
|
+
|
467
|
+
# and it should continue to be mutable by mass_assignment
|
468
|
+
# even after the acts_as_list plugin has been joined
|
469
|
+
def test_still_unprotected_mixin
|
470
|
+
b = UnProtectedListMixin.new
|
471
|
+
b.assign_attributes({:active => false})
|
472
|
+
# p UnProtectedListMixin.accessible_attributes.length
|
473
|
+
assert_equal false, b.active
|
474
|
+
end
|
475
|
+
|
476
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,11 +11,11 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-12-04 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
18
|
-
requirement: &
|
18
|
+
requirement: &2155911360 !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
21
|
- - ! '>='
|
@@ -23,10 +23,10 @@ dependencies:
|
|
23
23
|
version: 1.0.0
|
24
24
|
type: :development
|
25
25
|
prerelease: false
|
26
|
-
version_requirements: *
|
26
|
+
version_requirements: *2155911360
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
|
-
requirement: &
|
29
|
+
requirement: &2155910780 !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
31
|
requirements:
|
32
32
|
- - ! '>='
|
@@ -34,10 +34,10 @@ dependencies:
|
|
34
34
|
version: 1.15.4.7794
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
|
-
version_requirements: *
|
37
|
+
version_requirements: *2155910780
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: rdoc
|
40
|
-
requirement: &
|
40
|
+
requirement: &2155910340 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
43
|
- - ! '>='
|
@@ -45,10 +45,10 @@ dependencies:
|
|
45
45
|
version: '0'
|
46
46
|
type: :development
|
47
47
|
prerelease: false
|
48
|
-
version_requirements: *
|
48
|
+
version_requirements: *2155910340
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: sqlite3
|
51
|
-
requirement: &
|
51
|
+
requirement: &2155909700 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
54
|
- - ! '>='
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
version: '0'
|
57
57
|
type: :development
|
58
58
|
prerelease: false
|
59
|
-
version_requirements: *
|
59
|
+
version_requirements: *2155909700
|
60
60
|
description: This "acts_as" extension provides the capabilities for sorting and reordering
|
61
61
|
a number of objects in a list. The class that has this specified needs to have a
|
62
62
|
"position" column defined as an integer on the mapped database table.
|