acts_as_list 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5cbf688ac985c43f61b4c0669030f5b6f6c6caa
4
- data.tar.gz: aef8fc0d40ed0ee55bf73ff2b7989e14e9c6572e
3
+ metadata.gz: 0cf6c26261c5ef1690443023c346ddf8de5f5a95
4
+ data.tar.gz: 419d7210f9a06bd54a113da0b8e8c929f716f4b6
5
5
  SHA512:
6
- metadata.gz: de7f8dd3123cb1bf1fd5837517c221d8a6682207c3a75ac99a7457782b5ebf6d3fa995ee12664674cc7dfacaca6bd7e5ffaa484407502856aa8e9932ff066746
7
- data.tar.gz: 20d069cc6a4420dd3dd6073f12ab8940cf2bb3c1003bd71faca489ddefe780349159696cd12ef37e65578ac796c0d4686af3c231cff832f3eb2b2a3306f2da62
6
+ metadata.gz: 07d0a625d3d0ea4344b4fea41be748b0191a0495edd5255e679c3c543b3101e70711f47595f5f6009f2490f01cf7c8e5683370254a65d654bed00ec7e6eb5265
7
+ data.tar.gz: aaea3b5d86259b0564ea278a7dc936b2826b82a66d6c25e8abc33cb9124d434fb4a57de70c01751176ef285d8d36a25f8b62b9b60e18a6dcf198713fbe5cd2d6
@@ -5,7 +5,7 @@ rvm:
5
5
  - 2.0.0
6
6
  - 2.1.0
7
7
  - jruby-19mode
8
- - rbx
8
+ - rbx-2
9
9
  gemfile:
10
10
  - gemfiles/rails3/Gemfile
11
11
  - gemfiles/rails4/Gemfile
data/Gemfile CHANGED
@@ -12,4 +12,8 @@ end
12
12
  # Specify your gem's dependencies in acts_as_list-rails3.gemspec
13
13
  gemspec
14
14
 
15
- gem 'rake'
15
+ gem 'rake'
16
+
17
+ group :test do
18
+ gem 'minitest', '~> 5'
19
+ end
data/README.md CHANGED
@@ -76,6 +76,15 @@ All `position` queries (select, update, etc.) inside gem methods are executed wi
76
76
 
77
77
  The `position` column is set after validations are called, so you should not put a `presence` validation on the `position` column.
78
78
 
79
+
80
+ If you need a scope by a non-association field you should pass an array, containing field name, to a scope:
81
+ ```ruby
82
+ class TodoItem < ActiveRecord::Base
83
+ # `kind` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association
84
+ acts_as_list scope: [:kind]
85
+ end
86
+ ```
87
+
79
88
  ## Versions
80
89
  All versions `0.1.5` onwards require Rails 3.0.x and higher.
81
90
 
@@ -15,3 +15,7 @@ gem 'activerecord', '>= 3.0', '< 4'
15
15
  gemspec :path => File.join('..', '..')
16
16
 
17
17
  gem 'rake'
18
+
19
+ group :test do
20
+ gem 'minitest', '~> 5'
21
+ end
@@ -9,10 +9,13 @@ platforms :rbx do
9
9
  gem 'rubysl-test-unit'
10
10
  end
11
11
 
12
-
13
12
  gem 'activerecord', '>= 4.0.0', '< 5'
14
13
 
15
14
  # Specify your gem's dependencies in acts_as_list.gemspec
16
15
  gemspec :path => File.join('..', '..')
17
16
 
18
17
  gem 'rake'
18
+
19
+ group :test do
20
+ gem 'minitest', '~> 5'
21
+ end
data/init.rb CHANGED
@@ -1,4 +1,2 @@
1
1
  $:.unshift "#{File.dirname(__FILE__)}/lib"
2
2
  require 'acts_as_list'
3
-
4
- ActsAsList::Railtie.insert
@@ -1,24 +1,17 @@
1
1
  require 'acts_as_list/active_record/acts/list'
2
2
 
3
3
  module ActsAsList
4
- if defined? Rails::Railtie
4
+ begin
5
5
  require 'rails'
6
+
6
7
  class Railtie < Rails::Railtie
7
8
  initializer 'acts_as_list.insert_into_active_record' do
8
9
  ActiveSupport.on_load :active_record do
9
- ActsAsList::Railtie.insert
10
+ ActiveRecord::Base.send(:include, ActiveRecord::Acts::List)
10
11
  end
11
12
  end
12
13
  end
13
- end
14
-
15
- class Railtie
16
- def self.insert
17
- if defined?(ActiveRecord)
18
- ActiveRecord::Base.send(:include, ActiveRecord::Acts::List)
19
- end
20
- end
14
+ rescue LoadError
15
+ ActiveRecord::Base.send(:include, ActiveRecord::Acts::List) if defined?(ActiveRecord)
21
16
  end
22
17
  end
23
-
24
- ActsAsList::Railtie.insert
@@ -99,6 +99,11 @@ module ActiveRecord
99
99
  '#{configuration[:add_new_at]}'
100
100
  end
101
101
 
102
+ def #{configuration[:column]}=(position)
103
+ write_attribute(:#{configuration[:column]}, position)
104
+ @position_changed = true
105
+ end
106
+
102
107
  #{scope_methods}
103
108
 
104
109
  # only add to attr_accessible
@@ -182,7 +187,8 @@ module ActiveRecord
182
187
  end
183
188
  end
184
189
 
185
- # Move the item within scope
190
+ # Move the item within scope. If a position within the new scope isn't supplied, the item will
191
+ # be appended to the end of the list.
186
192
  def move_within_scope(scope_id)
187
193
  send("#{scope_name}=", scope_id)
188
194
  save!
@@ -215,9 +221,10 @@ module ActiveRecord
215
221
  # Return the next higher item in the list.
216
222
  def higher_item
217
223
  return nil unless in_list?
218
- acts_as_list_class.unscoped.
219
- where("#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}").
224
+ acts_as_list_class.unscoped do
225
+ acts_as_list_class.where("#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}").
220
226
  order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
227
+ end
221
228
  end
222
229
 
223
230
  # Return the next n higher items in the list
@@ -235,9 +242,10 @@ module ActiveRecord
235
242
  # Return the next lower item in the list.
236
243
  def lower_item
237
244
  return nil unless in_list?
238
- acts_as_list_class.unscoped.
239
- where("#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}").
245
+ acts_as_list_class.unscoped do
246
+ acts_as_list_class.where("#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}").
240
247
  order("#{acts_as_list_class.table_name}.#{position_column} ASC").first
248
+ end
241
249
  end
242
250
 
243
251
  # Return the next n lower items in the list
@@ -266,19 +274,20 @@ module ActiveRecord
266
274
  end
267
275
 
268
276
  def default_position?
269
- default_position == send(position_column)
277
+ default_position && default_position.to_i == send(position_column)
270
278
  end
271
279
 
272
280
  # Sets the new position and saves it
273
281
  def set_list_position(new_position)
274
- send("#{position_column}=", new_position)
275
- save!
282
+ write_attribute position_column, new_position
283
+ save(validate: false)
276
284
  end
277
285
 
278
286
  private
279
287
  def acts_as_list_list
280
- acts_as_list_class.unscoped.
281
- where(scope_condition)
288
+ acts_as_list_class.unscoped do
289
+ acts_as_list_class.where(scope_condition)
290
+ end
282
291
  end
283
292
 
284
293
  def add_to_list_top
@@ -287,10 +296,10 @@ module ActiveRecord
287
296
  end
288
297
 
289
298
  def add_to_list_bottom
290
- if not_in_list? || default_position?
299
+ if not_in_list? || scope_changed? && !@position_changed || default_position?
291
300
  self[position_column] = bottom_position_in_list.to_i + 1
292
301
  else
293
- increment_positions_on_lower_items(self[position_column])
302
+ increment_positions_on_lower_items(self[position_column], id)
294
303
  end
295
304
  end
296
305
 
@@ -307,8 +316,10 @@ module ActiveRecord
307
316
  # Returns the bottom item
308
317
  def bottom_item(except = nil)
309
318
  conditions = scope_condition
310
- conditions = "#{conditions} AND #{self.class.primary_key} != '#{except.id}'" if except
311
- acts_as_list_class.unscoped.in_list.where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
319
+ conditions = "#{conditions} AND #{self.class.primary_key} != #{self.class.connection.quote(except.id)}" if except
320
+ acts_as_list_class.unscoped do
321
+ acts_as_list_class.in_list.where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
322
+ end
312
323
  end
313
324
 
314
325
  # Forces item to assume the bottom position in the list.
@@ -323,76 +334,93 @@ module ActiveRecord
323
334
 
324
335
  # This has the effect of moving all the higher items up one.
325
336
  def decrement_positions_on_higher_items(position)
326
- acts_as_list_class.unscoped.where(
327
- "#{scope_condition} AND #{position_column} <= #{position}"
328
- ).update_all(
329
- "#{position_column} = (#{position_column} - 1)"
330
- )
337
+ acts_as_list_class.unscoped do
338
+ acts_as_list_class.where(
339
+ "#{scope_condition} AND #{position_column} <= #{position}"
340
+ ).update_all(
341
+ "#{position_column} = (#{position_column} - 1)"
342
+ )
343
+ end
331
344
  end
332
345
 
333
346
  # This has the effect of moving all the lower items up one.
334
347
  def decrement_positions_on_lower_items(position=nil)
335
348
  return unless in_list?
336
349
  position ||= send(position_column).to_i
337
- acts_as_list_class.unscoped.where(
338
- "#{scope_condition} AND #{position_column} > #{position}"
339
- ).update_all(
340
- "#{position_column} = (#{position_column} - 1)"
341
- )
350
+ acts_as_list_class.unscoped do
351
+ acts_as_list_class.where(
352
+ "#{scope_condition} AND #{position_column} > #{position}"
353
+ ).update_all(
354
+ "#{position_column} = (#{position_column} - 1)"
355
+ )
356
+ end
342
357
  end
343
358
 
344
359
  # This has the effect of moving all the higher items down one.
345
360
  def increment_positions_on_higher_items
346
361
  return unless in_list?
347
- acts_as_list_class.unscoped.where(
348
- "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
349
- ).update_all(
350
- "#{position_column} = (#{position_column} + 1)"
351
- )
362
+ acts_as_list_class.unscoped do
363
+ acts_as_list_class.where(
364
+ "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
365
+ ).update_all(
366
+ "#{position_column} = (#{position_column} + 1)"
367
+ )
368
+ end
352
369
  end
353
370
 
354
371
  # This has the effect of moving all the lower items down one.
355
- def increment_positions_on_lower_items(position)
356
- acts_as_list_class.unscoped.where(
357
- "#{scope_condition} AND #{position_column} >= #{position}"
358
- ).update_all(
359
- "#{position_column} = (#{position_column} + 1)"
360
- )
372
+ 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)}" : ''
374
+
375
+ acts_as_list_class.unscoped do
376
+ acts_as_list_class.where(
377
+ "#{scope_condition} AND #{position_column} >= #{position}#{avoid_id_condition}"
378
+ ).update_all(
379
+ "#{position_column} = (#{position_column} + 1)"
380
+ )
381
+ end
361
382
  end
362
383
 
363
384
  # Increments position (<tt>position_column</tt>) of all items in the list.
364
385
  def increment_positions_on_all_items
365
- acts_as_list_class.unscoped.where(
366
- "#{scope_condition}"
367
- ).update_all(
368
- "#{position_column} = (#{position_column} + 1)"
369
- )
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
370
393
  end
371
394
 
372
395
  # Reorders intermediate items to support moving an item from old_position to new_position.
373
396
  def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil)
374
397
  return if old_position == new_position
375
- avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != '#{avoid_id}'" : ''
398
+ avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : ''
399
+
376
400
  if old_position < new_position
377
401
  # Decrement position of intermediate items
378
402
  #
379
403
  # e.g., if moving an item from 2 to 5,
380
404
  # move [3, 4, 5] to [2, 3, 4]
381
- acts_as_list_class.unscoped.where(
382
- "#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}#{avoid_id_condition}"
383
- ).update_all(
384
- "#{position_column} = (#{position_column} - 1)"
385
- )
405
+ acts_as_list_class.unscoped do
406
+ acts_as_list_class.where(
407
+ "#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}#{avoid_id_condition}"
408
+ ).update_all(
409
+ "#{position_column} = (#{position_column} - 1)"
410
+ )
411
+ end
386
412
  else
387
413
  # Increment position of intermediate items
388
414
  #
389
415
  # e.g., if moving an item from 5 to 2,
390
416
  # move [2, 3, 4] to [3, 4, 5]
391
- acts_as_list_class.unscoped.where(
392
- "#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}#{avoid_id_condition}"
393
- ).update_all(
394
- "#{position_column} = (#{position_column} + 1)"
395
- )
417
+ acts_as_list_class.unscoped do
418
+ acts_as_list_class.where(
419
+ "#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}#{avoid_id_condition}"
420
+ ).update_all(
421
+ "#{position_column} = (#{position_column} + 1)"
422
+ )
423
+ end
396
424
  end
397
425
  end
398
426
 
@@ -421,7 +449,9 @@ module ActiveRecord
421
449
  old_position = send("#{position_column}_was").to_i
422
450
  new_position = send(position_column).to_i
423
451
 
424
- return unless acts_as_list_class.unscoped.where("#{scope_condition} AND #{position_column} = #{new_position}").count > 1
452
+ return unless acts_as_list_class.unscoped do
453
+ acts_as_list_class.where("#{scope_condition} AND #{position_column} = #{new_position}").count > 1
454
+ end
425
455
  shuffle_positions_on_intermediate_items old_position, new_position, id
426
456
  end
427
457
 
@@ -444,8 +474,10 @@ module ActiveRecord
444
474
  self.reload
445
475
  end
446
476
 
477
+ # This check is skipped if the position is currently the default position from the table
478
+ # as modifying the default position on creation is handled elsewhere
447
479
  def check_top_position
448
- if send(position_column) && send(position_column) < acts_as_list_top
480
+ if send(position_column) && !default_position? && send(position_column) < acts_as_list_top
449
481
  self[position_column] = acts_as_list_top
450
482
  end
451
483
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module Acts
3
3
  module List
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
6
6
  end
7
7
  end
@@ -137,12 +137,12 @@ module Shared
137
137
  def test_nil_scope
138
138
  new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
139
139
  new2.move_higher
140
- assert_equal [new2, new1, new3], ListMixin.where(parent_id: nil).order('pos')
140
+ assert_equal [new2, new1, new3].map(&:id), ListMixin.where(parent_id: nil).order('pos').map(&:id)
141
141
  end
142
142
 
143
143
  def test_update_position_when_scope_changes
144
144
  assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
145
- parent = ListMixin.create(id: 6)
145
+ parent = ListMixin.create(parent_id: 6)
146
146
 
147
147
  ListMixin.where(id: 2).first.move_within_scope(6)
148
148
 
@@ -155,7 +155,7 @@ module Shared
155
155
  assert_equal 3, ListMixin.where(id: 4).first.pos
156
156
 
157
157
  ListMixin.where(id: 2).first.move_within_scope(5)
158
- assert_equal [1, 2, 3, 4], ListMixin.where(parent_id: 5).order('pos').map(&:id)
158
+ assert_equal [1, 3, 4, 2], ListMixin.where(parent_id: 5).order('pos').map(&:id)
159
159
  end
160
160
 
161
161
  def test_remove_from_list_should_then_fail_in_list?
@@ -6,16 +6,21 @@ ActiveRecord::Schema.verbose = false
6
6
 
7
7
  def setup_db(position_options = {})
8
8
  # AR caches columns options like defaults etc. Clear them!
9
+ ActiveRecord::Base.connection.create_table :mixins do |t|
10
+ t.column :pos, :integer, position_options
11
+ t.column :active, :boolean, default: true
12
+ t.column :parent_id, :integer
13
+ t.column :parent_type, :string
14
+ t.column :created_at, :datetime
15
+ t.column :updated_at, :datetime
16
+ end
17
+
9
18
  ActiveRecord::Base.connection.schema_cache.clear!
10
- ActiveRecord::Schema.define(version: 1) do
11
- create_table :mixins do |t|
12
- t.column :pos, :integer, position_options
13
- t.column :active, :boolean, default: true
14
- t.column :parent_id, :integer
15
- t.column :parent_type, :string
16
- t.column :created_at, :datetime
17
- t.column :updated_at, :datetime
18
- end
19
+ [ Mixin, ListMixin, ListMixinSub1, ListMixinSub2, ListWithStringScopeMixin,
20
+ ArrayScopeListMixin, ZeroBasedMixin, DefaultScopedMixin,
21
+ DefaultScopedWhereMixin, TopAdditionMixin, NoAdditionMixin ].each do |klass|
22
+
23
+ klass.reset_column_information
19
24
  end
20
25
  end
21
26
 
@@ -87,8 +92,8 @@ class NoAdditionMixin < Mixin
87
92
  acts_as_list column: "pos", add_new_at: nil, scope: :parent_id
88
93
  end
89
94
 
90
- class ActsAsListTestCase < MiniTest::Unit::TestCase
91
- # No default test required a this class is abstract.
95
+ class ActsAsListTestCase < Minitest::Test
96
+ # No default test required as this class is abstract.
92
97
  # Need for test/unit.
93
98
  undef_method :default_test if method_defined?(:default_test)
94
99
 
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.4.0
4
+ version: 0.5.0
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: 2014-02-22 00:00:00.000000000 Z
13
+ date: 2014-10-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  version: '0'
93
93
  requirements: []
94
94
  rubyforge_project: acts_as_list
95
- rubygems_version: 2.2.2
95
+ rubygems_version: 2.0.14
96
96
  signing_key:
97
97
  specification_version: 4
98
98
  summary: A gem allowing a active_record model to act_as_list.