acts_as_list 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Gemfile +5 -1
- data/README.md +9 -0
- data/gemfiles/rails3/Gemfile +4 -0
- data/gemfiles/rails4/Gemfile +4 -1
- data/init.rb +0 -2
- data/lib/acts_as_list.rb +5 -12
- data/lib/acts_as_list/active_record/acts/list.rb +85 -53
- data/lib/acts_as_list/version.rb +1 -1
- data/test/shared_list.rb +3 -3
- data/test/test_list.rb +16 -11
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cf6c26261c5ef1690443023c346ddf8de5f5a95
|
4
|
+
data.tar.gz: 419d7210f9a06bd54a113da0b8e8c929f716f4b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07d0a625d3d0ea4344b4fea41be748b0191a0495edd5255e679c3c543b3101e70711f47595f5f6009f2490f01cf7c8e5683370254a65d654bed00ec7e6eb5265
|
7
|
+
data.tar.gz: aaea3b5d86259b0564ea278a7dc936b2826b82a66d6c25e8abc33cb9124d434fb4a57de70c01751176ef285d8d36a25f8b62b9b60e18a6dcf198713fbe5cd2d6
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -76,6 +76,15 @@ All `position` queries (select, update, etc.) inside gem methods are executed wi
|
|
76
76
|
|
77
77
|
The `position` column is set after validations are called, so you should not put a `presence` validation on the `position` column.
|
78
78
|
|
79
|
+
|
80
|
+
If you need a scope by a non-association field you should pass an array, containing field name, to a scope:
|
81
|
+
```ruby
|
82
|
+
class TodoItem < ActiveRecord::Base
|
83
|
+
# `kind` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association
|
84
|
+
acts_as_list scope: [:kind]
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
79
88
|
## Versions
|
80
89
|
All versions `0.1.5` onwards require Rails 3.0.x and higher.
|
81
90
|
|
data/gemfiles/rails3/Gemfile
CHANGED
data/gemfiles/rails4/Gemfile
CHANGED
@@ -9,10 +9,13 @@ platforms :rbx do
|
|
9
9
|
gem 'rubysl-test-unit'
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
12
|
gem 'activerecord', '>= 4.0.0', '< 5'
|
14
13
|
|
15
14
|
# Specify your gem's dependencies in acts_as_list.gemspec
|
16
15
|
gemspec :path => File.join('..', '..')
|
17
16
|
|
18
17
|
gem 'rake'
|
18
|
+
|
19
|
+
group :test do
|
20
|
+
gem 'minitest', '~> 5'
|
21
|
+
end
|
data/init.rb
CHANGED
data/lib/acts_as_list.rb
CHANGED
@@ -1,24 +1,17 @@
|
|
1
1
|
require 'acts_as_list/active_record/acts/list'
|
2
2
|
|
3
3
|
module ActsAsList
|
4
|
-
|
4
|
+
begin
|
5
5
|
require 'rails'
|
6
|
+
|
6
7
|
class Railtie < Rails::Railtie
|
7
8
|
initializer 'acts_as_list.insert_into_active_record' do
|
8
9
|
ActiveSupport.on_load :active_record do
|
9
|
-
|
10
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Acts::List)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
13
|
-
|
14
|
-
|
15
|
-
class Railtie
|
16
|
-
def self.insert
|
17
|
-
if defined?(ActiveRecord)
|
18
|
-
ActiveRecord::Base.send(:include, ActiveRecord::Acts::List)
|
19
|
-
end
|
20
|
-
end
|
14
|
+
rescue LoadError
|
15
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Acts::List) if defined?(ActiveRecord)
|
21
16
|
end
|
22
17
|
end
|
23
|
-
|
24
|
-
ActsAsList::Railtie.insert
|
@@ -99,6 +99,11 @@ module ActiveRecord
|
|
99
99
|
'#{configuration[:add_new_at]}'
|
100
100
|
end
|
101
101
|
|
102
|
+
def #{configuration[:column]}=(position)
|
103
|
+
write_attribute(:#{configuration[:column]}, position)
|
104
|
+
@position_changed = true
|
105
|
+
end
|
106
|
+
|
102
107
|
#{scope_methods}
|
103
108
|
|
104
109
|
# only add to attr_accessible
|
@@ -182,7 +187,8 @@ module ActiveRecord
|
|
182
187
|
end
|
183
188
|
end
|
184
189
|
|
185
|
-
# Move the item within scope
|
190
|
+
# Move the item within scope. If a position within the new scope isn't supplied, the item will
|
191
|
+
# be appended to the end of the list.
|
186
192
|
def move_within_scope(scope_id)
|
187
193
|
send("#{scope_name}=", scope_id)
|
188
194
|
save!
|
@@ -215,9 +221,10 @@ module ActiveRecord
|
|
215
221
|
# Return the next higher item in the list.
|
216
222
|
def higher_item
|
217
223
|
return nil unless in_list?
|
218
|
-
acts_as_list_class.unscoped
|
219
|
-
where("#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}").
|
224
|
+
acts_as_list_class.unscoped do
|
225
|
+
acts_as_list_class.where("#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}").
|
220
226
|
order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
|
227
|
+
end
|
221
228
|
end
|
222
229
|
|
223
230
|
# Return the next n higher items in the list
|
@@ -235,9 +242,10 @@ module ActiveRecord
|
|
235
242
|
# Return the next lower item in the list.
|
236
243
|
def lower_item
|
237
244
|
return nil unless in_list?
|
238
|
-
acts_as_list_class.unscoped
|
239
|
-
where("#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}").
|
245
|
+
acts_as_list_class.unscoped do
|
246
|
+
acts_as_list_class.where("#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}").
|
240
247
|
order("#{acts_as_list_class.table_name}.#{position_column} ASC").first
|
248
|
+
end
|
241
249
|
end
|
242
250
|
|
243
251
|
# Return the next n lower items in the list
|
@@ -266,19 +274,20 @@ module ActiveRecord
|
|
266
274
|
end
|
267
275
|
|
268
276
|
def default_position?
|
269
|
-
default_position == send(position_column)
|
277
|
+
default_position && default_position.to_i == send(position_column)
|
270
278
|
end
|
271
279
|
|
272
280
|
# Sets the new position and saves it
|
273
281
|
def set_list_position(new_position)
|
274
|
-
|
275
|
-
save
|
282
|
+
write_attribute position_column, new_position
|
283
|
+
save(validate: false)
|
276
284
|
end
|
277
285
|
|
278
286
|
private
|
279
287
|
def acts_as_list_list
|
280
|
-
acts_as_list_class.unscoped
|
281
|
-
where(scope_condition)
|
288
|
+
acts_as_list_class.unscoped do
|
289
|
+
acts_as_list_class.where(scope_condition)
|
290
|
+
end
|
282
291
|
end
|
283
292
|
|
284
293
|
def add_to_list_top
|
@@ -287,10 +296,10 @@ module ActiveRecord
|
|
287
296
|
end
|
288
297
|
|
289
298
|
def add_to_list_bottom
|
290
|
-
if not_in_list? || default_position?
|
299
|
+
if not_in_list? || scope_changed? && !@position_changed || default_position?
|
291
300
|
self[position_column] = bottom_position_in_list.to_i + 1
|
292
301
|
else
|
293
|
-
increment_positions_on_lower_items(self[position_column])
|
302
|
+
increment_positions_on_lower_items(self[position_column], id)
|
294
303
|
end
|
295
304
|
end
|
296
305
|
|
@@ -307,8 +316,10 @@ module ActiveRecord
|
|
307
316
|
# Returns the bottom item
|
308
317
|
def bottom_item(except = nil)
|
309
318
|
conditions = scope_condition
|
310
|
-
conditions = "#{conditions} AND #{self.class.primary_key} !=
|
311
|
-
acts_as_list_class.unscoped
|
319
|
+
conditions = "#{conditions} AND #{self.class.primary_key} != #{self.class.connection.quote(except.id)}" if except
|
320
|
+
acts_as_list_class.unscoped do
|
321
|
+
acts_as_list_class.in_list.where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
|
322
|
+
end
|
312
323
|
end
|
313
324
|
|
314
325
|
# Forces item to assume the bottom position in the list.
|
@@ -323,76 +334,93 @@ module ActiveRecord
|
|
323
334
|
|
324
335
|
# This has the effect of moving all the higher items up one.
|
325
336
|
def decrement_positions_on_higher_items(position)
|
326
|
-
acts_as_list_class.unscoped
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
337
|
+
acts_as_list_class.unscoped do
|
338
|
+
acts_as_list_class.where(
|
339
|
+
"#{scope_condition} AND #{position_column} <= #{position}"
|
340
|
+
).update_all(
|
341
|
+
"#{position_column} = (#{position_column} - 1)"
|
342
|
+
)
|
343
|
+
end
|
331
344
|
end
|
332
345
|
|
333
346
|
# This has the effect of moving all the lower items up one.
|
334
347
|
def decrement_positions_on_lower_items(position=nil)
|
335
348
|
return unless in_list?
|
336
349
|
position ||= send(position_column).to_i
|
337
|
-
acts_as_list_class.unscoped
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
350
|
+
acts_as_list_class.unscoped do
|
351
|
+
acts_as_list_class.where(
|
352
|
+
"#{scope_condition} AND #{position_column} > #{position}"
|
353
|
+
).update_all(
|
354
|
+
"#{position_column} = (#{position_column} - 1)"
|
355
|
+
)
|
356
|
+
end
|
342
357
|
end
|
343
358
|
|
344
359
|
# This has the effect of moving all the higher items down one.
|
345
360
|
def increment_positions_on_higher_items
|
346
361
|
return unless in_list?
|
347
|
-
acts_as_list_class.unscoped
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
362
|
+
acts_as_list_class.unscoped do
|
363
|
+
acts_as_list_class.where(
|
364
|
+
"#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
|
365
|
+
).update_all(
|
366
|
+
"#{position_column} = (#{position_column} + 1)"
|
367
|
+
)
|
368
|
+
end
|
352
369
|
end
|
353
370
|
|
354
371
|
# This has the effect of moving all the lower items down one.
|
355
|
-
def increment_positions_on_lower_items(position)
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
372
|
+
def increment_positions_on_lower_items(position, avoid_id = nil)
|
373
|
+
avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
|
374
|
+
|
375
|
+
acts_as_list_class.unscoped do
|
376
|
+
acts_as_list_class.where(
|
377
|
+
"#{scope_condition} AND #{position_column} >= #{position}#{avoid_id_condition}"
|
378
|
+
).update_all(
|
379
|
+
"#{position_column} = (#{position_column} + 1)"
|
380
|
+
)
|
381
|
+
end
|
361
382
|
end
|
362
383
|
|
363
384
|
# Increments position (<tt>position_column</tt>) of all items in the list.
|
364
385
|
def increment_positions_on_all_items
|
365
|
-
acts_as_list_class.unscoped
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
386
|
+
acts_as_list_class.unscoped do
|
387
|
+
acts_as_list_class.where(
|
388
|
+
"#{scope_condition}"
|
389
|
+
).update_all(
|
390
|
+
"#{position_column} = (#{position_column} + 1)"
|
391
|
+
)
|
392
|
+
end
|
370
393
|
end
|
371
394
|
|
372
395
|
# Reorders intermediate items to support moving an item from old_position to new_position.
|
373
396
|
def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil)
|
374
397
|
return if old_position == new_position
|
375
|
-
avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} !=
|
398
|
+
avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
|
399
|
+
|
376
400
|
if old_position < new_position
|
377
401
|
# Decrement position of intermediate items
|
378
402
|
#
|
379
403
|
# e.g., if moving an item from 2 to 5,
|
380
404
|
# move [3, 4, 5] to [2, 3, 4]
|
381
|
-
acts_as_list_class.unscoped
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
405
|
+
acts_as_list_class.unscoped do
|
406
|
+
acts_as_list_class.where(
|
407
|
+
"#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}#{avoid_id_condition}"
|
408
|
+
).update_all(
|
409
|
+
"#{position_column} = (#{position_column} - 1)"
|
410
|
+
)
|
411
|
+
end
|
386
412
|
else
|
387
413
|
# Increment position of intermediate items
|
388
414
|
#
|
389
415
|
# e.g., if moving an item from 5 to 2,
|
390
416
|
# move [2, 3, 4] to [3, 4, 5]
|
391
|
-
acts_as_list_class.unscoped
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
417
|
+
acts_as_list_class.unscoped do
|
418
|
+
acts_as_list_class.where(
|
419
|
+
"#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}#{avoid_id_condition}"
|
420
|
+
).update_all(
|
421
|
+
"#{position_column} = (#{position_column} + 1)"
|
422
|
+
)
|
423
|
+
end
|
396
424
|
end
|
397
425
|
end
|
398
426
|
|
@@ -421,7 +449,9 @@ module ActiveRecord
|
|
421
449
|
old_position = send("#{position_column}_was").to_i
|
422
450
|
new_position = send(position_column).to_i
|
423
451
|
|
424
|
-
return unless acts_as_list_class.unscoped
|
452
|
+
return unless acts_as_list_class.unscoped do
|
453
|
+
acts_as_list_class.where("#{scope_condition} AND #{position_column} = #{new_position}").count > 1
|
454
|
+
end
|
425
455
|
shuffle_positions_on_intermediate_items old_position, new_position, id
|
426
456
|
end
|
427
457
|
|
@@ -444,8 +474,10 @@ module ActiveRecord
|
|
444
474
|
self.reload
|
445
475
|
end
|
446
476
|
|
477
|
+
# This check is skipped if the position is currently the default position from the table
|
478
|
+
# as modifying the default position on creation is handled elsewhere
|
447
479
|
def check_top_position
|
448
|
-
if send(position_column) && send(position_column) < acts_as_list_top
|
480
|
+
if send(position_column) && !default_position? && send(position_column) < acts_as_list_top
|
449
481
|
self[position_column] = acts_as_list_top
|
450
482
|
end
|
451
483
|
end
|
data/lib/acts_as_list/version.rb
CHANGED
data/test/shared_list.rb
CHANGED
@@ -137,12 +137,12 @@ module Shared
|
|
137
137
|
def test_nil_scope
|
138
138
|
new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
|
139
139
|
new2.move_higher
|
140
|
-
assert_equal [new2, new1, new3], ListMixin.where(parent_id: nil).order('pos')
|
140
|
+
assert_equal [new2, new1, new3].map(&:id), ListMixin.where(parent_id: nil).order('pos').map(&:id)
|
141
141
|
end
|
142
142
|
|
143
143
|
def test_update_position_when_scope_changes
|
144
144
|
assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
|
145
|
-
parent = ListMixin.create(
|
145
|
+
parent = ListMixin.create(parent_id: 6)
|
146
146
|
|
147
147
|
ListMixin.where(id: 2).first.move_within_scope(6)
|
148
148
|
|
@@ -155,7 +155,7 @@ module Shared
|
|
155
155
|
assert_equal 3, ListMixin.where(id: 4).first.pos
|
156
156
|
|
157
157
|
ListMixin.where(id: 2).first.move_within_scope(5)
|
158
|
-
assert_equal [1,
|
158
|
+
assert_equal [1, 3, 4, 2], ListMixin.where(parent_id: 5).order('pos').map(&:id)
|
159
159
|
end
|
160
160
|
|
161
161
|
def test_remove_from_list_should_then_fail_in_list?
|
data/test/test_list.rb
CHANGED
@@ -6,16 +6,21 @@ ActiveRecord::Schema.verbose = false
|
|
6
6
|
|
7
7
|
def setup_db(position_options = {})
|
8
8
|
# AR caches columns options like defaults etc. Clear them!
|
9
|
+
ActiveRecord::Base.connection.create_table :mixins do |t|
|
10
|
+
t.column :pos, :integer, position_options
|
11
|
+
t.column :active, :boolean, default: true
|
12
|
+
t.column :parent_id, :integer
|
13
|
+
t.column :parent_type, :string
|
14
|
+
t.column :created_at, :datetime
|
15
|
+
t.column :updated_at, :datetime
|
16
|
+
end
|
17
|
+
|
9
18
|
ActiveRecord::Base.connection.schema_cache.clear!
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
t.column :parent_type, :string
|
16
|
-
t.column :created_at, :datetime
|
17
|
-
t.column :updated_at, :datetime
|
18
|
-
end
|
19
|
+
[ Mixin, ListMixin, ListMixinSub1, ListMixinSub2, ListWithStringScopeMixin,
|
20
|
+
ArrayScopeListMixin, ZeroBasedMixin, DefaultScopedMixin,
|
21
|
+
DefaultScopedWhereMixin, TopAdditionMixin, NoAdditionMixin ].each do |klass|
|
22
|
+
|
23
|
+
klass.reset_column_information
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
@@ -87,8 +92,8 @@ class NoAdditionMixin < Mixin
|
|
87
92
|
acts_as_list column: "pos", add_new_at: nil, scope: :parent_id
|
88
93
|
end
|
89
94
|
|
90
|
-
class ActsAsListTestCase <
|
91
|
-
# No default test required
|
95
|
+
class ActsAsListTestCase < Minitest::Test
|
96
|
+
# No default test required as this class is abstract.
|
92
97
|
# Need for test/unit.
|
93
98
|
undef_method :default_test if method_defined?(:default_test)
|
94
99
|
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-10-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
92
|
version: '0'
|
93
93
|
requirements: []
|
94
94
|
rubyforge_project: acts_as_list
|
95
|
-
rubygems_version: 2.
|
95
|
+
rubygems_version: 2.0.14
|
96
96
|
signing_key:
|
97
97
|
specification_version: 4
|
98
98
|
summary: A gem allowing a active_record model to act_as_list.
|