acts_as_list 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Acts
3
+ module List
4
+ module NoUpdate
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Lets you selectively disable all act_as_list database updates
9
+ # for the duration of a block.
10
+ #
11
+ # ==== Examples
12
+ # ActiveRecord::Acts::List.acts_as_list_no_update do
13
+ # TodoList....
14
+ # end
15
+ #
16
+ # TodoList.acts_as_list_no_update do
17
+ # TodoList....
18
+ # end
19
+ #
20
+ def acts_as_list_no_update(&block)
21
+ NoUpdate.apply_to(self, &block)
22
+ end
23
+ end
24
+
25
+ class << self
26
+ def apply_to(klass)
27
+ klasses.push(klass)
28
+ yield
29
+ ensure
30
+ klasses.pop
31
+ end
32
+
33
+ def applied_to?(klass)
34
+ klasses.any? { |k| k >= klass }
35
+ end
36
+
37
+ private
38
+
39
+ def klasses
40
+ Thread.current[:act_as_list_no_update] ||= []
41
+ end
42
+ end
43
+
44
+ def act_as_list_no_update?
45
+ NoUpdate.applied_to?(self.class)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
2
+ extend ActiveSupport::Inflector
3
+
4
+ def self.call(caller_class, scope)
5
+ scope = idify(scope) if scope.is_a?(Symbol)
6
+
7
+ caller_class.class_eval do
8
+ define_method :scope_name do
9
+ scope
10
+ end
11
+
12
+ if scope.is_a?(Symbol)
13
+ define_method :scope_condition do
14
+ { scope => send(:"#{scope}") }
15
+ end
16
+
17
+ define_method :scope_changed? do
18
+ changed.include?(scope_name.to_s)
19
+ end
20
+ elsif scope.is_a?(Array)
21
+ define_method :scope_condition do
22
+ scope.inject({}) do |hash, column|
23
+ hash.merge!({ column.to_sym => read_attribute(column.to_sym) })
24
+ end
25
+ end
26
+
27
+ define_method :scope_changed? do
28
+ (scope_condition.keys & changed.map(&:to_sym)).any?
29
+ end
30
+ else
31
+ define_method :scope_condition do
32
+ eval "%{#{scope}}"
33
+ end
34
+
35
+ define_method :scope_changed? do
36
+ false
37
+ end
38
+ end
39
+
40
+ self.scope :in_list, lambda { where("#{quoted_position_column_with_table_name} IS NOT NULL") }
41
+ end
42
+ end
43
+
44
+ def self.idify(name)
45
+ return name if name.to_s =~ /_id$/
46
+
47
+ foreign_key(name).to_sym
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord::Acts::List::SequentialUpdatesMethodDefiner #:nodoc:
2
+ def self.call(caller_class, column, sequential_updates_option)
3
+ caller_class.class_eval do
4
+ define_method :sequential_updates? do
5
+ if !defined?(@sequential_updates)
6
+ if sequential_updates_option.nil?
7
+ table_exists = caller_class.connection.table_exists?(caller_class.table_name)
8
+ index_exists = caller_class.connection.index_exists?(caller_class.table_name, column, unique: true)
9
+ @sequential_updates = table_exists && index_exists
10
+ else
11
+ @sequential_updates = sequential_updates_option
12
+ end
13
+ else
14
+ @sequential_updates
15
+ end
16
+ end
17
+
18
+ private :sequential_updates?
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord::Acts::List::TopOfListMethodDefiner #:nodoc:
2
+ def self.call(caller_class, top_of_list)
3
+ caller_class.class_eval do
4
+ define_singleton_method :acts_as_list_top do
5
+ top_of_list.to_i
6
+ end
7
+
8
+ define_method :acts_as_list_top do
9
+ top_of_list.to_i
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module Acts
3
3
  module List
4
- VERSION = '0.8.2'
4
+ VERSION = '0.9.0'
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,16 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: "file:memdb1?mode=memory&cache=shared"
4
+
5
+ mysql:
6
+ adapter: mysql2
7
+ username: root
8
+ password:
9
+ database: acts_as_list
10
+
11
+ postgresql:
12
+ adapter: postgresql
13
+ username: postgres
14
+ password:
15
+ database: acts_as_list
16
+ min_messages: ERROR
@@ -1,3 +1,5 @@
1
+ # $DEBUG = true
2
+
1
3
  require "rubygems"
2
4
  require "bundler/setup"
3
5
  begin
@@ -19,3 +21,13 @@ if defined?(ActiveRecord::VERSION) &&
19
21
  end
20
22
 
21
23
  require "shared"
24
+
25
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
26
+
27
+ def assert_equal_or_nil(a, b)
28
+ if a.nil?
29
+ assert_nil b
30
+ else
31
+ assert_equal a, b
32
+ end
33
+ end
@@ -25,7 +25,7 @@ module Shared
25
25
 
26
26
  ArrayScopeListMixin.where(id: 4).first.move_to_top
27
27
  assert_equal [4, 1, 3, 2], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
28
-
28
+
29
29
  ArrayScopeListMixin.where(id: 4).first.insert_at(4)
30
30
  assert_equal [1, 3, 2, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
31
31
  assert_equal [1, 2, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:pos)
@@ -60,6 +60,11 @@ module Shared
60
60
  assert !new.first?
61
61
  assert new.last?
62
62
 
63
+ new = ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') }
64
+ assert_equal_or_nil $default_position,new.pos
65
+ assert_equal $default_position.is_a?(Fixnum), new.first?
66
+ assert !new.last?
67
+
63
68
  new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
64
69
  assert_equal 3, new.pos
65
70
  assert !new.first?
@@ -81,6 +86,9 @@ module Shared
81
86
  new = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
82
87
  assert_equal 3, new.pos
83
88
 
89
+ new_noup = ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass') }
90
+ assert_equal_or_nil $default_position,new_noup.pos
91
+
84
92
  new4 = ArrayScopeListMixin.create(parent_id: 20, parent_type: 'ParentClass')
85
93
  assert_equal 4, new4.pos
86
94
 
@@ -104,6 +112,9 @@ module Shared
104
112
 
105
113
  new4.reload
106
114
  assert_equal 5, new4.pos
115
+
116
+ new_noup.reload
117
+ assert_equal_or_nil $default_position, new_noup.pos
107
118
  end
108
119
 
109
120
  def test_delete_middle
@@ -123,6 +134,12 @@ module Shared
123
134
 
124
135
  assert_equal 1, ArrayScopeListMixin.where(id: 3).first.pos
125
136
  assert_equal 2, ArrayScopeListMixin.where(id: 4).first.pos
137
+
138
+ ArrayScopeListMixin.acts_as_list_no_update { ArrayScopeListMixin.where(id: 3).first.destroy }
139
+
140
+ assert_equal [4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
141
+
142
+ assert_equal 2, ArrayScopeListMixin.where(id: 4).first.pos
126
143
  end
127
144
 
128
145
  def test_remove_from_list_should_then_fail_in_list?
@@ -136,10 +153,8 @@ module Shared
136
153
 
137
154
  ArrayScopeListMixin.where(id: 2).first.remove_from_list
138
155
 
139
- assert_equal [2, 1, 3, 4], ArrayScopeListMixin.where(parent_id: 5, parent_type: 'ParentClass').order('pos').map(&:id)
140
-
141
156
  assert_equal 1, ArrayScopeListMixin.where(id: 1).first.pos
142
- assert_equal nil, ArrayScopeListMixin.where(id: 2).first.pos
157
+ assert_nil ArrayScopeListMixin.where(id: 2).first.pos
143
158
  assert_equal 2, ArrayScopeListMixin.where(id: 3).first.pos
144
159
  assert_equal 3, ArrayScopeListMixin.where(id: 4).first.pos
145
160
  end
@@ -60,6 +60,11 @@ module Shared
60
60
  assert !new.first?
61
61
  assert new.last?
62
62
 
63
+ new = ListMixin.acts_as_list_no_update { ListMixin.create(parent_id: 20) }
64
+ assert_equal_or_nil $default_position, new.pos
65
+ assert_equal $default_position.is_a?(Fixnum), new.first?
66
+ assert !new.last?
67
+
63
68
  new = ListMixin.create(parent_id: 20)
64
69
  assert_equal 3, new.pos
65
70
  assert !new.first?
@@ -81,6 +86,9 @@ module Shared
81
86
  new = ListMixin.create(parent_id: 20)
82
87
  assert_equal 3, new.pos
83
88
 
89
+ new_noup = ListMixin.acts_as_list_no_update { ListMixin.create(parent_id: 20) }
90
+ assert_equal_or_nil $default_position, new_noup.pos
91
+
84
92
  new4 = ListMixin.create(parent_id: 20)
85
93
  assert_equal 4, new4.pos
86
94
 
@@ -105,18 +113,20 @@ module Shared
105
113
  new4.reload
106
114
  assert_equal 5, new4.pos
107
115
 
108
- last1 = ListMixin.order('pos').last
109
- last2 = ListMixin.order('pos').last
116
+ new_noup.reload
117
+ assert_equal_or_nil $default_position, new_noup.pos
118
+
119
+ last1 = ListMixin.where('pos IS NOT NULL').order('pos').last
120
+ last2 = ListMixin.where('pos IS NOT NULL').order('pos').last
110
121
  last1.insert_at(1)
111
122
  last2.insert_at(1)
112
- assert_equal [1, 2, 3, 4, 5], ListMixin.where(parent_id: 20).order('pos').map(&:pos)
123
+ pos_list = ListMixin.where(parent_id: 20).order("pos ASC#{' NULLS FIRST' if ENV['DB'] == 'postgresql'}").map(&:pos)
124
+ assert_equal [$default_position, 1, 2, 3, 4, 5], pos_list
113
125
  end
114
126
 
115
127
  def test_delete_middle
116
128
  assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
117
129
 
118
-
119
-
120
130
  ListMixin.where(id: 2).first.destroy
121
131
 
122
132
  assert_equal [1, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
@@ -131,6 +141,12 @@ module Shared
131
141
 
132
142
  assert_equal 1, ListMixin.where(id: 3).first.pos
133
143
  assert_equal 2, ListMixin.where(id: 4).first.pos
144
+
145
+ ListMixin.acts_as_list_no_update { ListMixin.where(id: 3).first.destroy }
146
+
147
+ assert_equal [4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
148
+
149
+ assert_equal 2, ListMixin.where(id: 4).first.pos
134
150
  end
135
151
 
136
152
  def test_with_string_based_scope
@@ -175,10 +191,8 @@ module Shared
175
191
 
176
192
  ListMixin.where(id: 2).first.remove_from_list
177
193
 
178
- assert_equal [2, 1, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
179
-
180
194
  assert_equal 1, ListMixin.where(id: 1).first.pos
181
- assert_equal nil, ListMixin.where(id: 2).first.pos
195
+ assert_nil ListMixin.where(id: 2).first.pos
182
196
  assert_equal 2, ListMixin.where(id: 3).first.pos
183
197
  assert_equal 3, ListMixin.where(id: 4).first.pos
184
198
  end
@@ -243,14 +257,24 @@ module Shared
243
257
 
244
258
  assert_equal [5, 1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
245
259
 
260
+ new6 = ListMixin.new(parent_id: 5)
261
+ new6.pos = 3
262
+ new6.save!
263
+ assert_equal 3, new6.pos
264
+ assert !new6.first?
265
+ assert !new6.last?
266
+
267
+ assert_equal [5, 1, 6, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
268
+
246
269
  new = ListMixin.new(parent_id: 5)
247
270
  new.pos = 3
248
- new.save!
271
+ ListMixin.acts_as_list_no_update { new.save! }
249
272
  assert_equal 3, new.pos
273
+ assert_equal 3, new6.pos
250
274
  assert !new.first?
251
275
  assert !new.last?
252
276
 
253
- assert_equal [5, 1, 6, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
277
+ assert_equal [5, 1, 6, 7, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos, id').map(&:id)
254
278
  end
255
279
 
256
280
  def test_non_persisted_records_dont_get_lock_called
@@ -49,12 +49,16 @@ module Shared
49
49
  end
50
50
 
51
51
  assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
52
+ assert_equal [5, 10, 15, 20], ListMixin.where(parent_id: 5000).order('id').map(&:pos)
52
53
 
53
54
  ListMixin.where(id: 2).first.move_lower
54
55
  assert_equal [1, 3, 2, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
56
+ assert_equal [5, 15, 10, 20], ListMixin.where(parent_id: 5000).order('id').map(&:pos)
57
+
55
58
 
56
59
  ListMixin.where(id: 2).first.move_higher
57
60
  assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
61
+ assert_equal [5, 10, 15, 20], ListMixin.where(parent_id: 5000).order('id').map(&:pos)
58
62
 
59
63
  ListMixin.where(id: 1).first.move_to_bottom
60
64
  assert_equal [2, 3, 4, 1], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
@@ -93,7 +97,7 @@ module Shared
93
97
 
94
98
  li3.update_column(:pos, 2) # Make the same position as li2
95
99
 
96
- assert_equal [1, 2, 2, 4], ListMixin.pluck(:pos)
100
+ assert_equal [1, 2, 2, 4], ListMixin.order(:pos).pluck(:pos)
97
101
 
98
102
  assert_equal [li3, li4], li2.lower_items
99
103
  assert_equal [li2, li4], li3.lower_items
@@ -118,6 +122,9 @@ module Shared
118
122
  new = ListMixinSub1.create("parent_id" => 20)
119
123
  assert_equal 3, new.pos
120
124
 
125
+ new_noup = ListMixinSub1.acts_as_list_no_update { ListMixinSub1.create("parent_id" => 20) }
126
+ assert_equal_or_nil $default_position, new_noup.pos
127
+
121
128
  new4 = ListMixin.create("parent_id" => 20)
122
129
  assert_equal 4, new4.pos
123
130
 
@@ -141,6 +148,9 @@ module Shared
141
148
 
142
149
  new4.reload
143
150
  assert_equal 5, new4.pos
151
+
152
+ new_noup.reload
153
+ assert_equal_or_nil $default_position, new_noup.pos
144
154
  end
145
155
 
146
156
  def test_delete_middle
@@ -160,6 +170,12 @@ module Shared
160
170
 
161
171
  assert_equal 1, ListMixin.where(id: 3).first.pos
162
172
  assert_equal 2, ListMixin.where(id: 4).first.pos
173
+
174
+ ListMixin.acts_as_list_no_update { ListMixin.where(id: 3).first.destroy }
175
+
176
+ assert_equal [4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
177
+
178
+ assert_equal 2, ListMixin.where(id: 4).first.pos
163
179
  end
164
180
 
165
181
  def test_acts_as_list_class
@@ -6,11 +6,11 @@ module Shared
6
6
 
7
7
  def test_insert
8
8
  new = NoAdditionMixin.create(parent_id: 20)
9
- assert_equal nil, new.pos
9
+ assert_nil new.pos
10
10
  assert !new.in_list?
11
11
 
12
12
  new = NoAdditionMixin.create(parent_id: 20)
13
- assert_equal nil, new.pos
13
+ assert_nil new.pos
14
14
  end
15
15
 
16
16
  def test_update_does_not_add_to_list
@@ -46,9 +46,14 @@ module Shared
46
46
  assert new.first?
47
47
  assert !new.last?
48
48
 
49
+ new = TopAdditionMixin.acts_as_list_no_update { TopAdditionMixin.create(parent_id: 20) }
50
+ assert_equal_or_nil $default_position, new.pos
51
+ assert_equal $default_position.is_a?(Fixnum), new.first?
52
+ assert !new.last?
53
+
49
54
  new = TopAdditionMixin.create(parent_id: 20)
50
55
  assert_equal 1, new.pos
51
- assert new.first?
56
+ assert_equal $default_position.nil?, new.first?
52
57
  assert !new.last?
53
58
 
54
59
  new = TopAdditionMixin.create(parent_id: 0)
@@ -67,6 +72,9 @@ module Shared
67
72
  new = TopAdditionMixin.create(parent_id: 20)
68
73
  assert_equal 1, new.pos
69
74
 
75
+ new = TopAdditionMixin.acts_as_list_no_update { TopAdditionMixin.create(parent_id: 20) }
76
+ assert_equal_or_nil $default_position, new.pos
77
+
70
78
  new4 = TopAdditionMixin.create(parent_id: 20)
71
79
  assert_equal 1, new4.pos
72
80
 
@@ -87,6 +95,13 @@ module Shared
87
95
  assert_equal 1, TopAdditionMixin.where(id: 1).first.pos
88
96
  assert_equal 2, TopAdditionMixin.where(id: 3).first.pos
89
97
  assert_equal 3, TopAdditionMixin.where(id: 4).first.pos
98
+
99
+ TopAdditionMixin.acts_as_list_no_update { TopAdditionMixin.where(id: 3).first.destroy }
100
+
101
+ assert_equal [1, 4], TopAdditionMixin.where(parent_id: 5).order('pos').map(&:id)
102
+
103
+ assert_equal 1, TopAdditionMixin.where(id: 1).first.pos
104
+ assert_equal 3, TopAdditionMixin.where(id: 4).first.pos
90
105
  end
91
106
 
92
107
  end