acts_as_list 0.1.5 → 0.2.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.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  pkg/*
5
5
  .rvmrc
6
6
  *.tmproj
7
+ .rbenv-version
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in acts_as_list-rails3.gemspec
4
4
  gemspec
5
+
6
+ gem 'rake'
data/README.md CHANGED
@@ -2,26 +2,91 @@
2
2
 
3
3
  ## Description
4
4
 
5
- This `acts_as extension` provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a `position` column defined as an integer on the mapped database table.
5
+ This `acts_as` extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a `position` column defined as an integer on the mapped database table.
6
6
 
7
+ ## Installation
8
+
9
+ In your Gemfile:
10
+
11
+ gem 'acts_as_list'
12
+
13
+ Or, from the command line:
14
+
15
+ gem install acts_as_list
7
16
 
8
17
  ## Example
9
18
 
10
- class TodoList < ActiveRecord::Base
11
- has_many :todo_items, :order => "position"
12
- end
19
+ At first, you need to add a `position` column to desired table:
20
+
21
+ rails g migration AddPositionToTodoItem position:integer
22
+ rake db:migrate
13
23
 
14
- class TodoItem < ActiveRecord::Base
15
- belongs_to :todo_list
16
- acts_as_list :scope => :todo_list
17
- end
24
+ After that you can use `acts_as_list` method in the model:
25
+
26
+ ```ruby
27
+ class TodoList < ActiveRecord::Base
28
+ has_many :todo_items, order: :position
29
+ end
18
30
 
19
- todo_list.first.move_to_bottom
20
- todo_list.last.move_higher
31
+ class TodoItem < ActiveRecord::Base
32
+ belongs_to :todo_list
33
+ acts_as_list scope: :todo_list
34
+ end
21
35
 
36
+ todo_list.first.move_to_bottom
37
+ todo_list.last.move_higher
38
+ ```
39
+
40
+ ## Instance Methods Added To ActiveRecord Models
41
+
42
+ You'll have a number of methods added to each instance of the ActiveRecord model that to which `acts_as_list` is added.
43
+
44
+ In `acts_as_list`, "higher" means further up the list (a lower `position`), and "lower" means further down the list (a higher `position`). That can be confusing, so it might make sense to add tests that validate that you're using the right method given your context.
45
+
46
+ ### Methods That Change Position and Reorder List
47
+
48
+ - `list_item.insert_at(2)`
49
+ - `list_item.move_lower` will do nothing if the item is the lowest item
50
+ - `list_item.move_higher` will do nothing if the item is the highest item
51
+ - `list_item.move_to_bottom`
52
+ - `list_item.move_to_top`
53
+ - `list_item.remove_from_list`
54
+
55
+ ### Methods That Change Position Without Reordering List
56
+
57
+ - `list_item.increment_position`
58
+ - `list_item.decrement_position`
59
+ - `list_item.set_list_position(3)`
60
+
61
+ ### Methods That Return Attributes of the Item's List Position
62
+ - `list_item.first?`
63
+ - `list_item.last?`
64
+ - `list_item.in_list?`
65
+ - `list_item.not_in_list?`
66
+ - `list_item.default_position?`
67
+ - `list_item.higher_item`
68
+ - `list_item.higher_items` will return all the items above `list_item` in the list (ordered by the position, ascending)
69
+ - `list_item.lower_item`
70
+ - `list_item.lower_items` will return all the items below `list_item` in the list (ordered by the position, ascending)
71
+
22
72
  ## Notes
23
73
  If the `position` column has a default value, then there is a slight change in behavior, i.e if you have 4 items in the list, and you insert 1, with a default position 0, it would be pushed to the bottom of the list. Please look at the tests for this and some recent pull requests for discussions related to this.
24
74
 
75
+ All `position` queries (select, update, etc.) inside gem methods are executed without the default scope (i.e. `Model.unscoped`), this will prevent nasty issues when the default scope is different from `acts_as_list` scope.
76
+
77
+ The `position` column is set after validations are called, so you should not put a `presence` validation on the `position` column.
78
+
79
+ ## Versions
80
+ All versions `0.1.5` onwards require Rails 3.0.x and higher.
81
+
82
+ ## Build Status
83
+ [![Build Status](https://secure.travis-ci.org/swanandp/acts_as_list.png)](https://secure.travis-ci.org/swanandp/acts_as_list)
84
+
85
+ ## Roadmap
86
+
87
+ 1. Sort based feature
88
+ 2. Rails 4 compatibility and bye bye Rails 2! Older versions would of course continue to work with Rails 2, but there won't be any support on those.
89
+
25
90
  ## Contributing to `acts_as_list`
26
91
 
27
92
  - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
@@ -31,6 +96,7 @@ If the `position` column has a default value, then there is a slight change in b
31
96
  - Commit and push until you are happy with your contribution
32
97
  - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
33
98
  - Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
99
+ - I would recommend using Rails 3.1.x and higher for testing the build before a pull request. The current test harness does not quite work with 3.0.x. The plugin itself works, but the issue lies with testing infrastructure.
34
100
 
35
101
  ## Copyright
36
102
 
data/Rakefile CHANGED
@@ -12,11 +12,9 @@ desc 'Test the acts_as_list plugin.'
12
12
  Rake::TestTask.new(:test) do |t|
13
13
  t.libs << 'lib' << 'test'
14
14
  t.pattern = 'test/**/test_*.rb'
15
- t.verbose = true
15
+ t.verbose = false
16
16
  end
17
17
 
18
-
19
-
20
18
  # Run the rdoc task to generate rdocs for this gem
21
19
  require 'rdoc/task'
22
20
  RDoc::Task.new do |rdoc|
data/acts_as_list.gemspec CHANGED
@@ -24,8 +24,8 @@ Gem::Specification.new do |s|
24
24
 
25
25
 
26
26
  # Dependencies (installed via 'bundle install')...
27
- s.add_development_dependency("bundler", ["~> 1.0.0"])
28
- s.add_development_dependency("activerecord", [">= 1.15.4.7794"])
27
+ s.add_dependency("activerecord", [">= 3.0"])
28
+ s.add_development_dependency("bundler", [">= 1.0.0"])
29
29
  s.add_development_dependency("rdoc")
30
30
  s.add_development_dependency("sqlite3")
31
31
  end
data/init.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  $:.unshift "#{File.dirname(__FILE__)}/lib"
2
2
  require 'acts_as_list'
3
+
4
+ ActsAsList::Railtie.insert
@@ -26,14 +26,15 @@ module ActiveRecord
26
26
  # Configuration options are:
27
27
  #
28
28
  # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
29
- # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
30
- # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
29
+ # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
30
+ # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
31
31
  # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
32
32
  # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
33
33
  # * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection
34
34
  # act more like an array in its indexing.
35
+ # * +add_new_at+ - specifies whether objects get added to the :top or :bottom of the list. (default: +bottom+)
35
36
  def acts_as_list(options = {})
36
- configuration = { :column => "position", :scope => "1 = 1", :top_of_list => 1}
37
+ configuration = { :column => "position", :scope => "1 = 1", :top_of_list => 1, :add_new_at => :bottom}
37
38
  configuration.update(options) if options.is_a?(Hash)
38
39
 
39
40
  configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
@@ -47,7 +48,7 @@ module ActiveRecord
47
48
  elsif configuration[:scope].is_a?(Array)
48
49
  scope_condition_method = %(
49
50
  def scope_condition
50
- attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
51
+ attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
51
52
  memo[column.intern] = send(column.intern); memo
52
53
  end
53
54
  self.class.send(:sanitize_sql_hash_for_conditions, attrs)
@@ -58,7 +59,7 @@ module ActiveRecord
58
59
  end
59
60
 
60
61
  class_eval <<-EOV
61
- include ActiveRecord::Acts::List::InstanceMethods
62
+ include ::ActiveRecord::Acts::List::InstanceMethods
62
63
 
63
64
  def acts_as_list_top
64
65
  #{configuration[:top_of_list]}.to_i
@@ -72,10 +73,28 @@ module ActiveRecord
72
73
  '#{configuration[:column]}'
73
74
  end
74
75
 
76
+ def scope_name
77
+ '#{configuration[:scope]}'
78
+ end
79
+
80
+ def add_new_at
81
+ '#{configuration[:add_new_at]}'
82
+ end
83
+
75
84
  #{scope_condition_method}
76
85
 
86
+ # only add to attr_accessible
87
+ # if the class has some mass_assignment_protection
88
+
89
+ if defined?(accessible_attributes) and !accessible_attributes.blank?
90
+ attr_accessible :#{configuration[:column]}
91
+ end
92
+
93
+ before_destroy :reload_position
77
94
  after_destroy :decrement_positions_on_lower_items
78
- before_validation :add_to_list_bottom, :on => :create
95
+ before_create :add_to_list_#{configuration[:add_new_at]}
96
+ after_update :update_positions
97
+ before_update :check_scope
79
98
  EOV
80
99
  end
81
100
  end
@@ -134,20 +153,20 @@ module ActiveRecord
134
153
  def remove_from_list
135
154
  if in_list?
136
155
  decrement_positions_on_lower_items
137
- update_attribute position_column, nil
156
+ set_list_position(nil)
138
157
  end
139
158
  end
140
159
 
141
160
  # Increase the position of this item without adjusting the rest of the list.
142
161
  def increment_position
143
162
  return unless in_list?
144
- update_attribute position_column, self.send(position_column).to_i + 1
163
+ set_list_position(self.send(position_column).to_i + 1)
145
164
  end
146
165
 
147
166
  # Decrease the position of this item without adjusting the rest of the list.
148
167
  def decrement_position
149
168
  return unless in_list?
150
- update_attribute position_column, self.send(position_column).to_i - 1
169
+ set_list_position(self.send(position_column).to_i - 1)
151
170
  end
152
171
 
153
172
  # Return +true+ if this object is the first in the list.
@@ -165,28 +184,54 @@ module ActiveRecord
165
184
  # Return the next higher item in the list.
166
185
  def higher_item
167
186
  return nil unless in_list?
168
- acts_as_list_class.find(:first, :conditions =>
169
- "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
187
+ acts_as_list_class.unscoped.find(:first, :conditions =>
188
+ "#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}",
189
+ :order => "#{acts_as_list_class.table_name}.#{position_column} DESC"
170
190
  )
171
191
  end
172
192
 
193
+ # Return the next n higher items in the list
194
+ # selects all higher items by default
195
+ def higher_items(limit=nil)
196
+ limit ||= acts_as_list_list.count
197
+ position_value = send(position_column)
198
+ acts_as_list_list.
199
+ where("#{position_column} < ?", position_value).
200
+ where("#{position_column} >= ?", position_value - limit).
201
+ limit(limit).
202
+ order("#{acts_as_list_class.table_name}.#{position_column} ASC")
203
+ end
204
+
173
205
  # Return the next lower item in the list.
174
206
  def lower_item
175
207
  return nil unless in_list?
176
- acts_as_list_class.find(:first, :conditions =>
177
- "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
208
+ acts_as_list_class.unscoped.find(:first, :conditions =>
209
+ "#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}",
210
+ :order => "#{acts_as_list_class.table_name}.#{position_column} ASC"
178
211
  )
179
212
  end
180
213
 
214
+ # Return the next n lower items in the list
215
+ # selects all lower items by default
216
+ def lower_items(limit=nil)
217
+ limit ||= acts_as_list_list.count
218
+ position_value = send(position_column)
219
+ acts_as_list_list.
220
+ where("#{position_column} > ?", position_value).
221
+ where("#{position_column} <= ?", position_value + limit).
222
+ limit(limit).
223
+ order("#{acts_as_list_class.table_name}.#{position_column} ASC")
224
+ end
225
+
181
226
  # Test if this record is in a list
182
227
  def in_list?
183
228
  !not_in_list?
184
229
  end
185
-
230
+
186
231
  def not_in_list?
187
232
  send(position_column).nil?
188
233
  end
189
-
234
+
190
235
  def default_position
191
236
  acts_as_list_class.columns_hash[position_column.to_s].default
192
237
  end
@@ -195,9 +240,21 @@ module ActiveRecord
195
240
  default_position == send(position_column)
196
241
  end
197
242
 
243
+ # Sets the new position and saves it
244
+ def set_list_position(new_position)
245
+ send("#{position_column}=", new_position)
246
+ save!
247
+ end
248
+
198
249
  private
250
+ def acts_as_list_list
251
+ acts_as_list_class.unscoped.
252
+ where(scope_condition)
253
+ end
254
+
199
255
  def add_to_list_top
200
256
  increment_positions_on_all_items
257
+ self[position_column] = acts_as_list_top
201
258
  end
202
259
 
203
260
  def add_to_list_bottom
@@ -222,23 +279,25 @@ module ActiveRecord
222
279
  def bottom_item(except = nil)
223
280
  conditions = scope_condition
224
281
  conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
225
- acts_as_list_class.unscoped.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
282
+ acts_as_list_class.unscoped.where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first
226
283
  end
227
284
 
228
285
  # Forces item to assume the bottom position in the list.
229
286
  def assume_bottom_position
230
- update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
287
+ set_list_position(bottom_position_in_list(self).to_i + 1)
231
288
  end
232
289
 
233
290
  # Forces item to assume the top position in the list.
234
291
  def assume_top_position
235
- update_attribute(position_column, acts_as_list_top)
292
+ set_list_position(acts_as_list_top)
236
293
  end
237
294
 
238
295
  # This has the effect of moving all the higher items up one.
239
296
  def decrement_positions_on_higher_items(position)
240
- acts_as_list_class.update_all(
241
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
297
+ acts_as_list_class.unscoped.where(
298
+ "#{scope_condition} AND #{position_column} <= #{position}"
299
+ ).update_all(
300
+ "#{position_column} = (#{position_column} - 1)"
242
301
  )
243
302
  end
244
303
 
@@ -246,52 +305,64 @@ module ActiveRecord
246
305
  def decrement_positions_on_lower_items(position=nil)
247
306
  return unless in_list?
248
307
  position ||= send(position_column).to_i
249
- acts_as_list_class.update_all(
250
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{position}"
308
+ acts_as_list_class.unscoped.where(
309
+ "#{scope_condition} AND #{position_column} > #{position}"
310
+ ).update_all(
311
+ "#{position_column} = (#{position_column} - 1)"
251
312
  )
252
313
  end
253
314
 
254
315
  # This has the effect of moving all the higher items down one.
255
316
  def increment_positions_on_higher_items
256
317
  return unless in_list?
257
- acts_as_list_class.update_all(
258
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
318
+ acts_as_list_class.unscoped.where(
319
+ "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
320
+ ).update_all(
321
+ "#{position_column} = (#{position_column} + 1)"
259
322
  )
260
323
  end
261
324
 
262
325
  # This has the effect of moving all the lower items down one.
263
326
  def increment_positions_on_lower_items(position)
264
- acts_as_list_class.update_all(
265
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
266
- )
327
+ acts_as_list_class.unscoped.where(
328
+ "#{scope_condition} AND #{position_column} >= #{position}"
329
+ ).update_all(
330
+ "#{position_column} = (#{position_column} + 1)"
331
+ )
267
332
  end
268
333
 
269
334
  # Increments position (<tt>position_column</tt>) of all items in the list.
270
335
  def increment_positions_on_all_items
271
- acts_as_list_class.update_all(
272
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
336
+ acts_as_list_class.unscoped.where(
337
+ "#{scope_condition}"
338
+ ).update_all(
339
+ "#{position_column} = (#{position_column} + 1)"
273
340
  )
274
341
  end
275
342
 
276
343
  # Reorders intermediate items to support moving an item from old_position to new_position.
277
- def shuffle_positions_on_intermediate_items(old_position, new_position)
344
+ def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil)
278
345
  return if old_position == new_position
279
-
346
+ avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{avoid_id}" : ''
280
347
  if old_position < new_position
281
348
  # Decrement position of intermediate items
282
349
  #
283
350
  # e.g., if moving an item from 2 to 5,
284
351
  # move [3, 4, 5] to [2, 3, 4]
285
- acts_as_list_class.update_all(
286
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}"
352
+ acts_as_list_class.unscoped.where(
353
+ "#{scope_condition} AND #{position_column} > #{old_position} AND #{position_column} <= #{new_position}#{avoid_id_condition}"
354
+ ).update_all(
355
+ "#{position_column} = (#{position_column} - 1)"
287
356
  )
288
357
  else
289
358
  # Increment position of intermediate items
290
359
  #
291
360
  # e.g., if moving an item from 5 to 2,
292
361
  # move [2, 3, 4] to [3, 4, 5]
293
- acts_as_list_class.update_all(
294
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}"
362
+ acts_as_list_class.unscoped.where(
363
+ "#{scope_condition} AND #{position_column} >= #{new_position} AND #{position_column} < #{old_position}#{avoid_id_condition}"
364
+ ).update_all(
365
+ "#{position_column} = (#{position_column} + 1)"
295
366
  )
296
367
  end
297
368
  end
@@ -304,18 +375,40 @@ module ActiveRecord
304
375
  else
305
376
  increment_positions_on_lower_items(position)
306
377
  end
307
- self.update_attribute(position_column, position)
378
+ set_list_position(position)
308
379
  end
309
380
 
310
381
  # used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint
311
382
  def store_at_0
312
383
  if in_list?
313
384
  old_position = send(position_column).to_i
314
- update_attribute(position_column, 0)
385
+ set_list_position(0)
315
386
  decrement_positions_on_lower_items(old_position)
316
387
  end
317
388
  end
318
- end
389
+
390
+ def update_positions
391
+ old_position = send("#{position_column}_was").to_i
392
+ new_position = send(position_column).to_i
393
+ return unless acts_as_list_class.unscoped.where("#{scope_condition} AND #{position_column} = #{new_position}").count > 1
394
+ shuffle_positions_on_intermediate_items old_position, new_position, id
395
+ end
396
+
397
+ def check_scope
398
+ if changes.include?("#{scope_name}")
399
+ old_scope_id = changes["#{scope_name}"].first
400
+ new_scope_id = changes["#{scope_name}"].last
401
+ self["#{scope_name}"] = old_scope_id
402
+ send("decrement_positions_on_lower_items")
403
+ self["#{scope_name}"] = new_scope_id
404
+ send("add_to_list_#{add_new_at}")
405
+ end
406
+ end
407
+
408
+ def reload_position
409
+ self.reload
410
+ end
411
+ end
319
412
  end
320
413
  end
321
414
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module Acts
3
3
  module List
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
7
7
  end
data/lib/acts_as_list.rb CHANGED
@@ -1,2 +1,24 @@
1
1
  require 'acts_as_list/active_record/acts/list'
2
- ActiveRecord::Base.class_eval { include ActiveRecord::Acts::List }
2
+
3
+ module ActsAsList
4
+ if defined? Rails::Railtie
5
+ require 'rails'
6
+ class Railtie < Rails::Railtie
7
+ initializer 'acts_as_list.insert_into_active_record' do
8
+ ActiveSupport.on_load :active_record do
9
+ ActsAsList::Railtie.insert
10
+ end
11
+ end
12
+ 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
21
+ end
22
+ end
23
+
24
+ ActsAsList::Railtie.insert
data/test/shared.rb CHANGED
@@ -4,4 +4,5 @@ module Shared
4
4
  autoload :ListSub, 'shared_list_sub'
5
5
  autoload :ZeroBased, 'shared_zero_based'
6
6
  autoload :ArrayScopeList, 'shared_array_scope_list'
7
+ autoload :TopAddition, 'shared_top_addition'
7
8
  end
@@ -2,6 +2,7 @@ module Shared
2
2
  module ArrayScopeList
3
3
  def setup
4
4
  (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter, :parent_id => 5, :parent_type => 'ParentClass' }
5
+ (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter, :parent_id => 6, :parent_type => 'ParentClass' }
5
6
  end
6
7
 
7
8
  def test_reordering
@@ -24,6 +25,10 @@ module Shared
24
25
 
25
26
  ArrayScopeListMixin.find(4).move_to_top
26
27
  assert_equal [4, 1, 3, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
28
+
29
+ ArrayScopeListMixin.find(4).insert_at(4)
30
+ assert_equal [1, 3, 2, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
31
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:pos)
27
32
  end
28
33
 
29
34
  def test_move_to_bottom_with_next_to_last_item
@@ -41,7 +46,6 @@ module Shared
41
46
 
42
47
  def test_injection
43
48
  item = ArrayScopeListMixin.new(:parent_id => 1, :parent_type => 'ParentClass')
44
- assert_equal '"mixins"."parent_id" = 1 AND "mixins"."parent_type" = \'ParentClass\'', item.scope_condition
45
49
  assert_equal "pos", item.position_column
46
50
  end
47
51
 
data/test/shared_list.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  module Shared
2
2
  module List
3
3
  def setup
4
- (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 5 }
4
+ (1..4).each do |counter|
5
+ node = ListMixin.new :parent_id => 5
6
+ node.pos = counter
7
+ node.save!
8
+ end
5
9
  end
6
10
 
7
11
  def test_reordering
@@ -102,17 +106,6 @@ module Shared
102
106
  assert_equal 5, new4.pos
103
107
  end
104
108
 
105
- def test_insert_middle_with_unsaved_item
106
- assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
107
-
108
- # new item, not saved yet
109
- insert_this = ListMixin.new(:parent_id => 5)
110
- insert_this.insert_at(2)
111
-
112
- assert_equal [1, 2, 3, 4, 5], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:pos)
113
- assert_equal [1, 5, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
114
- end
115
-
116
109
  def test_delete_middle
117
110
  assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
118
111
 
@@ -215,14 +208,18 @@ module Shared
215
208
  def test_before_create_callback_adds_to_given_position
216
209
  assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
217
210
 
218
- new = ListMixin.create(:pos => 1, :parent_id => 5)
211
+ new = ListMixin.new(:parent_id => 5)
212
+ new.pos = 1
213
+ new.save!
219
214
  assert_equal 1, new.pos
220
215
  assert new.first?
221
216
  assert !new.last?
222
217
 
223
218
  assert_equal [5, 1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
224
219
 
225
- new = ListMixin.create(:pos => 3, :parent_id => 5)
220
+ new = ListMixin.new(:parent_id => 5)
221
+ new.pos = 3
222
+ new.save!
226
223
  assert_equal 3, new.pos
227
224
  assert !new.first?
228
225
  assert !new.last?
@@ -1,7 +1,11 @@
1
1
  module Shared
2
2
  module ListSub
3
3
  def setup
4
- (1..4).each { |i| ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).create! :pos => i, :parent_id => 5000 }
4
+ (1..4).each do |i|
5
+ node = ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).new :parent_id => 5000
6
+ node.pos = i
7
+ node.save!
8
+ end
5
9
  end
6
10
 
7
11
  def test_reordering
@@ -39,6 +43,22 @@ module Shared
39
43
  assert_nil ListMixin.find(4).lower_item
40
44
  end
41
45
 
46
+ def test_next_prev_groups
47
+ li1 = ListMixin.find(1)
48
+ li2 = ListMixin.find(2)
49
+ li3 = ListMixin.find(3)
50
+ li4 = ListMixin.find(4)
51
+ assert_equal [li2, li3, li4], li1.lower_items
52
+ assert_equal [li4], li3.lower_items
53
+ assert_equal [li2, li3], li1.lower_items(2)
54
+ assert_equal [], li4.lower_items
55
+
56
+ assert_equal [li1, li2], li3.higher_items
57
+ assert_equal [li1], li2.higher_items
58
+ assert_equal [li2, li3], li4.higher_items(2)
59
+ assert_equal [], li1.higher_items
60
+ end
61
+
42
62
  def test_injection
43
63
  item = ListMixin.new("parent_id"=>1)
44
64
  assert_equal '"mixins"."parent_id" = 1', item.scope_condition
@@ -52,7 +72,7 @@ module Shared
52
72
  new = ListMixinSub1.create("parent_id" => 20)
53
73
  assert_equal 2, new.pos
54
74
 
55
- new = ListMixinSub2.create("parent_id" => 20)
75
+ new = ListMixinSub1.create("parent_id" => 20)
56
76
  assert_equal 3, new.pos
57
77
 
58
78
  new4 = ListMixin.create("parent_id" => 20)
@@ -0,0 +1,87 @@
1
+ module Shared
2
+ module TopAddition
3
+ def setup
4
+ (1..4).each { |counter| TopAdditionMixin.create! :pos => counter, :parent_id => 5 }
5
+ end
6
+
7
+ def test_reordering
8
+ assert_equal [4, 3, 2, 1], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
9
+
10
+ TopAdditionMixin.find(2).move_lower
11
+ assert_equal [4, 3, 1, 2], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
12
+
13
+ TopAdditionMixin.find(2).move_higher
14
+ assert_equal [4, 3, 2, 1], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
15
+
16
+ TopAdditionMixin.find(1).move_to_bottom
17
+ assert_equal [4, 3, 2, 1], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
18
+
19
+ TopAdditionMixin.find(1).move_to_top
20
+ assert_equal [1, 4, 3, 2], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
21
+
22
+ TopAdditionMixin.find(2).move_to_bottom
23
+ assert_equal [1, 4, 3, 2], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
24
+
25
+ TopAdditionMixin.find(4).move_to_top
26
+ assert_equal [4, 1, 3, 2], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
27
+ end
28
+
29
+ def test_injection
30
+ item = TopAdditionMixin.new(:parent_id => 1)
31
+ assert_equal '"mixins"."parent_id" = 1', item.scope_condition
32
+ assert_equal "pos", item.position_column
33
+ end
34
+
35
+ def test_insert
36
+ new = TopAdditionMixin.create(:parent_id => 20)
37
+ assert_equal 1, new.pos
38
+ assert new.first?
39
+ assert new.last?
40
+
41
+ new = TopAdditionMixin.create(:parent_id => 20)
42
+ assert_equal 1, new.pos
43
+ assert new.first?
44
+ assert !new.last?
45
+
46
+ new = TopAdditionMixin.create(:parent_id => 20)
47
+ assert_equal 1, new.pos
48
+ assert new.first?
49
+ assert !new.last?
50
+
51
+ new = TopAdditionMixin.create(:parent_id => 0)
52
+ assert_equal 1, new.pos
53
+ assert new.first?
54
+ assert new.last?
55
+ end
56
+
57
+ def test_insert_at
58
+ new = TopAdditionMixin.create(:parent_id => 20)
59
+ assert_equal 1, new.pos
60
+
61
+ new = TopAdditionMixin.create(:parent_id => 20)
62
+ assert_equal 1, new.pos
63
+
64
+ new = TopAdditionMixin.create(:parent_id => 20)
65
+ assert_equal 1, new.pos
66
+
67
+ new4 = TopAdditionMixin.create(:parent_id => 20)
68
+ assert_equal 1, new4.pos
69
+
70
+ new4.insert_at(3)
71
+ assert_equal 3, new4.pos
72
+ end
73
+
74
+ def test_delete_middle
75
+ assert_equal [4, 3, 2, 1], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
76
+
77
+ TopAdditionMixin.find(2).destroy
78
+
79
+ assert_equal [4, 3, 1], TopAdditionMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
80
+
81
+ assert_equal 3, TopAdditionMixin.find(1).pos
82
+ assert_equal 2, TopAdditionMixin.find(3).pos
83
+ assert_equal 1, TopAdditionMixin.find(4).pos
84
+ end
85
+
86
+ end
87
+ end
data/test/test_list.rb CHANGED
@@ -10,6 +10,7 @@ def setup_db(position_options = {})
10
10
  ActiveRecord::Schema.define(:version => 1) do
11
11
  create_table :mixins do |t|
12
12
  t.column :pos, :integer, position_options
13
+ t.column :active, :boolean, :default => true
13
14
  t.column :parent_id, :integer
14
15
  t.column :parent_type, :string
15
16
  t.column :created_at, :datetime
@@ -35,8 +36,27 @@ end
35
36
 
36
37
  class Mixin < ActiveRecord::Base
37
38
  self.table_name = 'mixins'
39
+ attr_accessible :active, :parent_id, :parent_type
38
40
  end
39
41
 
42
+ class ProtectedMixin < ActiveRecord::Base
43
+ self.table_name = 'mixins'
44
+ attr_protected :active
45
+ end
46
+
47
+ class ProtectedListMixin < ProtectedMixin
48
+ acts_as_list :column => "pos"
49
+ end
50
+
51
+ class UnProtectedMixin < ActiveRecord::Base
52
+ self.table_name = 'mixins'
53
+ end
54
+
55
+ class UnProtectedListMixin < UnProtectedMixin
56
+ acts_as_list :column => "pos"
57
+ end
58
+
59
+
40
60
  class ListMixin < Mixin
41
61
  acts_as_list :column => "pos", :scope => :parent
42
62
  end
@@ -69,6 +89,15 @@ class DefaultScopedMixin < Mixin
69
89
  default_scope { order('pos ASC') }
70
90
  end
71
91
 
92
+ class DefaultScopedWhereMixin < Mixin
93
+ acts_as_list :column => "pos"
94
+ default_scope { order('pos ASC').where(:active => true) }
95
+ end
96
+
97
+ class TopAdditionMixin < Mixin
98
+ acts_as_list :column => "pos", :add_new_at => :top, :scope => :parent_id
99
+ end
100
+
72
101
  class ActsAsListTestCase < Test::Unit::TestCase
73
102
  # No default test required a this class is abstract.
74
103
  # Need for test/unit.
@@ -154,7 +183,7 @@ end
154
183
  class DefaultScopedTest < ActsAsListTestCase
155
184
  def setup
156
185
  setup_db
157
- (1..4).each { |counter| DefaultScopedMixin.create! :pos => counter }
186
+ (1..4).each { |counter| DefaultScopedMixin.create!({:pos => counter}) }
158
187
  end
159
188
 
160
189
  def test_insert
@@ -231,4 +260,217 @@ class DefaultScopedTest < ActsAsListTestCase
231
260
  assert_equal 4, new4.pos
232
261
  end
233
262
 
263
+ def test_update_position
264
+ assert_equal [1, 2, 3, 4], DefaultScopedMixin.find(:all).map(&:id)
265
+ DefaultScopedMixin.find(2).set_list_position(4)
266
+ assert_equal [1, 3, 4, 2], DefaultScopedMixin.find(:all).map(&:id)
267
+ DefaultScopedMixin.find(2).set_list_position(2)
268
+ assert_equal [1, 2, 3, 4], DefaultScopedMixin.find(:all).map(&:id)
269
+ DefaultScopedMixin.find(1).set_list_position(4)
270
+ assert_equal [2, 3, 4, 1], DefaultScopedMixin.find(:all).map(&:id)
271
+ DefaultScopedMixin.find(1).set_list_position(1)
272
+ assert_equal [1, 2, 3, 4], DefaultScopedMixin.find(:all).map(&:id)
273
+ end
274
+
275
+ end
276
+
277
+ class DefaultScopedWhereTest < ActsAsListTestCase
278
+ def setup
279
+ setup_db
280
+ (1..4).each { |counter| DefaultScopedWhereMixin.create! :pos => counter, :active => false }
281
+ end
282
+
283
+ def test_insert
284
+ new = DefaultScopedWhereMixin.create
285
+ assert_equal 5, new.pos
286
+ assert !new.first?
287
+ assert new.last?
288
+
289
+ new = DefaultScopedWhereMixin.create
290
+ assert_equal 6, new.pos
291
+ assert !new.first?
292
+ assert new.last?
293
+
294
+ new = DefaultScopedWhereMixin.create
295
+ assert_equal 7, new.pos
296
+ assert !new.first?
297
+ assert new.last?
298
+ end
299
+
300
+ def test_reordering
301
+ assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).map(&:id)
302
+
303
+ DefaultScopedWhereMixin.where(:active => false).find(2).move_lower
304
+ assert_equal [1, 3, 2, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
305
+
306
+ DefaultScopedWhereMixin.where(:active => false).find(2).move_higher
307
+ assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
308
+
309
+ DefaultScopedWhereMixin.where(:active => false).find(1).move_to_bottom
310
+ assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
311
+
312
+ DefaultScopedWhereMixin.where(:active => false).find(1).move_to_top
313
+ assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
314
+
315
+ DefaultScopedWhereMixin.where(:active => false).find(2).move_to_bottom
316
+ assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
317
+
318
+ DefaultScopedWhereMixin.where(:active => false).find(4).move_to_top
319
+ assert_equal [4, 1, 3, 2], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
320
+ end
321
+
322
+ def test_insert_at
323
+ new = DefaultScopedWhereMixin.create
324
+ assert_equal 5, new.pos
325
+
326
+ new = DefaultScopedWhereMixin.create
327
+ assert_equal 6, new.pos
328
+
329
+ new = DefaultScopedWhereMixin.create
330
+ assert_equal 7, new.pos
331
+
332
+ new4 = DefaultScopedWhereMixin.create
333
+ assert_equal 8, new4.pos
334
+
335
+ new4.insert_at(2)
336
+ assert_equal 2, new4.pos
337
+
338
+ new.reload
339
+ assert_equal 8, new.pos
340
+
341
+ new.insert_at(2)
342
+ assert_equal 2, new.pos
343
+
344
+ new4.reload
345
+ assert_equal 3, new4.pos
346
+
347
+ new5 = DefaultScopedWhereMixin.create
348
+ assert_equal 9, new5.pos
349
+
350
+ new5.insert_at(1)
351
+ assert_equal 1, new5.pos
352
+
353
+ new4.reload
354
+ assert_equal 4, new4.pos
355
+ end
356
+
357
+ def test_update_position
358
+ assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
359
+ DefaultScopedWhereMixin.where(:active => false).find(2).set_list_position(4)
360
+ assert_equal [1, 3, 4, 2], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
361
+ DefaultScopedWhereMixin.where(:active => false).find(2).set_list_position(2)
362
+ assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
363
+ DefaultScopedWhereMixin.where(:active => false).find(1).set_list_position(4)
364
+ assert_equal [2, 3, 4, 1], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
365
+ DefaultScopedWhereMixin.where(:active => false).find(1).set_list_position(1)
366
+ assert_equal [1, 2, 3, 4], DefaultScopedWhereMixin.where(:active => false).find(:all).map(&:id)
367
+ end
368
+
369
+ end
370
+
371
+ class MultiDestroyTest < ActsAsListTestCase
372
+
373
+ def setup
374
+ setup_db
375
+ end
376
+
377
+ # example:
378
+ #
379
+ # class TodoList < ActiveRecord::Base
380
+ # has_many :todo_items, :order => "position"
381
+ # accepts_nested_attributes_for :todo_items, :allow_destroy => true
382
+ # end
383
+ #
384
+ # class TodoItem < ActiveRecord::Base
385
+ # belongs_to :todo_list
386
+ # acts_as_list :scope => :todo_list
387
+ # end
388
+ #
389
+ # Assume that there are three items.
390
+ # The user mark two items as deleted, click save button, form will be post:
391
+ #
392
+ # todo_list.todo_items_attributes = [
393
+ # {id: 1, _destroy: true},
394
+ # {id: 2, _destroy: true}
395
+ # ]
396
+ #
397
+ # Save toto_list, the position of item #3 should eql 1.
398
+ #
399
+ def test_destroy
400
+ new1 = DefaultScopedMixin.create
401
+ assert_equal 1, new1.pos
402
+
403
+ new2 = DefaultScopedMixin.create
404
+ assert_equal 2, new2.pos
405
+
406
+ new3 = DefaultScopedMixin.create
407
+ assert_equal 3, new3.pos
408
+
409
+ new1.destroy
410
+ new2.destroy
411
+ new3.reload
412
+ assert_equal 1, new3.pos
413
+ end
414
+ end
415
+
416
+ #class TopAdditionMixin < Mixin
417
+
418
+ class TopAdditionTest < ActsAsListTestCase
419
+ include Shared::TopAddition
420
+
421
+ def setup
422
+ setup_db
423
+ super
424
+ end
425
+ end
426
+
427
+
428
+ class TopAdditionTestWithDefault < ActsAsListTestCase
429
+ include Shared::TopAddition
430
+
431
+ def setup
432
+ setup_db_with_default
433
+ super
434
+ end
435
+ end
436
+
437
+ class RespectMixinProtection < ActsAsListTestCase
438
+ def setup
439
+ setup_db_with_default
440
+ super
441
+ end
442
+
443
+ # if an attribute is set attr_protected
444
+ # it should be unchanged by update_attributes
445
+ def test_unmodified_protection
446
+ a = ProtectedMixin.new
447
+ a.update_attributes({:active => false})
448
+ assert_equal true, a.active
449
+ end
450
+
451
+ # even after the acts_as_list mixin is joined
452
+ # that protection should continue to exist
453
+ def test_still_protected
454
+ b = ProtectedListMixin.new
455
+ b.update_attributes({:active => false})
456
+ assert_equal true, b.active
457
+ end
458
+
459
+ # similarly, if a class lacks mass_assignment protection
460
+ # it should be able to be changed
461
+ def test_unprotected
462
+ a = UnProtectedMixin.new
463
+ a.update_attributes({:active => false})
464
+ assert_equal false, a.active
465
+ end
466
+
467
+ # and it should continue to be mutable by mass_assignment
468
+ # even after the acts_as_list plugin has been joined
469
+ def test_still_unprotected_mixin
470
+ b = UnProtectedListMixin.new
471
+ b.assign_attributes({:active => false})
472
+ # p UnProtectedListMixin.accessible_attributes.length
473
+ assert_equal false, b.active
474
+ end
475
+
234
476
  end
metadata CHANGED
@@ -1,76 +1,98 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: acts_as_list
3
- version: !ruby/object:Gem::Version
4
- version: 0.1.5
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - David Heinemeier Hansson
9
14
  - Swanand Pagnis
10
15
  - Quinn Chaffee
11
16
  autorequire:
12
17
  bindir: bin
13
18
  cert_chain: []
14
- date: 2012-02-24 00:00:00.000000000 Z
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: bundler
18
- requirement: &2152300140 !ruby/object:Gem::Requirement
19
+
20
+ date: 2013-02-28 00:00:00 Z
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: activerecord
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
19
26
  none: false
20
- requirements:
21
- - - ~>
22
- - !ruby/object:Gem::Version
23
- version: 1.0.0
24
- type: :development
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 7
31
+ segments:
32
+ - 3
33
+ - 0
34
+ version: "3.0"
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: bundler
25
39
  prerelease: false
26
- version_requirements: *2152300140
27
- - !ruby/object:Gem::Dependency
28
- name: activerecord
29
- requirement: &2152299540 !ruby/object:Gem::Requirement
40
+ requirement: &id002 !ruby/object:Gem::Requirement
30
41
  none: false
31
- requirements:
32
- - - ! '>='
33
- - !ruby/object:Gem::Version
34
- version: 1.15.4.7794
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 23
46
+ segments:
47
+ - 1
48
+ - 0
49
+ - 0
50
+ version: 1.0.0
35
51
  type: :development
36
- prerelease: false
37
- version_requirements: *2152299540
38
- - !ruby/object:Gem::Dependency
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
39
54
  name: rdoc
40
- requirement: &2152299080 !ruby/object:Gem::Requirement
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
41
57
  none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
46
65
  type: :development
47
- prerelease: false
48
- version_requirements: *2152299080
49
- - !ruby/object:Gem::Dependency
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
50
68
  name: sqlite3
51
- requirement: &2152298480 !ruby/object:Gem::Requirement
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
52
71
  none: false
53
- requirements:
54
- - - ! '>='
55
- - !ruby/object:Gem::Version
56
- version: '0'
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
57
79
  type: :development
58
- prerelease: false
59
- version_requirements: *2152298480
60
- description: This "acts_as" extension provides the capabilities for sorting and reordering
61
- a number of objects in a list. The class that has this specified needs to have a
62
- "position" column defined as an integer on the mapped database table.
63
- email:
80
+ version_requirements: *id004
81
+ description: This "acts_as" extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a "position" column defined as an integer on the mapped database table.
82
+ email:
64
83
  - swanand.pagnis@gmail.com
65
84
  executables: []
85
+
66
86
  extensions: []
87
+
67
88
  extra_rdoc_files: []
68
- files:
89
+
90
+ files:
69
91
  - .gemtest
70
92
  - .gitignore
93
+ - .travis.yml
71
94
  - Gemfile
72
95
  - README.md
73
- - README.rdoc
74
96
  - Rakefile
75
97
  - acts_as_list.gemspec
76
98
  - init.rb
@@ -82,37 +104,48 @@ files:
82
104
  - test/shared_array_scope_list.rb
83
105
  - test/shared_list.rb
84
106
  - test/shared_list_sub.rb
107
+ - test/shared_top_addition.rb
85
108
  - test/shared_zero_based.rb
86
109
  - test/test_list.rb
87
110
  homepage: http://github.com/swanandp/acts_as_list
88
111
  licenses: []
112
+
89
113
  post_install_message:
90
114
  rdoc_options: []
91
- require_paths:
115
+
116
+ require_paths:
92
117
  - lib
93
- required_ruby_version: !ruby/object:Gem::Requirement
118
+ required_ruby_version: !ruby/object:Gem::Requirement
94
119
  none: false
95
- requirements:
96
- - - ! '>='
97
- - !ruby/object:Gem::Version
98
- version: '0'
99
- required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ hash: 3
124
+ segments:
125
+ - 0
126
+ version: "0"
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
128
  none: false
101
- requirements:
102
- - - ! '>='
103
- - !ruby/object:Gem::Version
104
- version: '0'
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ hash: 3
133
+ segments:
134
+ - 0
135
+ version: "0"
105
136
  requirements: []
137
+
106
138
  rubyforge_project: acts_as_list
107
139
  rubygems_version: 1.8.15
108
140
  signing_key:
109
141
  specification_version: 3
110
142
  summary: A gem allowing a active_record model to act_as_list.
111
- test_files:
143
+ test_files:
112
144
  - test/helper.rb
113
145
  - test/shared.rb
114
146
  - test/shared_array_scope_list.rb
115
147
  - test/shared_list.rb
116
148
  - test/shared_list_sub.rb
149
+ - test/shared_top_addition.rb
117
150
  - test/shared_zero_based.rb
118
151
  - test/test_list.rb
data/README.rdoc DELETED
@@ -1,35 +0,0 @@
1
- = ActsAsList
2
-
3
- == Description
4
-
5
- This acts_as extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a +position+ column defined as an integer on the mapped database table.
6
-
7
-
8
- == Example
9
-
10
- class TodoList < ActiveRecord::Base
11
- has_many :todo_items, :order => "position"
12
- end
13
-
14
- class TodoItem < ActiveRecord::Base
15
- belongs_to :todo_list
16
- acts_as_list :scope => :todo_list
17
- end
18
-
19
- todo_list.first.move_to_bottom
20
- todo_list.last.move_higher
21
-
22
- == Contributing to acts_as_list
23
-
24
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
25
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
26
- * Fork the project
27
- * Start a feature/bugfix branch
28
- * Commit and push until you are happy with your contribution
29
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
30
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
31
-
32
- == Copyright
33
-
34
- Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
35
-