acts_as_list 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -0
- data/Appraisals +4 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -0
- data/README.md +34 -7
- data/gemfiles/rails_3_2.gemfile +1 -0
- data/gemfiles/rails_4_1.gemfile +1 -0
- data/gemfiles/rails_4_2.gemfile +1 -0
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +33 -0
- data/lib/acts_as_list.rb +1 -1
- data/lib/acts_as_list/active_record/acts/callback_definer.rb +2 -2
- data/lib/acts_as_list/active_record/acts/list.rb +10 -2
- data/lib/acts_as_list/active_record/acts/no_update.rb +73 -13
- data/lib/acts_as_list/active_record/acts/{column_method_definer.rb → position_column_method_definer.rb} +39 -18
- data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +14 -0
- data/lib/acts_as_list/version.rb +1 -1
- data/test/helper.rb +27 -1
- data/test/test_joined_list.rb +1 -12
- data/test/test_list.rb +2 -27
- data/test/test_no_update_for_extra_classes.rb +104 -0
- data/test/test_no_update_for_scope_destruction.rb +72 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4602fbf2208d5b39ba3a0fe78b4ab3ad0947e69a
|
4
|
+
data.tar.gz: f3ef72c6695c6441366c657f41a3d8b273e7c588
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 499b30c7a96fb04f2ab8c66414c7165686652b4969692a1e82a3af482b6f54e49d2270ed902466721874d0fcbe144cbc541fad09b55f22c385a0f181ffca8b3d
|
7
|
+
data.tar.gz: 38ae417b51053320bf227a3be9b44c8de46f754990bd4d74e427c47da6502e282e20c02ce4a47dc0466ee488c73fec14f8b44af660645379a412c9632ed2e29b
|
data/.travis.yml
CHANGED
@@ -15,6 +15,7 @@ rvm:
|
|
15
15
|
- 2.1.9
|
16
16
|
- 2.2.6
|
17
17
|
- 2.3.3
|
18
|
+
- 2.4.0
|
18
19
|
- jruby-19mode
|
19
20
|
env:
|
20
21
|
- DB=sqlite
|
@@ -25,13 +26,26 @@ gemfile:
|
|
25
26
|
- gemfiles/rails_4_1.gemfile
|
26
27
|
- gemfiles/rails_4_2.gemfile
|
27
28
|
- gemfiles/rails_5_0.gemfile
|
29
|
+
- gemfiles/rails_5_1.gemfile
|
28
30
|
matrix:
|
29
31
|
exclude:
|
30
32
|
- rvm: 1.9.3
|
31
33
|
gemfile: gemfiles/rails_5_0.gemfile
|
34
|
+
- rvm: 1.9.3
|
35
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
32
36
|
- rvm: 2.0.0
|
33
37
|
gemfile: gemfiles/rails_5_0.gemfile
|
38
|
+
- rvm: 2.0.0
|
39
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
34
40
|
- rvm: 2.1.9
|
35
41
|
gemfile: gemfiles/rails_5_0.gemfile
|
42
|
+
- rvm: 2.1.9
|
43
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
44
|
+
- rvm: 2.4.0
|
45
|
+
gemfile: gemfiles/rails_3_2.gemfile
|
46
|
+
- rvm: 2.4.0
|
47
|
+
gemfile: gemfiles/rails_4_1.gemfile
|
36
48
|
- rvm: jruby-19mode
|
37
49
|
gemfile: gemfiles/rails_5_0.gemfile
|
50
|
+
- rvm: jruby-19mode
|
51
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.9.2](https://github.com/swanandp/acts_as_list/tree/v0.9.2) (2017-02-07)
|
4
|
+
[Full Changelog](https://github.com/swanandp/acts_as_list/compare/v0.9.1...v0.9.2)
|
5
|
+
|
6
|
+
**Closed issues:**
|
7
|
+
|
8
|
+
- Getting invalid input syntax for uuid [\#253](https://github.com/swanandp/acts_as_list/issues/253)
|
9
|
+
|
3
10
|
## [v0.9.1](https://github.com/swanandp/acts_as_list/tree/v0.9.1) (2017-01-26)
|
4
11
|
[Full Changelog](https://github.com/swanandp/acts_as_list/compare/v0.9.0...v0.9.1)
|
5
12
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -27,27 +27,27 @@ At first, you need to add a `position` column to desired table:
|
|
27
27
|
|
28
28
|
rails g migration AddPositionToTodoItem position:integer
|
29
29
|
rake db:migrate
|
30
|
-
|
31
|
-
After that you can use `acts_as_list` method in the model:
|
30
|
+
|
31
|
+
After that you can use `acts_as_list` method in the model:
|
32
32
|
|
33
33
|
```ruby
|
34
34
|
class TodoList < ActiveRecord::Base
|
35
35
|
has_many :todo_items, -> { order(position: :asc) }
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
class TodoItem < ActiveRecord::Base
|
39
39
|
belongs_to :todo_list
|
40
40
|
acts_as_list scope: :todo_list
|
41
41
|
end
|
42
42
|
|
43
|
-
todo_list = TodoList.find(...)
|
43
|
+
todo_list = TodoList.find(...)
|
44
44
|
todo_list.todo_items.first.move_to_bottom
|
45
45
|
todo_list.todo_items.last.move_higher
|
46
46
|
```
|
47
47
|
|
48
48
|
## Instance Methods Added To ActiveRecord Models
|
49
49
|
|
50
|
-
You'll have a number of methods added to each instance of the ActiveRecord model that to which `acts_as_list` is added.
|
50
|
+
You'll have a number of methods added to each instance of the ActiveRecord model that to which `acts_as_list` is added.
|
51
51
|
|
52
52
|
In `acts_as_list`, "higher" means further up the list (a lower `position`), and "lower" means further down the list (a higher `position`). That can be confusing, so it might make sense to add tests that validate that you're using the right method given your context.
|
53
53
|
|
@@ -132,7 +132,34 @@ TodoItem.acts_as_list_no_update do
|
|
132
132
|
end
|
133
133
|
```
|
134
134
|
In an `acts_as_list_no_update` block, all callbacks are disabled, and positions are not updated. New records will be created with
|
135
|
-
the default value from the database. It is your responsibility to correctly manage `positions` values.
|
135
|
+
the default value from the database. It is your responsibility to correctly manage `positions` values.
|
136
|
+
|
137
|
+
You can also pass an array of classes as an argument to disable database updates on just those classes. It can be any ActiveRecord class that has acts_as_list enabled.
|
138
|
+
```ruby
|
139
|
+
class TodoList < ActiveRecord::Base
|
140
|
+
has_many :todo_items, -> { order(position: :asc) }
|
141
|
+
acts_as_list
|
142
|
+
end
|
143
|
+
|
144
|
+
class TodoItem < ActiveRecord::Base
|
145
|
+
belongs_to :todo_list
|
146
|
+
has_many :todo_attachments, -> { order(position: :asc) }
|
147
|
+
|
148
|
+
acts_as_list scope: :todo_list
|
149
|
+
end
|
150
|
+
|
151
|
+
class TodoAttachment < ActiveRecord::Base
|
152
|
+
belongs_to :todo_list
|
153
|
+
acts_as_list scope: :todo_item
|
154
|
+
end
|
155
|
+
|
156
|
+
TodoItem.acts_as_list_no_update([TodoAttachment]) do
|
157
|
+
TodoItem.find(10).update(position: 2)
|
158
|
+
TodoAttachment.find(10).update(position: 1)
|
159
|
+
TodoAttachment.find(11).update(position: 2)
|
160
|
+
TodoList.find(2).update(position: 3) # For this instance the callbacks will be called because we haven't passed the class as an argument
|
161
|
+
end
|
162
|
+
```
|
136
163
|
|
137
164
|
## Versions
|
138
165
|
Version `0.9.0` adds `acts_as_list_no_update` (https://github.com/swanandp/acts_as_list/pull/244) and compatibility with not-null and uniqueness constraints on the database (https://github.com/swanandp/acts_as_list/pull/246). These additions shouldn't break compatibility with existing implementations.
|
@@ -152,7 +179,7 @@ All versions `0.1.5` onwards require Rails 3.0.x and higher.
|
|
152
179
|
1. Sort based feature
|
153
180
|
|
154
181
|
## Contributing to `acts_as_list`
|
155
|
-
|
182
|
+
|
156
183
|
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
157
184
|
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
158
185
|
- Fork the project
|
data/gemfiles/rails_3_2.gemfile
CHANGED
data/gemfiles/rails_4_1.gemfile
CHANGED
data/gemfiles/rails_4_2.gemfile
CHANGED
data/gemfiles/rails_5_0.gemfile
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "http://rubygems.org"
|
4
|
+
|
5
|
+
gem "rack", "~> 1", :platforms => [:ruby_19, :ruby_20, :ruby_21, :jruby]
|
6
|
+
gem "rake"
|
7
|
+
gem "appraisal"
|
8
|
+
gem "github_changelog_generator", "1.9.0"
|
9
|
+
gem "activerecord", "~> 5.1.0.beta1"
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem "minitest", "~> 5.0"
|
13
|
+
gem "test_after_commit", "~> 0.4.2"
|
14
|
+
gem "timecop"
|
15
|
+
gem "mocha"
|
16
|
+
end
|
17
|
+
|
18
|
+
group :sqlite do
|
19
|
+
gem "sqlite3", :platforms => [:ruby]
|
20
|
+
gem "activerecord-jdbcsqlite3-adapter", :platforms => [:jruby]
|
21
|
+
end
|
22
|
+
|
23
|
+
group :postgresql do
|
24
|
+
gem "pg", "~> 0.18.0", :platforms => [:ruby]
|
25
|
+
gem "activerecord-jdbcpostgresql-adapter", :platforms => [:jruby]
|
26
|
+
end
|
27
|
+
|
28
|
+
group :mysql do
|
29
|
+
gem "mysql2", "~> 0.3.10", :platforms => [:ruby]
|
30
|
+
gem "activerecord-jdbcmysql-adapter", :platforms => [:jruby]
|
31
|
+
end
|
32
|
+
|
33
|
+
gemspec :path => "../"
|
data/lib/acts_as_list.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'acts_as_list/active_record/acts/list'
|
2
|
-
require "acts_as_list/active_record/acts/
|
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"
|
@@ -3,8 +3,8 @@ module ActiveRecord::Acts::List::CallbackDefiner #:nodoc:
|
|
3
3
|
caller_class.class_eval do
|
4
4
|
before_validation :check_top_position, unless: :act_as_list_no_update?
|
5
5
|
|
6
|
-
before_destroy :lock
|
7
|
-
after_destroy :decrement_positions_on_lower_items, unless:
|
6
|
+
before_destroy :lock!, unless: "destroyed_via_scope? || act_as_list_no_update?"
|
7
|
+
after_destroy :decrement_positions_on_lower_items, unless: "destroyed_via_scope? || act_as_list_no_update?"
|
8
8
|
|
9
9
|
before_update :check_scope, unless: :act_as_list_no_update?
|
10
10
|
after_update :update_positions, unless: :act_as_list_no_update?
|
@@ -20,7 +20,7 @@ class << ActiveRecord::Base
|
|
20
20
|
|
21
21
|
caller_class = self
|
22
22
|
|
23
|
-
ActiveRecord::Acts::List::
|
23
|
+
ActiveRecord::Acts::List::PositionColumnMethodDefiner.call(caller_class, configuration[:column])
|
24
24
|
ActiveRecord::Acts::List::ScopeMethodDefiner.call(caller_class, configuration[:scope])
|
25
25
|
ActiveRecord::Acts::List::TopOfListMethodDefiner.call(caller_class, configuration[:top_of_list])
|
26
26
|
ActiveRecord::Acts::List::AddNewAtMethodDefiner.call(caller_class, configuration[:add_new_at])
|
@@ -393,7 +393,7 @@ module ActiveRecord
|
|
393
393
|
end
|
394
394
|
|
395
395
|
def update_positions
|
396
|
-
old_position =
|
396
|
+
old_position = position_before_save || bottom_position_in_list + 1
|
397
397
|
new_position = send(position_column).to_i
|
398
398
|
|
399
399
|
return unless acts_as_list_list.where(
|
@@ -402,6 +402,14 @@ module ActiveRecord
|
|
402
402
|
shuffle_positions_on_intermediate_items old_position, new_position, id
|
403
403
|
end
|
404
404
|
|
405
|
+
def position_before_save
|
406
|
+
if ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 1
|
407
|
+
send("#{position_column}_before_last_save")
|
408
|
+
else
|
409
|
+
send("#{position_column}_was")
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
405
413
|
def internal_scope_changed?
|
406
414
|
return @scope_changed if defined?(@scope_changed)
|
407
415
|
|
@@ -4,39 +4,99 @@ module ActiveRecord
|
|
4
4
|
module NoUpdate
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
+
class ArrayTypeError < SyntaxError
|
8
|
+
def initialize
|
9
|
+
super("The first argument must be an array")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class DisparityClassesError < NotImplementedError
|
14
|
+
def initialize
|
15
|
+
super("The first argument should contain ActiveRecord or ApplicationRecord classes")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
7
19
|
module ClassMethods
|
8
20
|
# Lets you selectively disable all act_as_list database updates
|
9
21
|
# for the duration of a block.
|
10
22
|
#
|
11
23
|
# ==== Examples
|
12
|
-
# ActiveRecord::Acts::List.acts_as_list_no_update do
|
13
|
-
# TodoList....
|
14
|
-
# end
|
15
24
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
25
|
+
# class TodoList < ActiveRecord::Base
|
26
|
+
# has_many :todo_items, -> { order(position: :asc) }
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class TodoItem < ActiveRecord::Base
|
30
|
+
# belongs_to :todo_list
|
31
|
+
#
|
32
|
+
# acts_as_list scope: :todo_list
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# TodoItem.acts_as_list_no_update do
|
36
|
+
# TodoList.first.update(position: 2)
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# You can also pass an array of classes as an argument to disable database updates on just those classes.
|
40
|
+
# It can be any ActiveRecord class that has acts_as_list enabled.
|
41
|
+
#
|
42
|
+
# ==== Examples
|
43
|
+
#
|
44
|
+
# class TodoList < ActiveRecord::Base
|
45
|
+
# has_many :todo_items, -> { order(position: :asc) }
|
46
|
+
# acts_as_list
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# class TodoItem < ActiveRecord::Base
|
50
|
+
# belongs_to :todo_list
|
51
|
+
# has_many :todo_attachments, -> { order(position: :asc) }
|
52
|
+
#
|
53
|
+
# acts_as_list scope: :todo_list
|
54
|
+
# end
|
19
55
|
#
|
20
|
-
|
21
|
-
|
56
|
+
# class TodoAttachment < ActiveRecord::Base
|
57
|
+
# belongs_to :todo_list
|
58
|
+
# acts_as_list scope: :todo_item
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# TodoItem.acts_as_list_no_update([TodoAttachment]) do
|
62
|
+
# TodoItem.find(10).update(position: 2)
|
63
|
+
# TodoAttachment.find(10).update(position: 1)
|
64
|
+
# TodoAttachment.find(11).update(position: 2)
|
65
|
+
# TodoList.find(2).update(position: 3) # For this instance the callbacks will be called because we haven't passed the class as an argument
|
66
|
+
# end
|
67
|
+
|
68
|
+
def acts_as_list_no_update(extra_classes = [], &block)
|
69
|
+
return raise ArrayTypeError unless extra_classes.is_a?(Array)
|
70
|
+
|
71
|
+
extra_classes << self
|
72
|
+
|
73
|
+
return raise DisparityClassesError unless active_record_objects?(extra_classes)
|
74
|
+
|
75
|
+
NoUpdate.apply_to(extra_classes, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def active_record_objects?(extra_classes)
|
81
|
+
extra_classes.all? { |klass| klass.ancestors.include? ActiveRecord::Base }
|
22
82
|
end
|
23
83
|
end
|
24
84
|
|
25
85
|
class << self
|
26
|
-
def apply_to(
|
27
|
-
|
86
|
+
def apply_to(klasses)
|
87
|
+
extracted_klasses.push(*klasses)
|
28
88
|
yield
|
29
89
|
ensure
|
30
|
-
|
90
|
+
extracted_klasses.clear
|
31
91
|
end
|
32
92
|
|
33
93
|
def applied_to?(klass)
|
34
|
-
|
94
|
+
extracted_klasses.any? { |k| k == klass }
|
35
95
|
end
|
36
96
|
|
37
97
|
private
|
38
98
|
|
39
|
-
def
|
99
|
+
def extracted_klasses
|
40
100
|
Thread.current[:act_as_list_no_update] ||= []
|
41
101
|
end
|
42
102
|
end
|
@@ -1,25 +1,19 @@
|
|
1
|
-
module ActiveRecord::Acts::List::
|
2
|
-
def self.call(caller_class,
|
3
|
-
caller_class
|
4
|
-
|
5
|
-
|
6
|
-
define_method :position_column do
|
7
|
-
column
|
8
|
-
end
|
1
|
+
module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
2
|
+
def self.call(caller_class, position_column)
|
3
|
+
define_class_methods(caller_class, position_column)
|
4
|
+
define_instance_methods(caller_class, position_column)
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
if mass_assignment_protection_was_used_by_user?(caller_class)
|
7
|
+
protect_attributes_from_mass_assignment(caller_class, position_column)
|
8
|
+
end
|
9
|
+
end
|
14
10
|
|
15
|
-
|
16
|
-
# if the class has some mass_assignment_protection
|
17
|
-
if defined?(accessible_attributes) and !accessible_attributes.blank?
|
18
|
-
attr_accessible :"#{column}"
|
19
|
-
end
|
11
|
+
private
|
20
12
|
|
13
|
+
def self.define_class_methods(caller_class, position_column)
|
14
|
+
caller_class.class_eval do
|
21
15
|
define_singleton_method :quoted_position_column do
|
22
|
-
@_quoted_position_column ||= connection.quote_column_name(
|
16
|
+
@_quoted_position_column ||= connection.quote_column_name(position_column)
|
23
17
|
end
|
24
18
|
|
25
19
|
define_singleton_method :quoted_position_column_with_table_name do
|
@@ -47,4 +41,31 @@ module ActiveRecord::Acts::List::ColumnMethodDefiner #:nodoc:
|
|
47
41
|
end
|
48
42
|
end
|
49
43
|
end
|
44
|
+
|
45
|
+
def self.define_instance_methods(caller_class, position_column)
|
46
|
+
caller_class.class_eval do
|
47
|
+
attr_reader :position_changed
|
48
|
+
|
49
|
+
define_method :position_column do
|
50
|
+
position_column
|
51
|
+
end
|
52
|
+
|
53
|
+
define_method :"#{position_column}=" do |position|
|
54
|
+
write_attribute(position_column, position)
|
55
|
+
@position_changed = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.mass_assignment_protection_was_used_by_user?(caller_class)
|
61
|
+
caller_class.class_eval do
|
62
|
+
respond_to?(:accessible_attributes) and accessible_attributes.present?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.protect_attributes_from_mass_assignment(caller_class, position_column)
|
67
|
+
caller_class.class_eval do
|
68
|
+
attr_accessible position_column.to_sym
|
69
|
+
end
|
70
|
+
end
|
50
71
|
end
|
@@ -17,6 +17,11 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
17
17
|
define_method :scope_changed? do
|
18
18
|
changed.include?(scope_name.to_s)
|
19
19
|
end
|
20
|
+
|
21
|
+
define_method :destroyed_via_scope? do
|
22
|
+
return false if ActiveRecord::VERSION::MAJOR < 4
|
23
|
+
scope == (destroyed_by_association && destroyed_by_association.foreign_key.to_sym)
|
24
|
+
end
|
20
25
|
elsif scope.is_a?(Array)
|
21
26
|
define_method :scope_condition do
|
22
27
|
scope.inject({}) do |hash, column|
|
@@ -27,6 +32,11 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
27
32
|
define_method :scope_changed? do
|
28
33
|
(scope_condition.keys & changed.map(&:to_sym)).any?
|
29
34
|
end
|
35
|
+
|
36
|
+
define_method :destroyed_via_scope? do
|
37
|
+
return false if ActiveRecord::VERSION::MAJOR < 4
|
38
|
+
scope_condition.keys.include? (destroyed_by_association && destroyed_by_association.foreign_key.to_sym)
|
39
|
+
end
|
30
40
|
else
|
31
41
|
define_method :scope_condition do
|
32
42
|
eval "%{#{scope}}"
|
@@ -35,6 +45,10 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
35
45
|
define_method :scope_changed? do
|
36
46
|
false
|
37
47
|
end
|
48
|
+
|
49
|
+
define_method :destroyed_via_scope? do
|
50
|
+
false
|
51
|
+
end
|
38
52
|
end
|
39
53
|
|
40
54
|
self.scope :in_list, lambda { where("#{quoted_position_column_with_table_name} IS NOT NULL") }
|
data/lib/acts_as_list/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -11,6 +11,7 @@ rescue Bundler::BundlerError => e
|
|
11
11
|
end
|
12
12
|
require "active_record"
|
13
13
|
require "minitest/autorun"
|
14
|
+
require "mocha/mini_test"
|
14
15
|
require "#{File.dirname(__FILE__)}/../init"
|
15
16
|
|
16
17
|
if defined?(ActiveRecord::VERSION) &&
|
@@ -20,6 +21,31 @@ if defined?(ActiveRecord::VERSION) &&
|
|
20
21
|
ActiveRecord::Base.raise_in_transactional_callbacks = true
|
21
22
|
end
|
22
23
|
|
24
|
+
db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
|
25
|
+
ActiveRecord::Base.establish_connection(db_config)
|
26
|
+
ActiveRecord::Schema.verbose = false
|
27
|
+
|
28
|
+
# Returns true if ActiveRecord is rails 3, 4 version
|
29
|
+
def rails_3
|
30
|
+
defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 3
|
31
|
+
end
|
32
|
+
|
33
|
+
def rails_4
|
34
|
+
defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 4
|
35
|
+
end
|
36
|
+
|
37
|
+
def teardown_db
|
38
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
39
|
+
tables = ActiveRecord::Base.connection.data_sources
|
40
|
+
else
|
41
|
+
tables = ActiveRecord::Base.connection.tables
|
42
|
+
end
|
43
|
+
|
44
|
+
tables.each do |table|
|
45
|
+
ActiveRecord::Base.connection.drop_table(table)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
23
49
|
require "shared"
|
24
50
|
|
25
51
|
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
@@ -30,4 +56,4 @@ def assert_equal_or_nil(a, b)
|
|
30
56
|
else
|
31
57
|
assert_equal a, b
|
32
58
|
end
|
33
|
-
end
|
59
|
+
end
|
data/test/test_joined_list.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
|
4
|
-
ActiveRecord::Base.establish_connection(db_config)
|
5
|
-
ActiveRecord::Schema.verbose = false
|
6
|
-
|
7
3
|
class Section < ActiveRecord::Base
|
8
4
|
has_many :items
|
9
5
|
acts_as_list
|
@@ -37,14 +33,7 @@ class JoinedTestCase < Minitest::Test
|
|
37
33
|
end
|
38
34
|
|
39
35
|
def teardown
|
40
|
-
|
41
|
-
tables = ActiveRecord::Base.connection.data_sources
|
42
|
-
else
|
43
|
-
tables = ActiveRecord::Base.connection.tables
|
44
|
-
end
|
45
|
-
tables.each do |table|
|
46
|
-
ActiveRecord::Base.connection.drop_table(table)
|
47
|
-
end
|
36
|
+
teardown_db
|
48
37
|
super
|
49
38
|
end
|
50
39
|
end
|
data/test/test_list.rb
CHANGED
@@ -1,13 +1,9 @@
|
|
1
1
|
# NOTE: following now done in helper.rb (better Readability)
|
2
2
|
require 'helper'
|
3
3
|
|
4
|
-
db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
|
5
|
-
ActiveRecord::Base.establish_connection(db_config)
|
6
|
-
ActiveRecord::Schema.verbose = false
|
7
|
-
|
8
4
|
def setup_db(position_options = {})
|
9
5
|
$default_position = position_options[:default]
|
10
|
-
|
6
|
+
|
11
7
|
# sqlite cannot drop/rename/alter columns and add constraints after table creation
|
12
8
|
sqlite = ENV.fetch("DB", "sqlite") == "sqlite"
|
13
9
|
|
@@ -25,7 +21,7 @@ def setup_db(position_options = {})
|
|
25
21
|
if position_options[:unique] && !(sqlite && position_options[:positive])
|
26
22
|
ActiveRecord::Base.connection.add_index :mixins, :pos, unique: true
|
27
23
|
end
|
28
|
-
|
24
|
+
|
29
25
|
if position_options[:positive]
|
30
26
|
if sqlite
|
31
27
|
# SQLite cannot add constraint after table creation, also cannot add unique inside ADD COLUMN
|
@@ -57,27 +53,6 @@ def setup_db_with_default
|
|
57
53
|
setup_db default: 0
|
58
54
|
end
|
59
55
|
|
60
|
-
# Returns true if ActiveRecord is rails3,4 version
|
61
|
-
def rails_3
|
62
|
-
defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 3
|
63
|
-
end
|
64
|
-
|
65
|
-
def rails_4
|
66
|
-
defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 4
|
67
|
-
end
|
68
|
-
|
69
|
-
def teardown_db
|
70
|
-
if ActiveRecord::VERSION::MAJOR >= 5
|
71
|
-
tables = ActiveRecord::Base.connection.data_sources
|
72
|
-
else
|
73
|
-
tables = ActiveRecord::Base.connection.tables
|
74
|
-
end
|
75
|
-
|
76
|
-
tables.each do |table|
|
77
|
-
ActiveRecord::Base.connection.drop_table(table)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
56
|
class Mixin < ActiveRecord::Base
|
82
57
|
self.table_name = 'mixins'
|
83
58
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TodoList < ActiveRecord::Base
|
4
|
+
has_many :todo_items
|
5
|
+
acts_as_list
|
6
|
+
end
|
7
|
+
|
8
|
+
class TodoItem < ActiveRecord::Base
|
9
|
+
belongs_to :todo_list
|
10
|
+
has_many :todo_item_attachments
|
11
|
+
acts_as_list scope: :todo_list
|
12
|
+
end
|
13
|
+
|
14
|
+
class TodoItemAttachment < ActiveRecord::Base
|
15
|
+
belongs_to :todo_item
|
16
|
+
acts_as_list scope: :todo_item
|
17
|
+
end
|
18
|
+
|
19
|
+
class NoUpdateForCollectionClassesTestCase < Minitest::Test
|
20
|
+
def setup
|
21
|
+
ActiveRecord::Base.connection.create_table :todo_lists do |t|
|
22
|
+
t.column :position, :integer
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::Base.connection.create_table :todo_items do |t|
|
26
|
+
t.column :position, :integer
|
27
|
+
t.column :todo_list_id, :integer
|
28
|
+
end
|
29
|
+
|
30
|
+
ActiveRecord::Base.connection.create_table :todo_item_attachments do |t|
|
31
|
+
t.column :position, :integer
|
32
|
+
t.column :todo_item_id, :integer
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveRecord::Base.connection.schema_cache.clear!
|
36
|
+
[TodoList, TodoItem, TodoItemAttachment].each(&:reset_column_information)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def teardown
|
41
|
+
teardown_db
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class NoUpdateForCollectionClassesTest < NoUpdateForCollectionClassesTestCase
|
47
|
+
def setup
|
48
|
+
super
|
49
|
+
@list_1, @list_2 = (1..2).map { |counter| TodoList.create!(position: counter) }
|
50
|
+
|
51
|
+
@item_1, @item_2 = (1..2).map { |counter| TodoItem.create!(position: counter, todo_list_id: @list_1.id) }
|
52
|
+
@attachment_1, @attachment_2 = (1..2).map { |counter| TodoItemAttachment.create!(position: counter, todo_item_id: @item_1.id) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_update
|
56
|
+
@item_1.update_attributes(position: 2)
|
57
|
+
assert_equal 2, @item_1.reload.position
|
58
|
+
assert_equal 1, @item_2.reload.position
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_no_update_for_single_class_instances
|
62
|
+
TodoItem.acts_as_list_no_update { @item_1.update_attributes(position: 2) }
|
63
|
+
|
64
|
+
assert_equal 2, @item_1.reload.position
|
65
|
+
assert_equal 2, @item_2.reload.position
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_no_update_for_different_class_instances
|
69
|
+
TodoItem.acts_as_list_no_update([TodoItemAttachment]) { update_records! }
|
70
|
+
|
71
|
+
assert_equal 2, @item_1.reload.position
|
72
|
+
assert_equal 2, @item_2.reload.position
|
73
|
+
|
74
|
+
assert_equal 2, @attachment_1.reload.position
|
75
|
+
assert_equal 2, @attachment_2.reload.position
|
76
|
+
|
77
|
+
assert_equal 2, @list_1.reload.position
|
78
|
+
assert_equal 1, @list_2.reload.position
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_raising_array_type_error
|
82
|
+
exception = assert_raises ActiveRecord::Acts::List::NoUpdate::ArrayTypeError do
|
83
|
+
TodoList.acts_as_list_no_update(nil)
|
84
|
+
end
|
85
|
+
|
86
|
+
assert_equal("The first argument must be an array", exception.message )
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_non_disparity_classes_error
|
90
|
+
exception = assert_raises ActiveRecord::Acts::List::NoUpdate::DisparityClassesError do
|
91
|
+
TodoList.acts_as_list_no_update([Class])
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal("The first argument should contain ActiveRecord or ApplicationRecord classes", exception.message )
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def update_records!
|
100
|
+
@item_1.update_attributes(position: 2)
|
101
|
+
@attachment_1.update_attributes(position: 2)
|
102
|
+
@list_1.update_attributes(position: 2)
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class DestructionTodoList < ActiveRecord::Base
|
4
|
+
has_many :destruction_todo_items, dependent: :destroy
|
5
|
+
has_many :destruction_tada_items, dependent: :destroy
|
6
|
+
end
|
7
|
+
|
8
|
+
class DestructionTodoItem < ActiveRecord::Base
|
9
|
+
belongs_to :destruction_todo_list
|
10
|
+
acts_as_list scope: :destruction_todo_list
|
11
|
+
end
|
12
|
+
|
13
|
+
class DestructionTadaItem < ActiveRecord::Base
|
14
|
+
belongs_to :destruction_todo_list
|
15
|
+
acts_as_list scope: [:destruction_todo_list_id, :enabled]
|
16
|
+
end
|
17
|
+
|
18
|
+
class NoUpdateForScopeDestructionTestCase < Minitest::Test
|
19
|
+
def setup
|
20
|
+
ActiveRecord::Base.connection.create_table :destruction_todo_lists do |t|
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveRecord::Base.connection.create_table :destruction_todo_items do |t|
|
24
|
+
t.column :position, :integer
|
25
|
+
t.column :destruction_todo_list_id, :integer
|
26
|
+
end
|
27
|
+
|
28
|
+
ActiveRecord::Base.connection.create_table :destruction_tada_items do |t|
|
29
|
+
t.column :position, :integer
|
30
|
+
t.column :destruction_todo_list_id, :integer
|
31
|
+
t.column :enabled, :boolean
|
32
|
+
end
|
33
|
+
|
34
|
+
ActiveRecord::Base.connection.schema_cache.clear!
|
35
|
+
[DestructionTodoList, DestructionTodoItem, DestructionTadaItem].each(&:reset_column_information)
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def teardown
|
40
|
+
teardown_db
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
class NoUpdateForScopeDestructionTest < NoUpdateForScopeDestructionTestCase
|
45
|
+
def setup
|
46
|
+
super
|
47
|
+
@list = DestructionTodoList.create!
|
48
|
+
|
49
|
+
@todo_item_1 = DestructionTodoItem.create! position: 1, destruction_todo_list_id: @list.id
|
50
|
+
@tada_item_1 = DestructionTadaItem.create! position: 1, destruction_todo_list_id: @list.id, enabled: true
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_no_update_children_when_parent_destroyed
|
54
|
+
if ActiveRecord::VERSION::MAJOR < 4
|
55
|
+
DestructionTodoItem.any_instance.expects(:decrement_positions_on_lower_items).once
|
56
|
+
DestructionTadaItem.any_instance.expects(:decrement_positions_on_lower_items).once
|
57
|
+
else
|
58
|
+
DestructionTodoItem.any_instance.expects(:decrement_positions_on_lower_items).never
|
59
|
+
DestructionTadaItem.any_instance.expects(:decrement_positions_on_lower_items).never
|
60
|
+
end
|
61
|
+
assert @list.destroy
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_update_children_when_sibling_destroyed
|
65
|
+
@todo_item_1.expects(:decrement_positions_on_lower_items).once
|
66
|
+
@tada_item_1.expects(:decrement_positions_on_lower_items).once
|
67
|
+
assert @todo_item_1.destroy
|
68
|
+
assert @tada_item_1.destroy
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
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.9.
|
4
|
+
version: 0.9.3
|
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-03-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -63,14 +63,15 @@ files:
|
|
63
63
|
- gemfiles/rails_4_1.gemfile
|
64
64
|
- gemfiles/rails_4_2.gemfile
|
65
65
|
- gemfiles/rails_5_0.gemfile
|
66
|
+
- gemfiles/rails_5_1.gemfile
|
66
67
|
- init.rb
|
67
68
|
- lib/acts_as_list.rb
|
68
69
|
- lib/acts_as_list/active_record/acts/add_new_at_method_definer.rb
|
69
70
|
- lib/acts_as_list/active_record/acts/aux_method_definer.rb
|
70
71
|
- lib/acts_as_list/active_record/acts/callback_definer.rb
|
71
|
-
- lib/acts_as_list/active_record/acts/column_method_definer.rb
|
72
72
|
- lib/acts_as_list/active_record/acts/list.rb
|
73
73
|
- lib/acts_as_list/active_record/acts/no_update.rb
|
74
|
+
- lib/acts_as_list/active_record/acts/position_column_method_definer.rb
|
74
75
|
- lib/acts_as_list/active_record/acts/scope_method_definer.rb
|
75
76
|
- lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb
|
76
77
|
- lib/acts_as_list/active_record/acts/top_of_list_method_definer.rb
|
@@ -87,6 +88,8 @@ files:
|
|
87
88
|
- test/shared_zero_based.rb
|
88
89
|
- test/test_joined_list.rb
|
89
90
|
- test/test_list.rb
|
91
|
+
- test/test_no_update_for_extra_classes.rb
|
92
|
+
- test/test_no_update_for_scope_destruction.rb
|
90
93
|
homepage: http://github.com/swanandp/acts_as_list
|
91
94
|
licenses:
|
92
95
|
- MIT
|
@@ -125,3 +128,5 @@ test_files:
|
|
125
128
|
- test/shared_zero_based.rb
|
126
129
|
- test/test_joined_list.rb
|
127
130
|
- test/test_list.rb
|
131
|
+
- test/test_no_update_for_extra_classes.rb
|
132
|
+
- test/test_no_update_for_scope_destruction.rb
|