acts_as_list 0.9.5 → 0.9.6
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/CHANGELOG.md +13 -0
- data/acts_as_list.gemspec +2 -2
- data/lib/acts_as_list.rb +3 -2
- data/lib/acts_as_list/active_record/acts/active_record.rb +3 -0
- data/lib/acts_as_list/active_record/acts/list.rb +65 -62
- data/lib/acts_as_list/active_record/acts/no_update.rb +7 -4
- data/lib/acts_as_list/active_record/acts/position_column_method_definer.rb +22 -7
- data/lib/acts_as_list/version.rb +1 -1
- data/test/shared_array_scope_list.rb +1 -1
- data/test/shared_list.rb +1 -1
- data/test/shared_top_addition.rb +1 -1
- data/test/test_list.rb +8 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5be2da067217f4dab82cf86a51c149578b3839ff
|
4
|
+
data.tar.gz: ead2d7fc41857aa193f37d0f8cbd5bffbe052462
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa9338e297f70fc50afd3d1ad6bf4d933883f0626f6f85a2ef79543255a4191df222a90e9b683edc95d3f613f27135538de0dc795086144e7d6bcbc4404c87a9
|
7
|
+
data.tar.gz: 02f13a0c906e75a3fe32341bcaa6237c3ba4ba258bbf80809a7cc332dffb0e1d90e3ab1047335ad46f6f315a3daf2b46dadc44959d791009617588d849f99988
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.9.5](https://github.com/swanandp/acts_as_list/tree/v0.9.5) (2017-04-04)
|
4
|
+
[Full Changelog](https://github.com/swanandp/acts_as_list/compare/v0.9.4...v0.9.5)
|
5
|
+
|
6
|
+
**Closed issues:**
|
7
|
+
|
8
|
+
- acts\_as\_list\_class.maximum\(position\_column\) is causing the entire table to lock [\#264](https://github.com/swanandp/acts_as_list/issues/264)
|
9
|
+
- Be more precise with unscope-ing [\#263](https://github.com/swanandp/acts_as_list/issues/263)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Use bottom\_position\_in\_list instead of the highest value in the table [\#266](https://github.com/swanandp/acts_as_list/pull/266) ([brendon](https://github.com/brendon))
|
14
|
+
- Be more surgical about unscoping [\#265](https://github.com/swanandp/acts_as_list/pull/265) ([brendon](https://github.com/brendon))
|
15
|
+
|
3
16
|
## [v0.9.4](https://github.com/swanandp/acts_as_list/tree/v0.9.4) (2017-03-16)
|
4
17
|
[Full Changelog](https://github.com/swanandp/acts_as_list/compare/v0.9.3...v0.9.4)
|
5
18
|
|
data/acts_as_list.gemspec
CHANGED
@@ -24,6 +24,6 @@ Gem::Specification.new do |s|
|
|
24
24
|
|
25
25
|
|
26
26
|
# Dependencies (installed via "bundle install")
|
27
|
-
s.add_dependency
|
28
|
-
s.add_development_dependency
|
27
|
+
s.add_dependency "activerecord", ">= 3.0"
|
28
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
29
29
|
end
|
data/lib/acts_as_list.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require "acts_as_list/active_record/acts/list"
|
2
2
|
require "acts_as_list/active_record/acts/position_column_method_definer"
|
3
3
|
require "acts_as_list/active_record/acts/scope_method_definer"
|
4
4
|
require "acts_as_list/active_record/acts/top_of_list_method_definer"
|
5
5
|
require "acts_as_list/active_record/acts/add_new_at_method_definer"
|
6
6
|
require "acts_as_list/active_record/acts/aux_method_definer"
|
7
7
|
require "acts_as_list/active_record/acts/callback_definer"
|
8
|
-
require
|
8
|
+
require "acts_as_list/active_record/acts/no_update"
|
9
9
|
require "acts_as_list/active_record/acts/sequential_updates_method_definer"
|
10
|
+
require "acts_as_list/active_record/acts/active_record"
|
@@ -1,64 +1,66 @@
|
|
1
|
-
class << ActiveRecord::Base
|
2
|
-
# Configuration options are:
|
3
|
-
#
|
4
|
-
# * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
|
5
|
-
# * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
|
6
|
-
# (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
|
7
|
-
# to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
|
8
|
-
# Example: <tt>acts_as_list scope: 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
|
9
|
-
# * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection
|
10
|
-
# act more like an array in its indexing.
|
11
|
-
# * +add_new_at+ - specifies whether objects get added to the :top or :bottom of the list. (default: +bottom+)
|
12
|
-
# `nil` will result in new items not being added to the list on create.
|
13
|
-
# * +sequential_updates+ - specifies whether insert_at should update objects positions during shuffling
|
14
|
-
# one by one to respect position column unique not null constraint.
|
15
|
-
# Defaults to true if position column has unique index, otherwise false.
|
16
|
-
# If constraint is <tt>deferrable initially deferred<tt>, overriding it with false will speed up insert_at.
|
17
|
-
def acts_as_list(options = {})
|
18
|
-
configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom }
|
19
|
-
configuration.update(options) if options.is_a?(Hash)
|
20
|
-
|
21
|
-
caller_class = self
|
22
|
-
|
23
|
-
ActiveRecord::Acts::List::PositionColumnMethodDefiner.call(caller_class, configuration[:column])
|
24
|
-
ActiveRecord::Acts::List::ScopeMethodDefiner.call(caller_class, configuration[:scope])
|
25
|
-
ActiveRecord::Acts::List::TopOfListMethodDefiner.call(caller_class, configuration[:top_of_list])
|
26
|
-
ActiveRecord::Acts::List::AddNewAtMethodDefiner.call(caller_class, configuration[:add_new_at])
|
27
|
-
|
28
|
-
ActiveRecord::Acts::List::AuxMethodDefiner.call(caller_class)
|
29
|
-
ActiveRecord::Acts::List::CallbackDefiner.call(caller_class, configuration[:add_new_at])
|
30
|
-
ActiveRecord::Acts::List::SequentialUpdatesMethodDefiner.call(caller_class, configuration[:column], configuration[:sequential_updates])
|
31
|
-
|
32
|
-
include ActiveRecord::Acts::List::InstanceMethods
|
33
|
-
include ActiveRecord::Acts::List::NoUpdate
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
1
|
module ActiveRecord
|
38
2
|
module Acts #:nodoc:
|
39
3
|
module List #:nodoc:
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# Configuration options are:
|
7
|
+
#
|
8
|
+
# * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
|
9
|
+
# * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
|
10
|
+
# (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
|
11
|
+
# to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
|
12
|
+
# Example: <tt>acts_as_list scope: 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
|
13
|
+
# * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection
|
14
|
+
# act more like an array in its indexing.
|
15
|
+
# * +add_new_at+ - specifies whether objects get added to the :top or :bottom of the list. (default: +bottom+)
|
16
|
+
# `nil` will result in new items not being added to the list on create.
|
17
|
+
# * +sequential_updates+ - specifies whether insert_at should update objects positions during shuffling
|
18
|
+
# one by one to respect position column unique not null constraint.
|
19
|
+
# Defaults to true if position column has unique index, otherwise false.
|
20
|
+
# If constraint is <tt>deferrable initially deferred<tt>, overriding it with false will speed up insert_at.
|
21
|
+
def acts_as_list(options = {})
|
22
|
+
configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom }
|
23
|
+
configuration.update(options) if options.is_a?(Hash)
|
24
|
+
|
25
|
+
caller_class = self
|
26
|
+
|
27
|
+
ActiveRecord::Acts::List::PositionColumnMethodDefiner.call(caller_class, configuration[:column])
|
28
|
+
ActiveRecord::Acts::List::ScopeMethodDefiner.call(caller_class, configuration[:scope])
|
29
|
+
ActiveRecord::Acts::List::TopOfListMethodDefiner.call(caller_class, configuration[:top_of_list])
|
30
|
+
ActiveRecord::Acts::List::AddNewAtMethodDefiner.call(caller_class, configuration[:add_new_at])
|
31
|
+
|
32
|
+
ActiveRecord::Acts::List::AuxMethodDefiner.call(caller_class)
|
33
|
+
ActiveRecord::Acts::List::CallbackDefiner.call(caller_class, configuration[:add_new_at])
|
34
|
+
ActiveRecord::Acts::List::SequentialUpdatesMethodDefiner.call(caller_class, configuration[:column], configuration[:sequential_updates])
|
35
|
+
|
36
|
+
include ActiveRecord::Acts::List::InstanceMethods
|
37
|
+
include ActiveRecord::Acts::List::NoUpdate
|
38
|
+
end
|
39
|
+
|
40
|
+
# This +acts_as+ extension provides the capabilities for sorting and reordering a number of objects in a list.
|
41
|
+
# The class that has this specified needs to have a +position+ column defined as an integer on
|
42
|
+
# the mapped database table.
|
43
|
+
#
|
44
|
+
# Todo list example:
|
45
|
+
#
|
46
|
+
# class TodoList < ActiveRecord::Base
|
47
|
+
# has_many :todo_items, order: "position"
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# class TodoItem < ActiveRecord::Base
|
51
|
+
# belongs_to :todo_list
|
52
|
+
# acts_as_list scope: :todo_list
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# todo_list.first.move_to_bottom
|
56
|
+
# todo_list.last.move_higher
|
57
|
+
|
58
|
+
# All the methods available to a record that has had <tt>acts_as_list</tt> specified. Each method works
|
59
|
+
# by assuming the object to be the item in the list, so <tt>chapter.move_lower</tt> would move that chapter
|
60
|
+
# lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return +true+ if that chapter is
|
61
|
+
# the first in the list of all chapters.
|
62
|
+
end
|
63
|
+
|
62
64
|
module InstanceMethods
|
63
65
|
# Insert the item at the given position (defaults to the top position of 1).
|
64
66
|
def insert_at(position = acts_as_list_top)
|
@@ -376,7 +378,7 @@ module ActiveRecord
|
|
376
378
|
end
|
377
379
|
end
|
378
380
|
end
|
379
|
-
|
381
|
+
|
380
382
|
def insert_at_position(position)
|
381
383
|
return set_list_position(position) if new_record?
|
382
384
|
with_lock do
|
@@ -408,7 +410,7 @@ module ActiveRecord
|
|
408
410
|
|
409
411
|
def position_before_save
|
410
412
|
if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 1 ||
|
411
|
-
|
413
|
+
ActiveRecord::VERSION::MAJOR > 5
|
412
414
|
|
413
415
|
send("#{position_column}_before_last_save")
|
414
416
|
else
|
@@ -430,9 +432,9 @@ module ActiveRecord
|
|
430
432
|
if internal_scope_changed?
|
431
433
|
cached_changes = changes
|
432
434
|
|
433
|
-
cached_changes.each { |attribute, values|
|
435
|
+
cached_changes.each { |attribute, values| send("#{attribute}=", values[0]) }
|
434
436
|
send('decrement_positions_on_lower_items') if lower_item
|
435
|
-
cached_changes.each { |attribute, values|
|
437
|
+
cached_changes.each { |attribute, values| send("#{attribute}=", values[1]) }
|
436
438
|
|
437
439
|
send("add_to_list_#{add_new_at}") if add_new_at.present?
|
438
440
|
end
|
@@ -460,6 +462,7 @@ module ActiveRecord
|
|
460
462
|
@_quoted_position_column_with_table_name ||= "#{quoted_table_name}.#{quoted_position_column}"
|
461
463
|
end
|
462
464
|
end
|
465
|
+
|
463
466
|
end
|
464
467
|
end
|
465
468
|
end
|
@@ -2,7 +2,10 @@ module ActiveRecord
|
|
2
2
|
module Acts
|
3
3
|
module List
|
4
4
|
module NoUpdate
|
5
|
-
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
6
9
|
|
7
10
|
class ArrayTypeError < SyntaxError
|
8
11
|
def initialize
|
@@ -96,9 +99,9 @@ module ActiveRecord
|
|
96
99
|
|
97
100
|
private
|
98
101
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
+
def extracted_klasses
|
103
|
+
Thread.current[:act_as_list_no_update] ||= []
|
104
|
+
end
|
102
105
|
end
|
103
106
|
|
104
107
|
def act_as_list_no_update?
|
@@ -29,15 +29,13 @@ module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
|
29
29
|
end
|
30
30
|
|
31
31
|
define_singleton_method :update_all_with_touch do |updates|
|
32
|
-
|
33
|
-
|
34
|
-
now = record.send(:current_time_from_proper_timezone)
|
32
|
+
update_all(updates << touch_record_sql)
|
33
|
+
end
|
35
34
|
|
36
|
-
|
37
|
-
updates << ", #{connection.quote_column_name(attr)} = #{connection.quote(connection.quoted_date(now))}"
|
38
|
-
end
|
35
|
+
private
|
39
36
|
|
40
|
-
|
37
|
+
define_singleton_method :touch_record_sql do
|
38
|
+
new.touch_record_sql
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
@@ -54,6 +52,23 @@ module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
|
54
52
|
write_attribute(position_column, position)
|
55
53
|
@position_changed = true
|
56
54
|
end
|
55
|
+
|
56
|
+
define_method :touch_record_sql do
|
57
|
+
cached_quoted_now = quoted_current_time_from_proper_timezone
|
58
|
+
|
59
|
+
timestamp_attributes_for_update_in_model.map do |attr|
|
60
|
+
", #{connection.quote_column_name(attr)} = #{cached_quoted_now}"
|
61
|
+
end.join
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
delegate :connection, to: self
|
67
|
+
|
68
|
+
def quoted_current_time_from_proper_timezone
|
69
|
+
connection.quote(connection.quoted_date(
|
70
|
+
current_time_from_proper_timezone))
|
71
|
+
end
|
57
72
|
end
|
58
73
|
end
|
59
74
|
|
data/lib/acts_as_list/version.rb
CHANGED
@@ -62,7 +62,7 @@ module Shared
|
|
62
62
|
|
63
63
|
new = ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') }
|
64
64
|
assert_equal_or_nil $default_position,new.pos
|
65
|
-
assert_equal $default_position.is_a?(
|
65
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
66
66
|
assert !new.last?
|
67
67
|
|
68
68
|
new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
|
data/test/shared_list.rb
CHANGED
@@ -62,7 +62,7 @@ module Shared
|
|
62
62
|
|
63
63
|
new = ListMixin.acts_as_list_no_update { ListMixin.create(parent_id: 20) }
|
64
64
|
assert_equal_or_nil $default_position, new.pos
|
65
|
-
assert_equal $default_position.is_a?(
|
65
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
66
66
|
assert !new.last?
|
67
67
|
|
68
68
|
new = ListMixin.create(parent_id: 20)
|
data/test/shared_top_addition.rb
CHANGED
@@ -48,7 +48,7 @@ module Shared
|
|
48
48
|
|
49
49
|
new = TopAdditionMixin.acts_as_list_no_update { TopAdditionMixin.create(parent_id: 20) }
|
50
50
|
assert_equal_or_nil $default_position, new.pos
|
51
|
-
assert_equal $default_position.is_a?(
|
51
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
52
52
|
assert !new.last?
|
53
53
|
|
54
54
|
new = TopAdditionMixin.create(parent_id: 20)
|
data/test/test_list.rb
CHANGED
@@ -327,7 +327,7 @@ class DefaultScopedTest < ActsAsListTestCase
|
|
327
327
|
|
328
328
|
new = DefaultScopedMixin.acts_as_list_no_update { DefaultScopedMixin.create }
|
329
329
|
assert_equal_or_nil $default_position, new.pos
|
330
|
-
assert_equal $default_position.is_a?(
|
330
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
331
331
|
assert !new.last?
|
332
332
|
|
333
333
|
new = DefaultScopedMixin.create
|
@@ -431,7 +431,7 @@ class DefaultScopedWhereTest < ActsAsListTestCase
|
|
431
431
|
|
432
432
|
new = DefaultScopedWhereMixin.acts_as_list_no_update { DefaultScopedWhereMixin.create }
|
433
433
|
assert_equal_or_nil $default_position, new.pos
|
434
|
-
assert_equal $default_position.is_a?(
|
434
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
435
435
|
assert !new.last?
|
436
436
|
|
437
437
|
new = DefaultScopedWhereMixin.create
|
@@ -641,6 +641,12 @@ if rails_4
|
|
641
641
|
assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active']).map(&:pos)
|
642
642
|
assert_equal [1], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos)
|
643
643
|
end
|
644
|
+
|
645
|
+
def test_update_state
|
646
|
+
active_item = EnumArrayScopeListMixin.find_by(:parent_id => 2, :state => EnumArrayScopeListMixin.states['active'])
|
647
|
+
active_item.update(state: EnumArrayScopeListMixin.states['archived'])
|
648
|
+
assert_equal [1, 2], EnumArrayScopeListMixin.where(:parent_id => 2, :state => EnumArrayScopeListMixin.states['archived']).map(&:pos).sort
|
649
|
+
end
|
644
650
|
end
|
645
651
|
end
|
646
652
|
|
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.9.
|
4
|
+
version: 0.9.6
|
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: 2017-
|
13
|
+
date: 2017-07-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- gemfiles/rails_5_1.gemfile
|
67
67
|
- init.rb
|
68
68
|
- lib/acts_as_list.rb
|
69
|
+
- lib/acts_as_list/active_record/acts/active_record.rb
|
69
70
|
- lib/acts_as_list/active_record/acts/add_new_at_method_definer.rb
|
70
71
|
- lib/acts_as_list/active_record/acts/aux_method_definer.rb
|
71
72
|
- lib/acts_as_list/active_record/acts/callback_definer.rb
|