acts_as_list 0.7.4 → 1.1.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 +5 -13
- data/.github/FUNDING.yml +3 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +123 -0
- data/.gitignore +1 -0
- data/.travis.yml +50 -12
- data/Appraisals +39 -6
- data/CHANGELOG.md +565 -148
- data/Gemfile +19 -14
- data/README.md +206 -19
- data/Rakefile +4 -4
- data/acts_as_list.gemspec +16 -11
- data/gemfiles/rails_4_2.gemfile +18 -9
- data/gemfiles/rails_5_0.gemfile +31 -0
- data/gemfiles/rails_5_1.gemfile +31 -0
- data/gemfiles/rails_5_2.gemfile +31 -0
- data/gemfiles/rails_6_0.gemfile +31 -0
- data/gemfiles/rails_6_1.gemfile +31 -0
- data/gemfiles/rails_7_0.gemfile +31 -0
- data/init.rb +2 -0
- data/lib/acts_as_list/active_record/acts/active_record.rb +5 -0
- data/lib/acts_as_list/active_record/acts/add_new_at_method_definer.rb +11 -0
- data/lib/acts_as_list/active_record/acts/aux_method_definer.rb +11 -0
- data/lib/acts_as_list/active_record/acts/callback_definer.rb +19 -0
- data/lib/acts_as_list/active_record/acts/list.rb +299 -306
- data/lib/acts_as_list/active_record/acts/no_update.rb +125 -0
- data/lib/acts_as_list/active_record/acts/position_column_method_definer.rb +101 -0
- data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +77 -0
- data/lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb +28 -0
- data/lib/acts_as_list/active_record/acts/top_of_list_method_definer.rb +15 -0
- data/lib/acts_as_list/version.rb +3 -1
- data/lib/acts_as_list.rb +11 -14
- data/test/database.yml +18 -0
- data/test/helper.rb +50 -2
- data/test/shared.rb +3 -0
- data/test/shared_array_scope_list.rb +21 -4
- data/test/shared_list.rb +86 -12
- data/test/shared_list_sub.rb +63 -2
- data/test/shared_no_addition.rb +50 -2
- data/test/shared_quoting.rb +23 -0
- data/test/shared_top_addition.rb +36 -13
- data/test/shared_zero_based.rb +13 -0
- data/test/test_default_scope_with_select.rb +33 -0
- data/test/test_joined_list.rb +61 -0
- data/test/test_list.rb +601 -84
- data/test/test_no_update_for_extra_classes.rb +131 -0
- data/test/test_no_update_for_scope_destruction.rb +69 -0
- data/test/test_no_update_for_subclasses.rb +56 -0
- data/test/test_scope_with_user_defined_foreign_key.rb +42 -0
- metadata +56 -22
- data/gemfiles/rails_3_2.gemfile +0 -24
- data/gemfiles/rails_4_1.gemfile +0 -24
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Acts
|
5
|
+
module List
|
6
|
+
module NoUpdate
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
class ArrayTypeError < ArgumentError
|
13
|
+
def initialize
|
14
|
+
super("The first argument must be an array")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class DisparityClassesError < ArgumentError
|
19
|
+
def initialize
|
20
|
+
super("The first argument should contain ActiveRecord or ApplicationRecord classes")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# Lets you selectively disable all act_as_list database updates
|
26
|
+
# for the duration of a block.
|
27
|
+
#
|
28
|
+
# ==== Examples
|
29
|
+
#
|
30
|
+
# class TodoList < ActiveRecord::Base
|
31
|
+
# has_many :todo_items, -> { order(position: :asc) }
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# class TodoItem < ActiveRecord::Base
|
35
|
+
# belongs_to :todo_list
|
36
|
+
#
|
37
|
+
# acts_as_list scope: :todo_list
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# TodoItem.acts_as_list_no_update do
|
41
|
+
# TodoList.first.update(position: 2)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# You can also pass an array of classes as an argument to disable database updates on just those classes.
|
45
|
+
# It can be any ActiveRecord class that has acts_as_list enabled.
|
46
|
+
#
|
47
|
+
# ==== Examples
|
48
|
+
#
|
49
|
+
# class TodoList < ActiveRecord::Base
|
50
|
+
# has_many :todo_items, -> { order(position: :asc) }
|
51
|
+
# acts_as_list
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# class TodoItem < ActiveRecord::Base
|
55
|
+
# belongs_to :todo_list
|
56
|
+
# has_many :todo_attachments, -> { order(position: :asc) }
|
57
|
+
#
|
58
|
+
# acts_as_list scope: :todo_list
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# class TodoAttachment < ActiveRecord::Base
|
62
|
+
# belongs_to :todo_list
|
63
|
+
# acts_as_list scope: :todo_item
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# TodoItem.acts_as_list_no_update([TodoAttachment]) do
|
67
|
+
# TodoItem.find(10).update(position: 2)
|
68
|
+
# TodoAttachment.find(10).update(position: 1)
|
69
|
+
# TodoAttachment.find(11).update(position: 2)
|
70
|
+
# TodoList.find(2).update(position: 3) # For this instance the callbacks will be called because we haven't passed the class as an argument
|
71
|
+
# end
|
72
|
+
|
73
|
+
def acts_as_list_no_update(extra_classes = [], &block)
|
74
|
+
return raise ArrayTypeError unless extra_classes.is_a?(Array)
|
75
|
+
|
76
|
+
extra_classes << self
|
77
|
+
|
78
|
+
return raise DisparityClassesError unless active_record_objects?(extra_classes)
|
79
|
+
|
80
|
+
NoUpdate.apply_to(extra_classes, &block)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def active_record_objects?(extra_classes)
|
86
|
+
extra_classes.all? { |klass| klass.ancestors.include? ActiveRecord::Base }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class << self
|
91
|
+
def apply_to(klasses)
|
92
|
+
klasses.map {|klass| add_klass(klass)}
|
93
|
+
yield
|
94
|
+
ensure
|
95
|
+
klasses.map {|klass| remove_klass(klass)}
|
96
|
+
end
|
97
|
+
|
98
|
+
def applied_to?(klass)
|
99
|
+
!(klass.ancestors & extracted_klasses.keys).empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def extracted_klasses
|
105
|
+
Thread.current[:act_as_list_no_update] ||= {}
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_klass(klass)
|
109
|
+
extracted_klasses[klass] = 0 unless extracted_klasses.key?(klass)
|
110
|
+
extracted_klasses[klass] += 1
|
111
|
+
end
|
112
|
+
|
113
|
+
def remove_klass(klass)
|
114
|
+
extracted_klasses[klass] -= 1
|
115
|
+
extracted_klasses.delete(klass) if extracted_klasses[klass] <= 0
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def act_as_list_no_update?
|
120
|
+
NoUpdate.applied_to?(self.class)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
4
|
+
def self.call(caller_class, position_column, touch_on_update)
|
5
|
+
define_class_methods(caller_class, position_column, touch_on_update)
|
6
|
+
define_instance_methods(caller_class, position_column)
|
7
|
+
|
8
|
+
if mass_assignment_protection_was_used_by_user?(caller_class)
|
9
|
+
protect_attributes_from_mass_assignment(caller_class, position_column)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def self.define_class_methods(caller_class, position_column, touch_on_update)
|
16
|
+
caller_class.class_eval do
|
17
|
+
define_singleton_method :quoted_position_column do
|
18
|
+
@_quoted_position_column ||= connection.quote_column_name(position_column)
|
19
|
+
end
|
20
|
+
|
21
|
+
define_singleton_method :quoted_position_column_with_table_name do
|
22
|
+
@_quoted_position_column_with_table_name ||= "#{caller_class.quoted_table_name}.#{quoted_position_column}"
|
23
|
+
end
|
24
|
+
|
25
|
+
define_singleton_method :decrement_sequentially do
|
26
|
+
pluck(primary_key).each do |id|
|
27
|
+
where(primary_key => id).decrement_all
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
define_singleton_method :increment_sequentially do
|
32
|
+
pluck(primary_key).each do |id|
|
33
|
+
where(primary_key => id).increment_all
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
define_singleton_method :decrement_all do
|
38
|
+
update_all_with_touch "#{quoted_position_column} = (#{quoted_position_column_with_table_name} - 1)"
|
39
|
+
end
|
40
|
+
|
41
|
+
define_singleton_method :increment_all do
|
42
|
+
update_all_with_touch "#{quoted_position_column} = (#{quoted_position_column_with_table_name} + 1)"
|
43
|
+
end
|
44
|
+
|
45
|
+
define_singleton_method :update_all_with_touch do |updates|
|
46
|
+
updates += touch_record_sql if touch_on_update
|
47
|
+
update_all(updates)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
define_singleton_method :touch_record_sql do
|
53
|
+
new.touch_record_sql
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.define_instance_methods(caller_class, position_column)
|
59
|
+
caller_class.class_eval do
|
60
|
+
attr_reader :position_changed
|
61
|
+
|
62
|
+
define_method :position_column do
|
63
|
+
position_column
|
64
|
+
end
|
65
|
+
|
66
|
+
define_method :"#{position_column}=" do |position|
|
67
|
+
self[position_column] = position
|
68
|
+
@position_changed = true
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method :touch_record_sql do
|
72
|
+
cached_quoted_now = quoted_current_time_from_proper_timezone
|
73
|
+
|
74
|
+
timestamp_attributes_for_update_in_model.map do |attr|
|
75
|
+
", #{connection.quote_column_name(attr)} = #{cached_quoted_now}"
|
76
|
+
end.join
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
delegate :connection, to: self
|
82
|
+
|
83
|
+
def quoted_current_time_from_proper_timezone
|
84
|
+
connection.quote(connection.quoted_date(
|
85
|
+
current_time_from_proper_timezone))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.mass_assignment_protection_was_used_by_user?(caller_class)
|
91
|
+
caller_class.class_eval do
|
92
|
+
respond_to?(:accessible_attributes) and accessible_attributes.present?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.protect_attributes_from_mass_assignment(caller_class, position_column)
|
97
|
+
caller_class.class_eval do
|
98
|
+
attr_accessible position_column.to_sym
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "active_support/inflector"
|
3
|
+
|
4
|
+
module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
5
|
+
extend ActiveSupport::Inflector
|
6
|
+
|
7
|
+
def self.call(caller_class, scope)
|
8
|
+
scope = idify(caller_class, scope) if scope.is_a?(Symbol)
|
9
|
+
|
10
|
+
caller_class.class_eval do
|
11
|
+
define_method :scope_name do
|
12
|
+
scope
|
13
|
+
end
|
14
|
+
|
15
|
+
if scope.is_a?(Symbol)
|
16
|
+
define_method :scope_condition do
|
17
|
+
{ scope => send(:"#{scope}") }
|
18
|
+
end
|
19
|
+
|
20
|
+
define_method :scope_changed? do
|
21
|
+
changed.include?(scope_name.to_s)
|
22
|
+
end
|
23
|
+
|
24
|
+
define_method :destroyed_via_scope? do
|
25
|
+
scope == (destroyed_by_association && destroyed_by_association.foreign_key.to_sym)
|
26
|
+
end
|
27
|
+
elsif scope.is_a?(Array)
|
28
|
+
define_method :scope_condition do
|
29
|
+
# The elements of the Array can be symbols, strings, or hashes.
|
30
|
+
# If symbols or strings, they are treated as column names and the current value is looked up.
|
31
|
+
# If hashes, they are treated as fixed values.
|
32
|
+
scope.inject({}) do |hash, column_or_fixed_vals|
|
33
|
+
if column_or_fixed_vals.is_a?(Hash)
|
34
|
+
fixed_vals = column_or_fixed_vals
|
35
|
+
hash.merge!(fixed_vals)
|
36
|
+
else
|
37
|
+
column = column_or_fixed_vals
|
38
|
+
hash.merge!({ column.to_sym => read_attribute(column.to_sym) })
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
define_method :scope_changed? do
|
44
|
+
(scope_condition.keys & changed.map(&:to_sym)).any?
|
45
|
+
end
|
46
|
+
|
47
|
+
define_method :destroyed_via_scope? do
|
48
|
+
scope_condition.keys.include? (destroyed_by_association && destroyed_by_association.foreign_key.to_sym)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
define_method :scope_condition do
|
52
|
+
eval "%{#{scope}}"
|
53
|
+
end
|
54
|
+
|
55
|
+
define_method :scope_changed? do
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
define_method :destroyed_via_scope? do
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
self.scope :in_list, lambda { where("#{quoted_position_column_with_table_name} IS NOT NULL") }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.idify(caller_class, name)
|
69
|
+
return name if name.to_s =~ /_id$/
|
70
|
+
|
71
|
+
if caller_class.reflections.key?(name.to_s)
|
72
|
+
caller_class.reflections[name.to_s].foreign_key.to_sym
|
73
|
+
else
|
74
|
+
foreign_key(name).to_sym
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord::Acts::List::SequentialUpdatesMethodDefiner #:nodoc:
|
4
|
+
def self.call(caller_class, column, sequential_updates_option)
|
5
|
+
caller_class.class_eval do
|
6
|
+
define_method :sequential_updates? do
|
7
|
+
if !defined?(@sequential_updates)
|
8
|
+
if sequential_updates_option.nil?
|
9
|
+
table_exists =
|
10
|
+
if active_record_version_is?('>= 5')
|
11
|
+
caller_class.connection.data_source_exists?(caller_class.table_name)
|
12
|
+
else
|
13
|
+
caller_class.connection.table_exists?(caller_class.table_name)
|
14
|
+
end
|
15
|
+
index_exists = caller_class.connection.index_exists?(caller_class.table_name, column, unique: true)
|
16
|
+
@sequential_updates = table_exists && index_exists
|
17
|
+
else
|
18
|
+
@sequential_updates = sequential_updates_option
|
19
|
+
end
|
20
|
+
else
|
21
|
+
@sequential_updates
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private :sequential_updates?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord::Acts::List::TopOfListMethodDefiner #:nodoc:
|
4
|
+
def self.call(caller_class, top_of_list)
|
5
|
+
caller_class.class_eval do
|
6
|
+
define_singleton_method :acts_as_list_top do
|
7
|
+
top_of_list.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
define_method :acts_as_list_top do
|
11
|
+
top_of_list.to_i
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/acts_as_list/version.rb
CHANGED
data/lib/acts_as_list.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
ActiveRecord::Base.send(:include, ActiveRecord::Acts::List) if defined?(ActiveRecord)
|
14
|
-
end
|
15
|
-
end
|
3
|
+
require "acts_as_list/active_record/acts/list"
|
4
|
+
require "acts_as_list/active_record/acts/position_column_method_definer"
|
5
|
+
require "acts_as_list/active_record/acts/scope_method_definer"
|
6
|
+
require "acts_as_list/active_record/acts/top_of_list_method_definer"
|
7
|
+
require "acts_as_list/active_record/acts/add_new_at_method_definer"
|
8
|
+
require "acts_as_list/active_record/acts/aux_method_definer"
|
9
|
+
require "acts_as_list/active_record/acts/callback_definer"
|
10
|
+
require "acts_as_list/active_record/acts/no_update"
|
11
|
+
require "acts_as_list/active_record/acts/sequential_updates_method_definer"
|
12
|
+
require "acts_as_list/active_record/acts/active_record"
|
data/test/database.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
sqlite:
|
2
|
+
adapter: sqlite3
|
3
|
+
database: "file:memdb1?mode=memory&cache=shared"
|
4
|
+
|
5
|
+
mysql:
|
6
|
+
adapter: mysql2
|
7
|
+
host: 127.0.0.1
|
8
|
+
username: root
|
9
|
+
password:
|
10
|
+
database: acts_as_list
|
11
|
+
|
12
|
+
postgresql:
|
13
|
+
adapter: postgresql
|
14
|
+
host: localhost
|
15
|
+
username: postgres
|
16
|
+
password: postgres
|
17
|
+
database: acts_as_list
|
18
|
+
min_messages: ERROR
|
data/test/helper.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# $DEBUG = true
|
4
|
+
|
1
5
|
require "rubygems"
|
2
6
|
require "bundler/setup"
|
3
7
|
begin
|
@@ -9,13 +13,57 @@ rescue Bundler::BundlerError => e
|
|
9
13
|
end
|
10
14
|
require "active_record"
|
11
15
|
require "minitest/autorun"
|
16
|
+
require "mocha/minitest"
|
12
17
|
require "#{File.dirname(__FILE__)}/../init"
|
13
18
|
|
14
19
|
if defined?(ActiveRecord::VERSION) &&
|
15
|
-
ActiveRecord::VERSION::MAJOR
|
16
|
-
(ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2)
|
20
|
+
ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2
|
17
21
|
|
22
|
+
# Was removed in Rails 5 and is effectively true.
|
18
23
|
ActiveRecord::Base.raise_in_transactional_callbacks = true
|
19
24
|
end
|
20
25
|
|
26
|
+
db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
|
27
|
+
ActiveRecord::Base.establish_connection(db_config)
|
28
|
+
ActiveRecord::Schema.verbose = false
|
29
|
+
|
30
|
+
def teardown_db
|
31
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
32
|
+
tables = ActiveRecord::Base.connection.data_sources
|
33
|
+
else
|
34
|
+
tables = ActiveRecord::Base.connection.tables
|
35
|
+
end
|
36
|
+
|
37
|
+
tables.each do |table|
|
38
|
+
ActiveRecord::Base.connection.drop_table(table)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
21
42
|
require "shared"
|
43
|
+
|
44
|
+
# require 'logger'
|
45
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
46
|
+
|
47
|
+
def assert_equal_or_nil(a, b)
|
48
|
+
if a.nil?
|
49
|
+
assert_nil b
|
50
|
+
else
|
51
|
+
assert_equal a, b
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def assert_no_deprecation_warning_raised_by(failure_message = 'ActiveRecord deprecation warning raised when we didn\'t expect it', pass_message = 'No ActiveRecord deprecation raised')
|
56
|
+
original_behavior = ActiveSupport::Deprecation.behavior
|
57
|
+
ActiveSupport::Deprecation.behavior = :raise
|
58
|
+
begin
|
59
|
+
yield
|
60
|
+
rescue ActiveSupport::DeprecationException => e
|
61
|
+
flunk "#{failure_message}: #{e}"
|
62
|
+
rescue
|
63
|
+
raise
|
64
|
+
else
|
65
|
+
pass pass_message
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
ActiveSupport::Deprecation.behavior = original_behavior
|
69
|
+
end
|
data/test/shared.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Common shared behaviour.
|
2
4
|
module Shared
|
3
5
|
autoload :List, 'shared_list'
|
@@ -6,4 +8,5 @@ module Shared
|
|
6
8
|
autoload :ArrayScopeList, 'shared_array_scope_list'
|
7
9
|
autoload :TopAddition, 'shared_top_addition'
|
8
10
|
autoload :NoAddition, 'shared_no_addition'
|
11
|
+
autoload :Quoting, 'shared_quoting'
|
9
12
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Shared
|
2
4
|
module ArrayScopeList
|
3
5
|
def setup
|
@@ -25,7 +27,7 @@ module Shared
|
|
25
27
|
|
26
28
|
ArrayScopeListMixin.where(id: 4).first.move_to_top
|
27
29
|
assert_equal [4, 1, 3, 2], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
|
28
|
-
|
30
|
+
|
29
31
|
ArrayScopeListMixin.where(id: 4).first.insert_at(4)
|
30
32
|
assert_equal [1, 3, 2, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
|
31
33
|
assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:pos)
|
@@ -60,6 +62,11 @@ module Shared
|
|
60
62
|
assert !new.first?
|
61
63
|
assert new.last?
|
62
64
|
|
65
|
+
new = ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') }
|
66
|
+
assert_equal_or_nil $default_position,new.pos
|
67
|
+
assert_equal $default_position.is_a?(Integer), new.first?
|
68
|
+
assert !new.last?
|
69
|
+
|
63
70
|
new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
|
64
71
|
assert_equal 3, new.pos
|
65
72
|
assert !new.first?
|
@@ -81,6 +88,9 @@ module Shared
|
|
81
88
|
new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
|
82
89
|
assert_equal 3, new.pos
|
83
90
|
|
91
|
+
new_noup = ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') }
|
92
|
+
assert_equal_or_nil $default_position,new_noup.pos
|
93
|
+
|
84
94
|
new4 = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
|
85
95
|
assert_equal 4, new4.pos
|
86
96
|
|
@@ -104,6 +114,9 @@ module Shared
|
|
104
114
|
|
105
115
|
new4.reload
|
106
116
|
assert_equal 5, new4.pos
|
117
|
+
|
118
|
+
new_noup.reload
|
119
|
+
assert_equal_or_nil $default_position, new_noup.pos
|
107
120
|
end
|
108
121
|
|
109
122
|
def test_delete_middle
|
@@ -123,6 +136,12 @@ module Shared
|
|
123
136
|
|
124
137
|
assert_equal 1, ArrayScopeListMixin.where(id: 3).first.pos
|
125
138
|
assert_equal 2, ArrayScopeListMixin.where(id: 4).first.pos
|
139
|
+
|
140
|
+
ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.where(id: 3).first.destroy }
|
141
|
+
|
142
|
+
assert_equal [4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
|
143
|
+
|
144
|
+
assert_equal 2, ArrayScopeListMixin.where(id: 4).first.pos
|
126
145
|
end
|
127
146
|
|
128
147
|
def test_remove_from_list_should_then_fail_in_list?
|
@@ -136,10 +155,8 @@ module Shared
|
|
136
155
|
|
137
156
|
ArrayScopeListMixin.where(id: 2).first.remove_from_list
|
138
157
|
|
139
|
-
assert_equal [2, 1, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
|
140
|
-
|
141
158
|
assert_equal 1, ArrayScopeListMixin.where(id: 1).first.pos
|
142
|
-
|
159
|
+
assert_nil ArrayScopeListMixin.where(id: 2).first.pos
|
143
160
|
assert_equal 2, ArrayScopeListMixin.where(id: 3).first.pos
|
144
161
|
assert_equal 3, ArrayScopeListMixin.where(id: 4).first.pos
|
145
162
|
end
|