acts_as_list 0.7.2 → 0.7.7
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/.gitignore +2 -0
- data/.travis.yml +21 -0
- data/Appraisals +13 -3
- data/CHANGELOG.md +316 -0
- data/Gemfile +5 -2
- data/README.md +14 -4
- data/Rakefile +8 -0
- data/gemfiles/rails_3_2.gemfile +6 -2
- data/gemfiles/rails_4_1.gemfile +6 -2
- data/gemfiles/rails_4_2.gemfile +5 -2
- data/gemfiles/rails_5_0.gemfile +24 -0
- data/lib/acts_as_list/active_record/acts/list.rb +135 -117
- data/lib/acts_as_list/version.rb +1 -1
- data/test/helper.rb +7 -0
- data/test/shared.rb +1 -0
- data/test/shared_list.rb +13 -1
- data/test/shared_no_addition.rb +11 -0
- data/test/shared_quoting.rb +21 -0
- data/test/test_joined_list.rb +64 -0
- data/test/test_list.rb +183 -3
- metadata +17 -14
- data/gemfiles/rails_3_2.gemfile.lock +0 -258
- data/gemfiles/rails_4_1.gemfile.lock +0 -262
- data/gemfiles/rails_4_2.gemfile.lock +0 -53
@@ -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
|
-
|
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
|
-
{
|
48
|
+
{ #{configuration[:scope]}: send(:#{configuration[:scope]}) }
|
47
49
|
end
|
48
50
|
|
49
51
|
def scope_changed?
|
50
|
-
|
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
|
56
|
-
|
57
|
-
|
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
|
-
(
|
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
|
-
|
80
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
|
@@ -221,10 +251,7 @@ module ActiveRecord
|
|
221
251
|
# Return the next higher item in the list.
|
222
252
|
def higher_item
|
223
253
|
return nil unless in_list?
|
224
|
-
|
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
|
254
|
+
higher_items(1).first
|
228
255
|
end
|
229
256
|
|
230
257
|
# Return the next n higher items in the list
|
@@ -233,19 +260,16 @@ module ActiveRecord
|
|
233
260
|
limit ||= acts_as_list_list.count
|
234
261
|
position_value = send(position_column)
|
235
262
|
acts_as_list_list.
|
236
|
-
where("#{
|
237
|
-
where("#{
|
263
|
+
where("#{quoted_position_column_with_table_name} < ?", position_value).
|
264
|
+
where("#{quoted_position_column_with_table_name} >= ?", position_value - limit).
|
238
265
|
limit(limit).
|
239
|
-
order("#{
|
266
|
+
order("#{quoted_position_column_with_table_name} ASC")
|
240
267
|
end
|
241
268
|
|
242
269
|
# Return the next lower item in the list.
|
243
270
|
def lower_item
|
244
271
|
return nil unless in_list?
|
245
|
-
|
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
|
272
|
+
lower_items(1).first
|
249
273
|
end
|
250
274
|
|
251
275
|
# Return the next n lower items in the list
|
@@ -254,10 +278,10 @@ module ActiveRecord
|
|
254
278
|
limit ||= acts_as_list_list.count
|
255
279
|
position_value = send(position_column)
|
256
280
|
acts_as_list_list.
|
257
|
-
where("#{
|
258
|
-
where("#{
|
281
|
+
where("#{quoted_position_column_with_table_name} > ?", position_value).
|
282
|
+
where("#{quoted_position_column_with_table_name} <= ?", position_value + limit).
|
259
283
|
limit(limit).
|
260
|
-
order("#{
|
284
|
+
order("#{quoted_position_column_with_table_name} ASC")
|
261
285
|
end
|
262
286
|
|
263
287
|
# Test if this record is in a list
|
@@ -293,14 +317,26 @@ module ActiveRecord
|
|
293
317
|
def add_to_list_top
|
294
318
|
increment_positions_on_all_items
|
295
319
|
self[position_column] = acts_as_list_top
|
320
|
+
# Make sure we know that we've processed this scope change already
|
321
|
+
@scope_changed = false
|
322
|
+
#dont halt the callback chain
|
323
|
+
true
|
296
324
|
end
|
297
325
|
|
326
|
+
# A poorly named method. It will insert the item at the desired position if the position
|
327
|
+
# has been set manually using position=, not necessarily the bottom of the list
|
298
328
|
def add_to_list_bottom
|
299
|
-
if not_in_list? ||
|
329
|
+
if not_in_list? || internal_scope_changed? && !position_changed || default_position?
|
300
330
|
self[position_column] = bottom_position_in_list.to_i + 1
|
301
331
|
else
|
302
332
|
increment_positions_on_lower_items(self[position_column], id)
|
303
333
|
end
|
334
|
+
|
335
|
+
# Make sure we know that we've processed this scope change already
|
336
|
+
@scope_changed = false
|
337
|
+
|
338
|
+
#dont halt the callback chain
|
339
|
+
true
|
304
340
|
end
|
305
341
|
|
306
342
|
# Overwrite this method to define the scope of the list changes
|
@@ -315,11 +351,12 @@ module ActiveRecord
|
|
315
351
|
|
316
352
|
# Returns the bottom item
|
317
353
|
def bottom_item(except = nil)
|
318
|
-
conditions =
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
354
|
+
conditions = except ? "#{quoted_table_name}.#{self.class.primary_key} != #{self.class.connection.quote(except.id)}" : {}
|
355
|
+
acts_as_list_list.in_list.where(
|
356
|
+
conditions
|
357
|
+
).order(
|
358
|
+
"#{quoted_position_column_with_table_name} DESC"
|
359
|
+
).first
|
323
360
|
end
|
324
361
|
|
325
362
|
# Forces item to assume the bottom position in the list.
|
@@ -334,110 +371,74 @@ module ActiveRecord
|
|
334
371
|
|
335
372
|
# This has the effect of moving all the higher items up one.
|
336
373
|
def decrement_positions_on_higher_items(position)
|
337
|
-
|
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
|
374
|
+
acts_as_list_list.where("#{quoted_position_column_with_table_name} <= ?", position).decrement_all
|
344
375
|
end
|
345
376
|
|
346
377
|
# This has the effect of moving all the lower items up one.
|
347
378
|
def decrement_positions_on_lower_items(position=nil)
|
348
379
|
return unless in_list?
|
349
380
|
position ||= send(position_column).to_i
|
350
|
-
|
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
|
381
|
+
acts_as_list_list.where("#{quoted_position_column_with_table_name} > ?", position).decrement_all
|
357
382
|
end
|
358
383
|
|
359
384
|
# This has the effect of moving all the higher items down one.
|
360
385
|
def increment_positions_on_higher_items
|
361
386
|
return unless in_list?
|
362
|
-
|
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
|
387
|
+
acts_as_list_list.where("#{quoted_position_column_with_table_name} < #{send(position_column).to_i}").increment_all
|
369
388
|
end
|
370
389
|
|
371
390
|
# This has the effect of moving all the lower items down one.
|
372
391
|
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)}" : ''
|
392
|
+
avoid_id_condition = avoid_id ? " AND #{quoted_table_name}.#{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
|
374
393
|
|
375
|
-
|
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
|
394
|
+
acts_as_list_list.where("#{quoted_position_column_with_table_name} >= #{position}#{avoid_id_condition}").increment_all
|
382
395
|
end
|
383
396
|
|
384
397
|
# Increments position (<tt>position_column</tt>) of all items in the list.
|
385
398
|
def increment_positions_on_all_items
|
386
|
-
|
387
|
-
acts_as_list_class.where(
|
388
|
-
scope_condition
|
389
|
-
).update_all(
|
390
|
-
"#{position_column} = (#{position_column} + 1)"
|
391
|
-
)
|
392
|
-
end
|
399
|
+
acts_as_list_list.increment_all
|
393
400
|
end
|
394
401
|
|
395
402
|
# Reorders intermediate items to support moving an item from old_position to new_position.
|
396
403
|
def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil)
|
397
404
|
return if old_position == new_position
|
398
|
-
avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
|
405
|
+
avoid_id_condition = avoid_id ? " AND #{quoted_table_name}.#{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
|
399
406
|
|
400
407
|
if old_position < new_position
|
401
408
|
# Decrement position of intermediate items
|
402
409
|
#
|
403
410
|
# e.g., if moving an item from 2 to 5,
|
404
411
|
# move [3, 4, 5] to [2, 3, 4]
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
).update_all(
|
411
|
-
"#{position_column} = (#{position_column} - 1)"
|
412
|
-
)
|
413
|
-
end
|
412
|
+
acts_as_list_list.where(
|
413
|
+
"#{quoted_position_column_with_table_name} > ?", old_position
|
414
|
+
).where(
|
415
|
+
"#{quoted_position_column_with_table_name} <= #{new_position}#{avoid_id_condition}"
|
416
|
+
).decrement_all
|
414
417
|
else
|
415
418
|
# Increment position of intermediate items
|
416
419
|
#
|
417
420
|
# e.g., if moving an item from 5 to 2,
|
418
421
|
# move [2, 3, 4] to [3, 4, 5]
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
).update_all(
|
425
|
-
"#{position_column} = (#{position_column} + 1)"
|
426
|
-
)
|
427
|
-
end
|
422
|
+
acts_as_list_list.where(
|
423
|
+
"#{quoted_position_column_with_table_name} >= ?", new_position
|
424
|
+
).where(
|
425
|
+
"#{quoted_position_column_with_table_name} < #{old_position}#{avoid_id_condition}"
|
426
|
+
).increment_all
|
428
427
|
end
|
429
428
|
end
|
430
429
|
|
431
430
|
def insert_at_position(position)
|
432
431
|
return set_list_position(position) if new_record?
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
432
|
+
with_lock do
|
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)
|
439
|
+
end
|
440
|
+
set_list_position(position)
|
439
441
|
end
|
440
|
-
set_list_position(position)
|
441
442
|
end
|
442
443
|
|
443
444
|
# used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint
|
@@ -453,29 +454,32 @@ module ActiveRecord
|
|
453
454
|
old_position = send("#{position_column}_was").to_i
|
454
455
|
new_position = send(position_column).to_i
|
455
456
|
|
456
|
-
return unless
|
457
|
-
|
458
|
-
|
457
|
+
return unless acts_as_list_list.where(
|
458
|
+
"#{quoted_position_column_with_table_name} = #{new_position}"
|
459
|
+
).count > 1
|
459
460
|
shuffle_positions_on_intermediate_items old_position, new_position, id
|
460
461
|
end
|
461
462
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
463
|
+
def internal_scope_changed?
|
464
|
+
return @scope_changed if defined?(@scope_changed)
|
465
|
+
|
466
|
+
@scope_changed = scope_changed?
|
467
|
+
end
|
468
|
+
|
469
|
+
def clear_scope_changed
|
470
|
+
remove_instance_variable(:@scope_changed) if defined?(@scope_changed)
|
466
471
|
end
|
467
472
|
|
468
473
|
def check_scope
|
469
|
-
if
|
470
|
-
|
474
|
+
if internal_scope_changed?
|
475
|
+
cached_changes = changes
|
476
|
+
|
477
|
+
cached_changes.each { |attribute, values| self[attribute] = values[0] }
|
471
478
|
send('decrement_positions_on_lower_items') if lower_item
|
472
|
-
|
473
|
-
send("add_to_list_#{add_new_at}")
|
474
|
-
end
|
475
|
-
end
|
479
|
+
cached_changes.each { |attribute, values| self[attribute] = values[1] }
|
476
480
|
|
477
|
-
|
478
|
-
|
481
|
+
send("add_to_list_#{add_new_at}") if add_new_at.present?
|
482
|
+
end
|
479
483
|
end
|
480
484
|
|
481
485
|
# This check is skipped if the position is currently the default position from the table
|
@@ -485,6 +489,20 @@ module ActiveRecord
|
|
485
489
|
self[position_column] = acts_as_list_top
|
486
490
|
end
|
487
491
|
end
|
492
|
+
|
493
|
+
# When using raw column name it must be quoted otherwise it can raise syntax errors with SQL keywords (e.g. order)
|
494
|
+
def quoted_position_column
|
495
|
+
@_quoted_position_column ||= self.class.connection.quote_column_name(position_column)
|
496
|
+
end
|
497
|
+
|
498
|
+
# Used in order clauses
|
499
|
+
def quoted_table_name
|
500
|
+
@_quoted_table_name ||= acts_as_list_class.quoted_table_name
|
501
|
+
end
|
502
|
+
|
503
|
+
def quoted_position_column_with_table_name
|
504
|
+
@_quoted_position_column_with_table_name ||= "#{quoted_table_name}.#{quoted_position_column}"
|
505
|
+
end
|
488
506
|
end
|
489
507
|
end
|
490
508
|
end
|
data/lib/acts_as_list/version.rb
CHANGED
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
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
|
-
|
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
|
data/test/shared_no_addition.rb
CHANGED
@@ -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
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
4
|
+
ActiveRecord::Schema.verbose = false
|
5
|
+
|
6
|
+
class Section < ActiveRecord::Base
|
7
|
+
has_many :items
|
8
|
+
acts_as_list
|
9
|
+
|
10
|
+
scope :visible, -> { where(visible: true) }
|
11
|
+
end
|
12
|
+
|
13
|
+
class Item < ActiveRecord::Base
|
14
|
+
belongs_to :section
|
15
|
+
acts_as_list scope: :section
|
16
|
+
|
17
|
+
scope :visible, -> { where(visible: true).joins(:section).merge(Section.visible) }
|
18
|
+
end
|
19
|
+
|
20
|
+
class JoinedTestCase < Minitest::Test
|
21
|
+
def setup
|
22
|
+
ActiveRecord::Base.connection.create_table :sections do |t|
|
23
|
+
t.column :position, :integer
|
24
|
+
t.column :visible, :boolean, default: true
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveRecord::Base.connection.create_table :items do |t|
|
28
|
+
t.column :position, :integer
|
29
|
+
t.column :section_id, :integer
|
30
|
+
t.column :visible, :boolean, default: true
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveRecord::Base.connection.schema_cache.clear!
|
34
|
+
[Section, Item].each(&:reset_column_information)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown
|
39
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
40
|
+
ActiveRecord::Base.connection.drop_table(table)
|
41
|
+
end
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# joining the relation returned by `#higher_items` or `#lower_items` to another table
|
47
|
+
# previously could result in ambiguous column names in the query
|
48
|
+
class TestHigherLowerItems < JoinedTestCase
|
49
|
+
def test_higher_items
|
50
|
+
section = Section.create
|
51
|
+
item1 = Item.create section: section
|
52
|
+
item2 = Item.create section: section
|
53
|
+
item3 = Item.create section: section
|
54
|
+
assert_equal item3.higher_items.visible, [item1, item2]
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_lower_items
|
58
|
+
section = Section.create
|
59
|
+
item1 = Item.create section: section
|
60
|
+
item2 = Item.create section: section
|
61
|
+
item3 = Item.create section: section
|
62
|
+
assert_equal item1.lower_items.visible, [item2, item3]
|
63
|
+
end
|
64
|
+
end
|