acts_as_list 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|