acts_as_list 0.7.2 → 0.8.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.
@@ -38,32 +38,30 @@ module ActiveRecord
38
38
  configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom}
39
39
  configuration.update(options) if options.is_a?(Hash)
40
40
 
41
- configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
41
+ if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
42
+ configuration[:scope] = :"#{configuration[:scope]}_id"
43
+ end
42
44
 
43
45
  if configuration[:scope].is_a?(Symbol)
44
46
  scope_methods = %(
45
47
  def scope_condition
46
- { :#{configuration[:scope].to_s} => send(:#{configuration[:scope].to_s}) }
48
+ { #{configuration[:scope]}: send(:#{configuration[:scope]}) }
47
49
  end
48
50
 
49
51
  def scope_changed?
50
- changes.include?(scope_name.to_s)
52
+ changed.include?(scope_name.to_s)
51
53
  end
52
54
  )
53
55
  elsif configuration[:scope].is_a?(Array)
54
56
  scope_methods = %(
55
- def attrs
56
- %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
57
- memo[column.intern] = read_attribute(column.intern); memo
57
+ def scope_condition
58
+ #{configuration[:scope]}.inject({}) do |hash, column|
59
+ hash.merge!({ column.to_sym => read_attribute(column.to_sym) })
58
60
  end
59
61
  end
60
62
 
61
63
  def scope_changed?
62
- (attrs.keys & changes.keys.map(&:to_sym)).any?
63
- end
64
-
65
- def scope_condition
66
- attrs
64
+ (scope_condition.keys & changed.map(&:to_sym)).any?
67
65
  end
68
66
  )
69
67
  else
@@ -76,8 +74,13 @@ module ActiveRecord
76
74
  )
77
75
  end
78
76
 
79
- class_eval <<-EOV
80
- include ::ActiveRecord::Acts::List::InstanceMethods
77
+ quoted_position_column = connection.quote_column_name(configuration[:column])
78
+ quoted_position_column_with_table_name = "#{quoted_table_name}.#{quoted_position_column}"
79
+
80
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
81
+ def self.acts_as_list_top
82
+ #{configuration[:top_of_list]}.to_i
83
+ end
81
84
 
82
85
  def acts_as_list_top
83
86
  #{configuration[:top_of_list]}.to_i
@@ -113,19 +116,46 @@ module ActiveRecord
113
116
  attr_accessible :#{configuration[:column]}
114
117
  end
115
118
 
116
- before_destroy :reload_position
117
- after_destroy :decrement_positions_on_lower_items
118
- before_update :check_scope
119
- after_update :update_positions
120
- before_validation :check_top_position
119
+ scope :in_list, lambda { where(%q{#{quoted_position_column_with_table_name} IS NOT NULL}) }
120
+
121
+ def self.decrement_all
122
+ update_all_with_touch %q(#{quoted_position_column} = (#{quoted_position_column_with_table_name} - 1))
123
+ end
124
+
125
+ def self.increment_all
126
+ update_all_with_touch %q(#{quoted_position_column} = (#{quoted_position_column_with_table_name} + 1))
127
+ end
128
+
129
+ def self.update_all_with_touch(updates)
130
+ record = new
131
+ attrs = record.send(:timestamp_attributes_for_update_in_model)
132
+ now = record.send(:current_time_from_proper_timezone)
133
+
134
+ query = attrs.map { |attr| %(\#{connection.quote_column_name(attr)} = :now) }
135
+ query.push updates
136
+ query = query.join(", ")
121
137
 
122
- scope :in_list, lambda { where("#{table_name}.#{configuration[:column]} IS NOT NULL") }
138
+ update_all([query, now: now])
139
+ end
123
140
  EOV
124
141
 
142
+ attr_reader :position_changed
143
+
144
+ before_validation :check_top_position
145
+
146
+ before_destroy :lock!
147
+ after_destroy :decrement_positions_on_lower_items
148
+
149
+ before_update :check_scope
150
+ after_update :update_positions
151
+
152
+ after_commit :clear_scope_changed
153
+
125
154
  if configuration[:add_new_at].present?
126
- self.send(:before_create, "add_to_list_#{configuration[:add_new_at]}")
155
+ before_create "add_to_list_#{configuration[:add_new_at]}".to_sym
127
156
  end
128
157
 
158
+ include ::ActiveRecord::Acts::List::InstanceMethods
129
159
  end
130
160
  end
131
161
 
@@ -144,8 +174,12 @@ module ActiveRecord
144
174
  return unless lower_item
145
175
 
146
176
  acts_as_list_class.transaction do
147
- lower_item.decrement_position
148
- increment_position
177
+ if lower_item.send(position_column) != self.send(position_column)
178
+ swap_positions(lower_item, self)
179
+ else
180
+ lower_item.decrement_position
181
+ increment_position
182
+ end
149
183
  end
150
184
  end
151
185
 
@@ -154,8 +188,12 @@ module ActiveRecord
154
188
  return unless higher_item
155
189
 
156
190
  acts_as_list_class.transaction do
157
- higher_item.increment_position
158
- decrement_position
191
+ if higher_item.send(position_column) != self.send(position_column)
192
+ swap_positions(higher_item, self)
193
+ else
194
+ higher_item.increment_position
195
+ decrement_position
196
+ end
159
197
  end
160
198
  end
161
199
 
@@ -206,25 +244,20 @@ module ActiveRecord
206
244
  set_list_position(self.send(position_column).to_i - 1)
207
245
  end
208
246
 
209
- # Return +true+ if this object is the first in the list.
210
247
  def first?
211
248
  return false unless in_list?
212
- self.send(position_column) == acts_as_list_top
249
+ !higher_item
213
250
  end
214
251
 
215
- # Return +true+ if this object is the last in the list.
216
252
  def last?
217
253
  return false unless in_list?
218
- self.send(position_column) == bottom_position_in_list
254
+ !lower_item
219
255
  end
220
256
 
221
257
  # Return the next higher item in the list.
222
258
  def higher_item
223
259
  return nil unless in_list?
224
- acts_as_list_class.unscoped do
225
- acts_as_list_class.where(scope_condition).where("#{position_column} < #{(send(position_column).to_i).to_s}").
226
- order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
227
- end
260
+ higher_items(1).first
228
261
  end
229
262
 
230
263
  # Return the next n higher items in the list
@@ -233,19 +266,15 @@ module ActiveRecord
233
266
  limit ||= acts_as_list_list.count
234
267
  position_value = send(position_column)
235
268
  acts_as_list_list.
236
- where("#{position_column} < ?", position_value).
237
- where("#{position_column} >= ?", position_value - limit).
238
- limit(limit).
239
- order("#{acts_as_list_class.table_name}.#{position_column} ASC")
269
+ where("#{quoted_position_column_with_table_name} < ?", position_value).
270
+ order("#{quoted_position_column_with_table_name} DESC").
271
+ limit(limit)
240
272
  end
241
273
 
242
274
  # Return the next lower item in the list.
243
275
  def lower_item
244
276
  return nil unless in_list?
245
- acts_as_list_class.unscoped do
246
- acts_as_list_class.where(scope_condition).where("#{position_column} > #{(send(position_column).to_i).to_s}").
247
- order("#{acts_as_list_class.table_name}.#{position_column} ASC").first
248
- end
277
+ lower_items(1).first
249
278
  end
250
279
 
251
280
  # Return the next n lower items in the list
@@ -254,10 +283,9 @@ module ActiveRecord
254
283
  limit ||= acts_as_list_list.count
255
284
  position_value = send(position_column)
256
285
  acts_as_list_list.
257
- where("#{position_column} > ?", position_value).
258
- where("#{position_column} <= ?", position_value + limit).
259
- limit(limit).
260
- order("#{acts_as_list_class.table_name}.#{position_column} ASC")
286
+ where("#{quoted_position_column_with_table_name} > ?", position_value).
287
+ order("#{quoted_position_column_with_table_name} ASC").
288
+ limit(limit)
261
289
  end
262
290
 
263
291
  # Test if this record is in a list
@@ -284,23 +312,48 @@ module ActiveRecord
284
312
  end
285
313
 
286
314
  private
315
+
316
+ def swap_positions(item1, item2)
317
+ item1.set_list_position(item2.send(position_column))
318
+ item2.set_list_position(item1.send("#{position_column}_was"))
319
+ end
320
+
287
321
  def acts_as_list_list
288
322
  acts_as_list_class.unscoped do
289
323
  acts_as_list_class.where(scope_condition)
290
324
  end
291
325
  end
292
326
 
327
+ # Poorly named methods. They will insert the item at the desired position if the position
328
+ # has been set manually using position=, not necessarily the top or bottom of the list:
329
+
293
330
  def add_to_list_top
294
- increment_positions_on_all_items
295
- self[position_column] = acts_as_list_top
331
+ if not_in_list? || internal_scope_changed? && !position_changed || default_position?
332
+ increment_positions_on_all_items
333
+ self[position_column] = acts_as_list_top
334
+ else
335
+ increment_positions_on_lower_items(self[position_column], id)
336
+ end
337
+
338
+ # Make sure we know that we've processed this scope change already
339
+ @scope_changed = false
340
+
341
+ # Don't halt the callback chain
342
+ true
296
343
  end
297
344
 
298
345
  def add_to_list_bottom
299
- if not_in_list? || scope_changed? && !@position_changed || default_position?
346
+ if not_in_list? || internal_scope_changed? && !position_changed || default_position?
300
347
  self[position_column] = bottom_position_in_list.to_i + 1
301
348
  else
302
349
  increment_positions_on_lower_items(self[position_column], id)
303
350
  end
351
+
352
+ # Make sure we know that we've processed this scope change already
353
+ @scope_changed = false
354
+
355
+ # Don't halt the callback chain
356
+ true
304
357
  end
305
358
 
306
359
  # Overwrite this method to define the scope of the list changes
@@ -315,11 +368,12 @@ module ActiveRecord
315
368
 
316
369
  # Returns the bottom item
317
370
  def bottom_item(except = nil)
318
- conditions = scope_condition
319
- conditions = except ? "#{self.class.primary_key} != #{self.class.connection.quote(except.id)}" : {}
320
- acts_as_list_class.unscoped do
321
- acts_as_list_class.in_list.where(scope_condition).where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
322
- end
371
+ conditions = except ? "#{quoted_table_name}.#{self.class.primary_key} != #{self.class.connection.quote(except.id)}" : {}
372
+ acts_as_list_list.in_list.where(
373
+ conditions
374
+ ).order(
375
+ "#{quoted_position_column_with_table_name} DESC"
376
+ ).first
323
377
  end
324
378
 
325
379
  # Forces item to assume the bottom position in the list.
@@ -334,110 +388,74 @@ module ActiveRecord
334
388
 
335
389
  # This has the effect of moving all the higher items up one.
336
390
  def decrement_positions_on_higher_items(position)
337
- acts_as_list_class.unscoped do
338
- acts_as_list_class.where(scope_condition).where(
339
- "#{position_column} <= #{position}"
340
- ).update_all(
341
- "#{position_column} = (#{position_column} - 1)"
342
- )
343
- end
391
+ acts_as_list_list.where("#{quoted_position_column_with_table_name} <= ?", position).decrement_all
344
392
  end
345
393
 
346
394
  # This has the effect of moving all the lower items up one.
347
395
  def decrement_positions_on_lower_items(position=nil)
348
396
  return unless in_list?
349
397
  position ||= send(position_column).to_i
350
- acts_as_list_class.unscoped do
351
- acts_as_list_class.where(scope_condition).where(
352
- "#{position_column} > #{position}"
353
- ).update_all(
354
- "#{position_column} = (#{position_column} - 1)"
355
- )
356
- end
398
+ acts_as_list_list.where("#{quoted_position_column_with_table_name} > ?", position).decrement_all
357
399
  end
358
400
 
359
401
  # This has the effect of moving all the higher items down one.
360
402
  def increment_positions_on_higher_items
361
403
  return unless in_list?
362
- acts_as_list_class.unscoped do
363
- acts_as_list_class.where(scope_condition).where(
364
- "#{position_column} < #{send(position_column).to_i}"
365
- ).update_all(
366
- "#{position_column} = (#{position_column} + 1)"
367
- )
368
- end
404
+ acts_as_list_list.where("#{quoted_position_column_with_table_name} < #{send(position_column).to_i}").increment_all
369
405
  end
370
406
 
371
407
  # This has the effect of moving all the lower items down one.
372
408
  def increment_positions_on_lower_items(position, avoid_id = nil)
373
- avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
409
+ avoid_id_condition = avoid_id ? " AND #{quoted_table_name}.#{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
374
410
 
375
- acts_as_list_class.unscoped do
376
- acts_as_list_class.where(scope_condition).where(
377
- "#{position_column} >= #{position}#{avoid_id_condition}"
378
- ).update_all(
379
- "#{position_column} = (#{position_column} + 1)"
380
- )
381
- end
411
+ acts_as_list_list.where("#{quoted_position_column_with_table_name} >= #{position}#{avoid_id_condition}").increment_all
382
412
  end
383
413
 
384
414
  # Increments position (<tt>position_column</tt>) of all items in the list.
385
415
  def increment_positions_on_all_items
386
- acts_as_list_class.unscoped do
387
- acts_as_list_class.where(
388
- scope_condition
389
- ).update_all(
390
- "#{position_column} = (#{position_column} + 1)"
391
- )
392
- end
416
+ acts_as_list_list.increment_all
393
417
  end
394
418
 
395
419
  # Reorders intermediate items to support moving an item from old_position to new_position.
396
420
  def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil)
397
421
  return if old_position == new_position
398
- avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
422
+ avoid_id_condition = avoid_id ? " AND #{quoted_table_name}.#{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
399
423
 
400
424
  if old_position < new_position
401
425
  # Decrement position of intermediate items
402
426
  #
403
427
  # e.g., if moving an item from 2 to 5,
404
428
  # move [3, 4, 5] to [2, 3, 4]
405
- acts_as_list_class.unscoped do
406
- acts_as_list_class.where(scope_condition).where(
407
- "#{position_column} > #{old_position}"
408
- ).where(
409
- "#{position_column} <= #{new_position}#{avoid_id_condition}"
410
- ).update_all(
411
- "#{position_column} = (#{position_column} - 1)"
412
- )
413
- end
429
+ acts_as_list_list.where(
430
+ "#{quoted_position_column_with_table_name} > ?", old_position
431
+ ).where(
432
+ "#{quoted_position_column_with_table_name} <= #{new_position}#{avoid_id_condition}"
433
+ ).decrement_all
414
434
  else
415
435
  # Increment position of intermediate items
416
436
  #
417
437
  # e.g., if moving an item from 5 to 2,
418
438
  # move [2, 3, 4] to [3, 4, 5]
419
- acts_as_list_class.unscoped do
420
- acts_as_list_class.where(scope_condition).where(
421
- "#{position_column} >= #{new_position}"
422
- ).where(
423
- "#{position_column} < #{old_position}#{avoid_id_condition}"
424
- ).update_all(
425
- "#{position_column} = (#{position_column} + 1)"
426
- )
427
- end
439
+ acts_as_list_list.where(
440
+ "#{quoted_position_column_with_table_name} >= ?", new_position
441
+ ).where(
442
+ "#{quoted_position_column_with_table_name} < #{old_position}#{avoid_id_condition}"
443
+ ).increment_all
428
444
  end
429
445
  end
430
446
 
431
447
  def insert_at_position(position)
432
448
  return set_list_position(position) if new_record?
433
- if in_list?
434
- old_position = send(position_column).to_i
435
- return if position == old_position
436
- shuffle_positions_on_intermediate_items(old_position, position)
437
- else
438
- increment_positions_on_lower_items(position)
449
+ with_lock do
450
+ if in_list?
451
+ old_position = send(position_column).to_i
452
+ return if position == old_position
453
+ shuffle_positions_on_intermediate_items(old_position, position)
454
+ else
455
+ increment_positions_on_lower_items(position)
456
+ end
457
+ set_list_position(position)
439
458
  end
440
- set_list_position(position)
441
459
  end
442
460
 
443
461
  # used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint
@@ -453,29 +471,32 @@ module ActiveRecord
453
471
  old_position = send("#{position_column}_was").to_i
454
472
  new_position = send(position_column).to_i
455
473
 
456
- return unless acts_as_list_class.unscoped do
457
- acts_as_list_class.where(scope_condition).where("#{position_column} = #{new_position}").count > 1
458
- end
474
+ return unless acts_as_list_list.where(
475
+ "#{quoted_position_column_with_table_name} = #{new_position}"
476
+ ).count > 1
459
477
  shuffle_positions_on_intermediate_items old_position, new_position, id
460
478
  end
461
479
 
462
- # Temporarily swap changes attributes with current attributes
463
- def swap_changed_attributes
464
- @changed_attributes.each { |k, _| @changed_attributes[k], self[k] =
465
- self[k], @changed_attributes[k] }
480
+ def internal_scope_changed?
481
+ return @scope_changed if defined?(@scope_changed)
482
+
483
+ @scope_changed = scope_changed?
484
+ end
485
+
486
+ def clear_scope_changed
487
+ remove_instance_variable(:@scope_changed) if defined?(@scope_changed)
466
488
  end
467
489
 
468
490
  def check_scope
469
- if scope_changed?
470
- swap_changed_attributes
491
+ if internal_scope_changed?
492
+ cached_changes = changes
493
+
494
+ cached_changes.each { |attribute, values| self[attribute] = values[0] }
471
495
  send('decrement_positions_on_lower_items') if lower_item
472
- swap_changed_attributes
473
- send("add_to_list_#{add_new_at}")
474
- end
475
- end
496
+ cached_changes.each { |attribute, values| self[attribute] = values[1] }
476
497
 
477
- def reload_position
478
- self.reload
498
+ send("add_to_list_#{add_new_at}") if add_new_at.present?
499
+ end
479
500
  end
480
501
 
481
502
  # This check is skipped if the position is currently the default position from the table
@@ -485,6 +506,20 @@ module ActiveRecord
485
506
  self[position_column] = acts_as_list_top
486
507
  end
487
508
  end
509
+
510
+ # When using raw column name it must be quoted otherwise it can raise syntax errors with SQL keywords (e.g. order)
511
+ def quoted_position_column
512
+ @_quoted_position_column ||= self.class.connection.quote_column_name(position_column)
513
+ end
514
+
515
+ # Used in order clauses
516
+ def quoted_table_name
517
+ @_quoted_table_name ||= acts_as_list_class.quoted_table_name
518
+ end
519
+
520
+ def quoted_position_column_with_table_name
521
+ @_quoted_position_column_with_table_name ||= "#{quoted_table_name}.#{quoted_position_column}"
522
+ end
488
523
  end
489
524
  end
490
525
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module Acts
3
3
  module List
4
- VERSION = '0.7.2'
4
+ VERSION = '0.8.0'
5
5
  end
6
6
  end
7
7
  end
data/test/helper.rb CHANGED
@@ -11,4 +11,11 @@ require "active_record"
11
11
  require "minitest/autorun"
12
12
  require "#{File.dirname(__FILE__)}/../init"
13
13
 
14
+ if defined?(ActiveRecord::VERSION) &&
15
+ ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2
16
+
17
+ # Was removed in Rails 5 and is effectively true.
18
+ ActiveRecord::Base.raise_in_transactional_callbacks = true
19
+ end
20
+
14
21
  require "shared"
data/test/shared.rb CHANGED
@@ -6,4 +6,5 @@ module Shared
6
6
  autoload :ArrayScopeList, 'shared_array_scope_list'
7
7
  autoload :TopAddition, 'shared_top_addition'
8
8
  autoload :NoAddition, 'shared_no_addition'
9
+ autoload :Quoting, 'shared_quoting'
9
10
  end
data/test/shared_list.rb CHANGED
@@ -104,6 +104,12 @@ module Shared
104
104
 
105
105
  new4.reload
106
106
  assert_equal 5, new4.pos
107
+
108
+ last1 = ListMixin.order('pos').last
109
+ last2 = ListMixin.order('pos').last
110
+ last1.insert_at(1)
111
+ last2.insert_at(1)
112
+ assert_equal [1, 2, 3, 4, 5], ListMixin.where(parent_id: 20).order('pos').map(&:pos)
107
113
  end
108
114
 
109
115
  def test_delete_middle
@@ -142,7 +148,7 @@ module Shared
142
148
 
143
149
  def test_update_position_when_scope_changes
144
150
  assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
145
- parent = ListMixin.create(parent_id: 6)
151
+ ListMixin.create(parent_id: 6)
146
152
 
147
153
  ListMixin.where(id: 2).first.move_within_scope(6)
148
154
 
@@ -246,5 +252,11 @@ module Shared
246
252
 
247
253
  assert_equal [5, 1, 6, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
248
254
  end
255
+
256
+ def test_non_persisted_records_dont_get_lock_called
257
+ new = ListMixin.new(parent_id: 5)
258
+
259
+ new.destroy
260
+ end
249
261
  end
250
262
  end
@@ -43,6 +43,32 @@ module Shared
43
43
  assert_nil ListMixin.where(id: 4).first.lower_item
44
44
  end
45
45
 
46
+ def test_next_prev_not_regular_sequence
47
+ ListMixin.all.each do |item|
48
+ item.update_attributes(pos: item.pos * 5)
49
+ end
50
+
51
+ assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
52
+
53
+ ListMixin.where(id: 2).first.move_lower
54
+ assert_equal [1, 3, 2, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
55
+
56
+ ListMixin.where(id: 2).first.move_higher
57
+ assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
58
+
59
+ ListMixin.where(id: 1).first.move_to_bottom
60
+ assert_equal [2, 3, 4, 1], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
61
+
62
+ ListMixin.where(id: 1).first.move_to_top
63
+ assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
64
+
65
+ ListMixin.where(id: 2).first.move_to_bottom
66
+ assert_equal [1, 3, 4, 2], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
67
+
68
+ ListMixin.where(id: 4).first.move_to_top
69
+ assert_equal [4, 1, 3, 2], ListMixin.where(parent_id: 5000).order('pos').map(&:id)
70
+ end
71
+
46
72
  def test_next_prev_groups
47
73
  li1 = ListMixin.where(id: 1).first
48
74
  li2 = ListMixin.where(id: 2).first
@@ -53,9 +79,9 @@ module Shared
53
79
  assert_equal [li2, li3], li1.lower_items(2)
54
80
  assert_equal [], li4.lower_items
55
81
 
56
- assert_equal [li1, li2], li3.higher_items
82
+ assert_equal [li2, li1], li3.higher_items
57
83
  assert_equal [li1], li2.higher_items
58
- assert_equal [li2, li3], li4.higher_items(2)
84
+ assert_equal [li3, li2], li4.higher_items(2)
59
85
  assert_equal [], li1.higher_items
60
86
  end
61
87
 
@@ -21,5 +21,16 @@ module Shared
21
21
  assert !new.in_list?
22
22
  end
23
23
 
24
+ def test_update_scope_does_not_add_to_list
25
+ new = NoAdditionMixin.create
26
+
27
+ new.update_attribute(:parent_id, 20)
28
+ new.reload
29
+ assert !new.in_list?
30
+
31
+ new.update_attribute(:parent_id, 5)
32
+ new.reload
33
+ assert !new.in_list?
34
+ end
24
35
  end
25
36
  end
@@ -0,0 +1,21 @@
1
+ module Shared
2
+ module Quoting
3
+
4
+ def setup
5
+ 3.times { |counter| QuotedList.create! order: counter }
6
+ end
7
+
8
+ def test_create
9
+ assert_equal QuotedList.in_list.size, 3
10
+ end
11
+
12
+ # This test execute raw queries involving table name
13
+ def test_moving
14
+ item = QuotedList.first
15
+ item.higher_items
16
+ item.lower_items
17
+ item.send :bottom_item # Part of private api
18
+ end
19
+
20
+ end
21
+ end