acts_as_list-rails3 0.0.1

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in acts_as_list-rails3.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,23 @@
1
+ ActsAsList
2
+ ==========
3
+
4
+ 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
+
6
+
7
+ Example
8
+ =======
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
+
23
+ Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
@@ -0,0 +1,45 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ #require 'rake'
5
+ #require 'rake/testtask'
6
+
7
+ # Run the test with 'rake' or 'rake test'
8
+ #desc 'Default: run acts_as_list unit tests.'
9
+ #task :default => :test
10
+
11
+ #desc 'Test the acts_as_list plugin.'
12
+ #Rake::TestTask.new(:test) do |t|
13
+ # t.libs << 'lib' << 'test'
14
+ # t.pattern = 'test/**/test_*.rb'
15
+ # t.verbose = true
16
+ #end
17
+
18
+
19
+ # Run jewler task to generate the gemspec file
20
+ #require 'jeweler'
21
+ #Jeweler::Tasks.new do |gem|
22
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
23
+ # gem.name = "acts_as_list-rails3"
24
+ # gem.homepage = "http://github.com/chaffeqa/acts_as_list"
25
+ # gem.license = "MIT"
26
+ # gem.summary = %Q{A gem allowing a active_record model to act_as_list.}
27
+ # gem.description = %Q{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.}
28
+ # gem.email = "chaffeqa@gmail.com"
29
+ # gem.authors = ["avid Heinemeier Hansson","Swanand Pagnis","Quinn@Kubuntu-Desktop"]
30
+ # dependencies defined in Gemfile
31
+ #end
32
+ #Jeweler::RubygemsDotOrgTasks.new
33
+
34
+
35
+ # Run the rdoc task to generate rdocs for this gem
36
+ #require 'rdoc/task'
37
+ #RDoc::Task.new do |rdoc|
38
+ # version = File.exist?('VERSION') ? File.read('VERSION') : ""
39
+
40
+ # rdoc.rdoc_dir = 'rdoc'
41
+ # rdoc.title = "acts_as_list-rails3 #{version}"
42
+ # rdoc.rdoc_files.include('README*')
43
+ # rdoc.rdoc_files.include('lib/**/*.rb')
44
+ #end
45
+
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "acts_as_list/version"
4
+
5
+ Gem::Specification.new do |s|
6
+
7
+ # Description Meta...
8
+ s.name = "acts_as_list-rails3"
9
+ s.version = ActiveRecord::Acts::List::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["David Heinemeier Hansson","Swanand Pagnis","Quinn Chaffee"]
12
+ s.email = ["chaffeqa@gmail.com"]
13
+ s.homepage = "http://github.com/chaffeqa/acts_as_list"
14
+ s.summary = %q{A gem allowing a active_record model to act_as_list.}
15
+ s.description = %q{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.}
16
+ s.rubyforge_project = "acts_as_list-rails3"
17
+
18
+
19
+ # Load Paths...
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+
25
+
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"])
29
+ s.add_development_dependency("rdoc")
30
+ s.add_development_dependency("sqlite3-ruby")
31
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ $:.unshift "#{File.dirname(__FILE__)}/lib"
2
+ require 'acts_as_list'
@@ -0,0 +1,2 @@
1
+ require 'acts_as_list/active_record/acts/list'
2
+ ActiveRecord::Base.class_eval { include ActiveRecord::Acts::List }
@@ -0,0 +1,271 @@
1
+ module ActiveRecord
2
+ module Acts #:nodoc:
3
+ module List #:nodoc:
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ # This +acts_as+ extension provides the capabilities for sorting and reordering a number of objects in a list.
9
+ # The class that has this specified needs to have a +position+ column defined as an integer on
10
+ # the mapped database table.
11
+ #
12
+ # Todo list example:
13
+ #
14
+ # class TodoList < ActiveRecord::Base
15
+ # has_many :todo_items, :order => "position"
16
+ # end
17
+ #
18
+ # class TodoItem < ActiveRecord::Base
19
+ # belongs_to :todo_list
20
+ # acts_as_list :scope => :todo_list
21
+ # end
22
+ #
23
+ # todo_list.first.move_to_bottom
24
+ # todo_list.last.move_higher
25
+ module ClassMethods
26
+ # Configuration options are:
27
+ #
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
31
+ # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
32
+ # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
33
+ # * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection
34
+ # act more line an array in it's indexing.
35
+ def acts_as_list(options = {})
36
+ configuration = { :column => "position", :scope => "1 = 1", :top_of_list => 1}
37
+ configuration.update(options) if options.is_a?(Hash)
38
+
39
+ configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
40
+
41
+ if configuration[:scope].is_a?(Symbol)
42
+ scope_condition_method = %(
43
+ def scope_condition
44
+ self.class.send(:sanitize_sql_hash_for_conditions, { :#{configuration[:scope].to_s} => send(:#{configuration[:scope].to_s}) })
45
+ end
46
+ )
47
+ elsif configuration[:scope].is_a?(Array)
48
+ scope_condition_method = %(
49
+ def scope_condition
50
+ attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
51
+ memo[column.intern] = send(column.intern); memo
52
+ end
53
+ self.class.send(:sanitize_sql_hash_for_conditions, attrs)
54
+ end
55
+ )
56
+ else
57
+ scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
58
+ end
59
+
60
+ class_eval <<-EOV
61
+ include ActiveRecord::Acts::List::InstanceMethods
62
+
63
+ def acts_as_list_top
64
+ #{configuration[:top_of_list]}.to_i
65
+ end
66
+
67
+ def acts_as_list_class
68
+ ::#{self.name}
69
+ end
70
+
71
+ def position_column
72
+ '#{configuration[:column]}'
73
+ end
74
+
75
+ #{scope_condition_method}
76
+
77
+ before_destroy :decrement_positions_on_lower_items
78
+ before_create :add_to_list_bottom
79
+ EOV
80
+ end
81
+ end
82
+
83
+ # All the methods available to a record that has had <tt>acts_as_list</tt> specified. Each method works
84
+ # by assuming the object to be the item in the list, so <tt>chapter.move_lower</tt> would move that chapter
85
+ # lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return +true+ if that chapter is
86
+ # the first in the list of all chapters.
87
+ module InstanceMethods
88
+ # Insert the item at the given position (defaults to the top position of 1).
89
+ def insert_at(position = acts_as_list_top)
90
+ insert_at_position(position)
91
+ end
92
+
93
+ # Swap positions with the next lower item, if one exists.
94
+ def move_lower
95
+ return unless lower_item
96
+
97
+ acts_as_list_class.transaction do
98
+ lower_item.decrement_position
99
+ increment_position
100
+ end
101
+ end
102
+
103
+ # Swap positions with the next higher item, if one exists.
104
+ def move_higher
105
+ return unless higher_item
106
+
107
+ acts_as_list_class.transaction do
108
+ higher_item.increment_position
109
+ decrement_position
110
+ end
111
+ end
112
+
113
+ # Move to the bottom of the list. If the item is already in the list, the items below it have their
114
+ # position adjusted accordingly.
115
+ def move_to_bottom
116
+ return unless in_list?
117
+ acts_as_list_class.transaction do
118
+ decrement_positions_on_lower_items
119
+ assume_bottom_position
120
+ end
121
+ end
122
+
123
+ # Move to the top of the list. If the item is already in the list, the items above it have their
124
+ # position adjusted accordingly.
125
+ def move_to_top
126
+ return unless in_list?
127
+ acts_as_list_class.transaction do
128
+ increment_positions_on_higher_items
129
+ assume_top_position
130
+ end
131
+ end
132
+
133
+ # Removes the item from the list.
134
+ def remove_from_list
135
+ if in_list?
136
+ decrement_positions_on_lower_items
137
+ update_attribute position_column, nil
138
+ end
139
+ end
140
+
141
+ # Increase the position of this item without adjusting the rest of the list.
142
+ def increment_position
143
+ return unless in_list?
144
+ update_attribute position_column, self.send(position_column).to_i + 1
145
+ end
146
+
147
+ # Decrease the position of this item without adjusting the rest of the list.
148
+ def decrement_position
149
+ return unless in_list?
150
+ update_attribute position_column, self.send(position_column).to_i - 1
151
+ end
152
+
153
+ # Return +true+ if this object is the first in the list.
154
+ def first?
155
+ return false unless in_list?
156
+ self.send(position_column) == acts_as_list_top
157
+ end
158
+
159
+ # Return +true+ if this object is the last in the list.
160
+ def last?
161
+ return false unless in_list?
162
+ self.send(position_column) == bottom_position_in_list
163
+ end
164
+
165
+ # Return the next higher item in the list.
166
+ def higher_item
167
+ 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}"
170
+ )
171
+ end
172
+
173
+ # Return the next lower item in the list.
174
+ def lower_item
175
+ 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}"
178
+ )
179
+ end
180
+
181
+ # Test if this record is in a list
182
+ def in_list?
183
+ !send(position_column).nil?
184
+ end
185
+
186
+ private
187
+ def add_to_list_top
188
+ increment_positions_on_all_items
189
+ end
190
+
191
+ def add_to_list_bottom
192
+ if self[position_column].nil?
193
+ self[position_column] = bottom_position_in_list.to_i + 1
194
+ else
195
+ increment_positions_on_lower_items(self[position_column])
196
+ end
197
+ end
198
+
199
+ # Overwrite this method to define the scope of the list changes
200
+ def scope_condition() "1" end
201
+
202
+ # Returns the bottom position number in the list.
203
+ # bottom_position_in_list # => 2
204
+ def bottom_position_in_list(except = nil)
205
+ item = bottom_item(except)
206
+ item ? item.send(position_column) : acts_as_list_top - 1
207
+ end
208
+
209
+ # Returns the bottom item
210
+ def bottom_item(except = nil)
211
+ conditions = scope_condition
212
+ conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
213
+ acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
214
+ end
215
+
216
+ # Forces item to assume the bottom position in the list.
217
+ def assume_bottom_position
218
+ update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
219
+ end
220
+
221
+ # Forces item to assume the top position in the list.
222
+ def assume_top_position
223
+ update_attribute(position_column, acts_as_list_top)
224
+ end
225
+
226
+ # This has the effect of moving all the higher items up one.
227
+ def decrement_positions_on_higher_items(position)
228
+ acts_as_list_class.update_all(
229
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
230
+ )
231
+ end
232
+
233
+ # This has the effect of moving all the lower items up one.
234
+ def decrement_positions_on_lower_items
235
+ return unless in_list?
236
+ acts_as_list_class.update_all(
237
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
238
+ )
239
+ end
240
+
241
+ # This has the effect of moving all the higher items down one.
242
+ def increment_positions_on_higher_items
243
+ return unless in_list?
244
+ acts_as_list_class.update_all(
245
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
246
+ )
247
+ end
248
+
249
+ # This has the effect of moving all the lower items down one.
250
+ def increment_positions_on_lower_items(position)
251
+ acts_as_list_class.update_all(
252
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
253
+ )
254
+ end
255
+
256
+ # Increments position (<tt>position_column</tt>) of all items in the list.
257
+ def increment_positions_on_all_items
258
+ acts_as_list_class.update_all(
259
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
260
+ )
261
+ end
262
+
263
+ def insert_at_position(position)
264
+ remove_from_list
265
+ increment_positions_on_lower_items(position)
266
+ self.update_attribute(position_column, position)
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveRecord
2
+ module Acts
3
+ module List
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ #require 'rubygems'
2
+ #require 'bundler'
3
+ #begin
4
+ # Bundler.setup(:default, :development)
5
+ #rescue Bundler::BundlerError => e
6
+ # $stderr.puts e.message
7
+ # $stderr.puts "Run `bundle install` to install missing gems"
8
+ # exit e.status_code
9
+ #end
10
+ require 'test/unit'
11
+ require 'active_record'
12
+ require "#{File.dirname(__FILE__)}/../init"
13
+
14
+
15
+
@@ -0,0 +1,659 @@
1
+ # NOTE: following now done in helper.rb (better Readability)
2
+ #require 'test/unit'
3
+ #require 'rubygems'
4
+ #gem 'activerecord', '>= 1.15.4.7794'
5
+ #require 'active_record'
6
+ #require "#{File.dirname(__FILE__)}/../init"
7
+ require 'helper.rb'
8
+
9
+
10
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
11
+
12
+ def setup_db
13
+ ActiveRecord::Schema.define(:version => 1) do
14
+ create_table :mixins do |t|
15
+ t.column :pos, :integer
16
+ t.column :parent_id, :integer
17
+ t.column :parent_type, :string
18
+ t.column :created_at, :datetime
19
+ t.column :updated_at, :datetime
20
+ end
21
+ end
22
+ end
23
+
24
+ # Returns true if ActiveRecord is rails3 version
25
+ def rails_3
26
+ defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 3
27
+ end
28
+
29
+ def teardown_db
30
+ ActiveRecord::Base.connection.tables.each do |table|
31
+ ActiveRecord::Base.connection.drop_table(table)
32
+ end
33
+ end
34
+
35
+ class Mixin < ActiveRecord::Base
36
+ end
37
+
38
+ class ListMixin < Mixin
39
+ acts_as_list :column => "pos", :scope => :parent
40
+
41
+ def self.table_name() "mixins" end
42
+ end
43
+
44
+ class ListMixinSub1 < ListMixin
45
+ end
46
+
47
+ class ListMixinSub2 < ListMixin
48
+ end
49
+
50
+ class ListWithStringScopeMixin < ActiveRecord::Base
51
+ acts_as_list :column => "pos", :scope => 'parent_id = #{parent_id}'
52
+
53
+ def self.table_name() "mixins" end
54
+ end
55
+
56
+ class ArrayScopeListMixin < Mixin
57
+ acts_as_list :column => "pos", :scope => [:parent_id, :parent_type]
58
+
59
+ def self.table_name() "mixins" end
60
+ end
61
+
62
+ class ZeroBasedMixin < ActiveRecord::Base
63
+ acts_as_list :column => "pos", :top_of_list => 0, :scope => [:parent_id]
64
+
65
+ def self.table_name() "mixins" end
66
+ end
67
+
68
+
69
+ class ZeroBasedTest < Test::Unit::TestCase
70
+ def setup
71
+ setup_db
72
+ (1..4).each { |counter| ZeroBasedMixin.create! :pos => counter, :parent_id => 5 }
73
+ end
74
+
75
+ def teardown
76
+ teardown_db
77
+ end
78
+
79
+ def test_insert
80
+ new = ZeroBasedMixin.create(:parent_id => 20)
81
+ assert_equal 0, new.pos
82
+ assert new.first?
83
+ assert new.last?
84
+
85
+ new = ZeroBasedMixin.create(:parent_id => 20)
86
+ assert_equal 1, new.pos
87
+ assert !new.first?
88
+ assert new.last?
89
+
90
+ new = ZeroBasedMixin.create(:parent_id => 20)
91
+ assert_equal 2, new.pos
92
+ assert !new.first?
93
+ assert new.last?
94
+
95
+ new = ZeroBasedMixin.create(:parent_id => 0)
96
+ assert_equal 0, new.pos
97
+ assert new.first?
98
+ assert new.last?
99
+ end
100
+
101
+
102
+ def test_reordering
103
+ assert_equal [1, 2, 3, 4], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
104
+
105
+ ListMixin.find(2).move_lower
106
+ assert_equal [1, 3, 2, 4], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
107
+
108
+ ListMixin.find(2).move_higher
109
+ assert_equal [1, 2, 3, 4], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
110
+
111
+ ListMixin.find(1).move_to_bottom
112
+ assert_equal [2, 3, 4, 1], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
113
+
114
+ ListMixin.find(1).move_to_top
115
+ assert_equal [1, 2, 3, 4], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
116
+
117
+ ListMixin.find(2).move_to_bottom
118
+ assert_equal [1, 3, 4, 2], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
119
+
120
+ ListMixin.find(4).move_to_top
121
+ assert_equal [4, 1, 3, 2], ZeroBasedMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
122
+ end
123
+
124
+ def test_insert_at
125
+ new = ZeroBasedMixin.create(:parent_id => 20)
126
+ assert_equal 0, new.pos
127
+
128
+ new = ZeroBasedMixin.create(:parent_id => 20)
129
+ assert_equal 1, new.pos
130
+
131
+ new = ZeroBasedMixin.create(:parent_id => 20)
132
+ assert_equal 2, new.pos
133
+
134
+ new4 = ZeroBasedMixin.create(:parent_id => 20)
135
+ assert_equal 3, new4.pos
136
+
137
+ new4.insert_at(2)
138
+ assert_equal 2, new4.pos
139
+
140
+ new.reload
141
+ assert_equal 3, new.pos
142
+
143
+ new.insert_at(2)
144
+ assert_equal 2, new.pos
145
+
146
+ new4.reload
147
+ assert_equal 3, new4.pos
148
+
149
+ new5 = ListMixin.create(:parent_id => 20)
150
+ assert_equal 4, new5.pos
151
+
152
+ new5.insert_at(1)
153
+ assert_equal 1, new5.pos
154
+
155
+ new4.reload
156
+ assert_equal 4, new4.pos
157
+ end
158
+
159
+ end
160
+
161
+
162
+ class ListTest < Test::Unit::TestCase
163
+
164
+ def setup
165
+ setup_db
166
+ (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 5 }
167
+ end
168
+
169
+ def teardown
170
+ teardown_db
171
+ end
172
+
173
+ def test_reordering
174
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
175
+
176
+ ListMixin.find(2).move_lower
177
+ assert_equal [1, 3, 2, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
178
+
179
+ ListMixin.find(2).move_higher
180
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
181
+
182
+ ListMixin.find(1).move_to_bottom
183
+ assert_equal [2, 3, 4, 1], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
184
+
185
+ ListMixin.find(1).move_to_top
186
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
187
+
188
+ ListMixin.find(2).move_to_bottom
189
+ assert_equal [1, 3, 4, 2], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
190
+
191
+ ListMixin.find(4).move_to_top
192
+ assert_equal [4, 1, 3, 2], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
193
+ end
194
+
195
+ def test_move_to_bottom_with_next_to_last_item
196
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
197
+ ListMixin.find(3).move_to_bottom
198
+ assert_equal [1, 2, 4, 3], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
199
+ end
200
+
201
+ def test_next_prev
202
+ assert_equal ListMixin.find(2), ListMixin.find(1).lower_item
203
+ assert_nil ListMixin.find(1).higher_item
204
+ assert_equal ListMixin.find(3), ListMixin.find(4).higher_item
205
+ assert_nil ListMixin.find(4).lower_item
206
+ end
207
+
208
+ def test_injection
209
+ item = ListMixin.new(:parent_id => 1)
210
+ assert_equal '"mixins"."parent_id" = 1', item.scope_condition
211
+ assert_equal "pos", item.position_column
212
+ end
213
+
214
+ def test_insert
215
+ new = ListMixin.create(:parent_id => 20)
216
+ assert_equal 1, new.pos
217
+ assert new.first?
218
+ assert new.last?
219
+
220
+ new = ListMixin.create(:parent_id => 20)
221
+ assert_equal 2, new.pos
222
+ assert !new.first?
223
+ assert new.last?
224
+
225
+ new = ListMixin.create(:parent_id => 20)
226
+ assert_equal 3, new.pos
227
+ assert !new.first?
228
+ assert new.last?
229
+
230
+ new = ListMixin.create(:parent_id => 0)
231
+ assert_equal 1, new.pos
232
+ assert new.first?
233
+ assert new.last?
234
+ end
235
+
236
+ def test_insert_at
237
+ new = ListMixin.create(:parent_id => 20)
238
+ assert_equal 1, new.pos
239
+
240
+ new = ListMixin.create(:parent_id => 20)
241
+ assert_equal 2, new.pos
242
+
243
+ new = ListMixin.create(:parent_id => 20)
244
+ assert_equal 3, new.pos
245
+
246
+ new4 = ListMixin.create(:parent_id => 20)
247
+ assert_equal 4, new4.pos
248
+
249
+ new4.insert_at(3)
250
+ assert_equal 3, new4.pos
251
+
252
+ new.reload
253
+ assert_equal 4, new.pos
254
+
255
+ new.insert_at(2)
256
+ assert_equal 2, new.pos
257
+
258
+ new4.reload
259
+ assert_equal 4, new4.pos
260
+
261
+ new5 = ListMixin.create(:parent_id => 20)
262
+ assert_equal 5, new5.pos
263
+
264
+ new5.insert_at(1)
265
+ assert_equal 1, new5.pos
266
+
267
+ new4.reload
268
+ assert_equal 5, new4.pos
269
+ end
270
+
271
+ def test_delete_middle
272
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
273
+
274
+ ListMixin.find(2).destroy
275
+
276
+ assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
277
+
278
+ assert_equal 1, ListMixin.find(1).pos
279
+ assert_equal 2, ListMixin.find(3).pos
280
+ assert_equal 3, ListMixin.find(4).pos
281
+
282
+ ListMixin.find(1).destroy
283
+
284
+ assert_equal [3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
285
+
286
+ assert_equal 1, ListMixin.find(3).pos
287
+ assert_equal 2, ListMixin.find(4).pos
288
+ end
289
+
290
+ def test_with_string_based_scope
291
+ new = ListWithStringScopeMixin.create(:parent_id => 500)
292
+ assert_equal 1, new.pos
293
+ assert new.first?
294
+ assert new.last?
295
+ end
296
+
297
+ def test_nil_scope
298
+ new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
299
+ new2.move_higher
300
+ assert_equal [new2, new1, new3], ListMixin.find(:all, :conditions => 'parent_id IS NULL', :order => 'pos')
301
+ end
302
+
303
+ def test_remove_from_list_should_then_fail_in_list?
304
+ assert_equal true, ListMixin.find(1).in_list?
305
+ ListMixin.find(1).remove_from_list
306
+ assert_equal false, ListMixin.find(1).in_list?
307
+ end
308
+
309
+ def test_remove_from_list_should_set_position_to_nil
310
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
311
+
312
+ ListMixin.find(2).remove_from_list
313
+
314
+ assert_equal [2, 1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
315
+
316
+ assert_equal 1, ListMixin.find(1).pos
317
+ assert_equal nil, ListMixin.find(2).pos
318
+ assert_equal 2, ListMixin.find(3).pos
319
+ assert_equal 3, ListMixin.find(4).pos
320
+ end
321
+
322
+ def test_remove_before_destroy_does_not_shift_lower_items_twice
323
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
324
+
325
+ ListMixin.find(2).remove_from_list
326
+ ListMixin.find(2).destroy
327
+
328
+ assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
329
+
330
+ assert_equal 1, ListMixin.find(1).pos
331
+ assert_equal 2, ListMixin.find(3).pos
332
+ assert_equal 3, ListMixin.find(4).pos
333
+ end
334
+
335
+ def test_before_destroy_callbacks_do_not_update_position_to_nil_before_deleting_the_record
336
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
337
+
338
+ # We need to trigger all the before_destroy callbacks without actually
339
+ # destroying the record so we can see the affect the callbacks have on
340
+ # the record.
341
+ # NOTE: Hotfix for rails3 ActiveRecord
342
+ list = ListMixin.find(2)
343
+ if list.respond_to?(:run_callbacks)
344
+ # Refactored to work according to Rails3 ActiveRSupport Callbacks <http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html>
345
+ list.run_callbacks :destroy, :before if rails_3
346
+ list.run_callbacks(:before_destroy) if !rails_3
347
+ else
348
+ list.send(:callback, :before_destroy)
349
+ end
350
+
351
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
352
+
353
+ assert_equal 1, ListMixin.find(1).pos
354
+ assert_equal 2, ListMixin.find(2).pos
355
+ assert_equal 2, ListMixin.find(3).pos
356
+ assert_equal 3, ListMixin.find(4).pos
357
+ end
358
+
359
+ def test_before_create_callback_adds_to_bottom
360
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
361
+
362
+ new = ListMixin.create(:parent_id => 5)
363
+ assert_equal 5, new.pos
364
+ assert !new.first?
365
+ assert new.last?
366
+
367
+ assert_equal [1, 2, 3, 4, 5], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
368
+ end
369
+
370
+ def test_before_create_callback_adds_to_given_position
371
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
372
+
373
+ new = ListMixin.create(:pos => 1, :parent_id => 5)
374
+ assert_equal 1, new.pos
375
+ assert new.first?
376
+ assert !new.last?
377
+
378
+ assert_equal [5, 1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
379
+
380
+ new = ListMixin.create(:pos => 3, :parent_id => 5)
381
+ assert_equal 3, new.pos
382
+ assert !new.first?
383
+ assert !new.last?
384
+
385
+ assert_equal [5, 1, 6, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
386
+ end
387
+
388
+ end
389
+
390
+ class ListSubTest < Test::Unit::TestCase
391
+
392
+ def setup
393
+ setup_db
394
+ (1..4).each { |i| ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).create! :pos => i, :parent_id => 5000 }
395
+ end
396
+
397
+ def teardown
398
+ teardown_db
399
+ end
400
+
401
+ def test_reordering
402
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
403
+
404
+ ListMixin.find(2).move_lower
405
+ assert_equal [1, 3, 2, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
406
+
407
+ ListMixin.find(2).move_higher
408
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
409
+
410
+ ListMixin.find(1).move_to_bottom
411
+ assert_equal [2, 3, 4, 1], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
412
+
413
+ ListMixin.find(1).move_to_top
414
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
415
+
416
+ ListMixin.find(2).move_to_bottom
417
+ assert_equal [1, 3, 4, 2], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
418
+
419
+ ListMixin.find(4).move_to_top
420
+ assert_equal [4, 1, 3, 2], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
421
+ end
422
+
423
+ def test_move_to_bottom_with_next_to_last_item
424
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
425
+ ListMixin.find(3).move_to_bottom
426
+ assert_equal [1, 2, 4, 3], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
427
+ end
428
+
429
+ def test_next_prev
430
+ assert_equal ListMixin.find(2), ListMixin.find(1).lower_item
431
+ assert_nil ListMixin.find(1).higher_item
432
+ assert_equal ListMixin.find(3), ListMixin.find(4).higher_item
433
+ assert_nil ListMixin.find(4).lower_item
434
+ end
435
+
436
+ def test_injection
437
+ item = ListMixin.new("parent_id"=>1)
438
+ assert_equal '"mixins"."parent_id" = 1', item.scope_condition
439
+ assert_equal "pos", item.position_column
440
+ end
441
+
442
+ def test_insert_at
443
+ new = ListMixin.create("parent_id" => 20)
444
+ assert_equal 1, new.pos
445
+
446
+ new = ListMixinSub1.create("parent_id" => 20)
447
+ assert_equal 2, new.pos
448
+
449
+ new = ListMixinSub2.create("parent_id" => 20)
450
+ assert_equal 3, new.pos
451
+
452
+ new4 = ListMixin.create("parent_id" => 20)
453
+ assert_equal 4, new4.pos
454
+
455
+ new4.insert_at(3)
456
+ assert_equal 3, new4.pos
457
+
458
+ new.reload
459
+ assert_equal 4, new.pos
460
+
461
+ new.insert_at(2)
462
+ assert_equal 2, new.pos
463
+
464
+ new4.reload
465
+ assert_equal 4, new4.pos
466
+
467
+ new5 = ListMixinSub1.create("parent_id" => 20)
468
+ assert_equal 5, new5.pos
469
+
470
+ new5.insert_at(1)
471
+ assert_equal 1, new5.pos
472
+
473
+ new4.reload
474
+ assert_equal 5, new4.pos
475
+ end
476
+
477
+ def test_delete_middle
478
+ assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
479
+
480
+ ListMixin.find(2).destroy
481
+
482
+ assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
483
+
484
+ assert_equal 1, ListMixin.find(1).pos
485
+ assert_equal 2, ListMixin.find(3).pos
486
+ assert_equal 3, ListMixin.find(4).pos
487
+
488
+ ListMixin.find(1).destroy
489
+
490
+ assert_equal [3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
491
+
492
+ assert_equal 1, ListMixin.find(3).pos
493
+ assert_equal 2, ListMixin.find(4).pos
494
+ end
495
+
496
+ end
497
+
498
+ class ArrayScopeListTest < Test::Unit::TestCase
499
+
500
+ def setup
501
+ setup_db
502
+ (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter, :parent_id => 5, :parent_type => 'ParentClass' }
503
+ end
504
+
505
+ def teardown
506
+ teardown_db
507
+ end
508
+
509
+ def test_reordering
510
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
511
+
512
+ ArrayScopeListMixin.find(2).move_lower
513
+ assert_equal [1, 3, 2, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
514
+
515
+ ArrayScopeListMixin.find(2).move_higher
516
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
517
+
518
+ ArrayScopeListMixin.find(1).move_to_bottom
519
+ assert_equal [2, 3, 4, 1], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
520
+
521
+ ArrayScopeListMixin.find(1).move_to_top
522
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
523
+
524
+ ArrayScopeListMixin.find(2).move_to_bottom
525
+ assert_equal [1, 3, 4, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
526
+
527
+ ArrayScopeListMixin.find(4).move_to_top
528
+ assert_equal [4, 1, 3, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
529
+ end
530
+
531
+ def test_move_to_bottom_with_next_to_last_item
532
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
533
+ ArrayScopeListMixin.find(3).move_to_bottom
534
+ assert_equal [1, 2, 4, 3], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
535
+ end
536
+
537
+ def test_next_prev
538
+ assert_equal ArrayScopeListMixin.find(2), ArrayScopeListMixin.find(1).lower_item
539
+ assert_nil ArrayScopeListMixin.find(1).higher_item
540
+ assert_equal ArrayScopeListMixin.find(3), ArrayScopeListMixin.find(4).higher_item
541
+ assert_nil ArrayScopeListMixin.find(4).lower_item
542
+ end
543
+
544
+ def test_injection
545
+ item = ArrayScopeListMixin.new(:parent_id => 1, :parent_type => 'ParentClass')
546
+ assert_equal '"mixins"."parent_id" = 1 AND "mixins"."parent_type" = \'ParentClass\'', item.scope_condition
547
+ assert_equal "pos", item.position_column
548
+ end
549
+
550
+ def test_insert
551
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
552
+ assert_equal 1, new.pos
553
+ assert new.first?
554
+ assert new.last?
555
+
556
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
557
+ assert_equal 2, new.pos
558
+ assert !new.first?
559
+ assert new.last?
560
+
561
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
562
+ assert_equal 3, new.pos
563
+ assert !new.first?
564
+ assert new.last?
565
+
566
+ new = ArrayScopeListMixin.create(:parent_id => 0, :parent_type => 'ParentClass')
567
+ assert_equal 1, new.pos
568
+ assert new.first?
569
+ assert new.last?
570
+ end
571
+
572
+ def test_insert_at
573
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
574
+ assert_equal 1, new.pos
575
+
576
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
577
+ assert_equal 2, new.pos
578
+
579
+ new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
580
+ assert_equal 3, new.pos
581
+
582
+ new4 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
583
+ assert_equal 4, new4.pos
584
+
585
+ new4.insert_at(3)
586
+ assert_equal 3, new4.pos
587
+
588
+ new.reload
589
+ assert_equal 4, new.pos
590
+
591
+ new.insert_at(2)
592
+ assert_equal 2, new.pos
593
+
594
+ new4.reload
595
+ assert_equal 4, new4.pos
596
+
597
+ new5 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
598
+ assert_equal 5, new5.pos
599
+
600
+ new5.insert_at(1)
601
+ assert_equal 1, new5.pos
602
+
603
+ new4.reload
604
+ assert_equal 5, new4.pos
605
+ end
606
+
607
+ def test_delete_middle
608
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
609
+
610
+ ArrayScopeListMixin.find(2).destroy
611
+
612
+ assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
613
+
614
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
615
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
616
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
617
+
618
+ ArrayScopeListMixin.find(1).destroy
619
+
620
+ assert_equal [3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
621
+
622
+ assert_equal 1, ArrayScopeListMixin.find(3).pos
623
+ assert_equal 2, ArrayScopeListMixin.find(4).pos
624
+ end
625
+
626
+ def test_remove_from_list_should_then_fail_in_list?
627
+ assert_equal true, ArrayScopeListMixin.find(1).in_list?
628
+ ArrayScopeListMixin.find(1).remove_from_list
629
+ assert_equal false, ArrayScopeListMixin.find(1).in_list?
630
+ end
631
+
632
+ def test_remove_from_list_should_set_position_to_nil
633
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
634
+
635
+ ArrayScopeListMixin.find(2).remove_from_list
636
+
637
+ assert_equal [2, 1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
638
+
639
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
640
+ assert_equal nil, ArrayScopeListMixin.find(2).pos
641
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
642
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
643
+ end
644
+
645
+ def test_remove_before_destroy_does_not_shift_lower_items_twice
646
+ assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
647
+
648
+ ArrayScopeListMixin.find(2).remove_from_list
649
+ ArrayScopeListMixin.find(2).destroy
650
+
651
+ assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
652
+
653
+ assert_equal 1, ArrayScopeListMixin.find(1).pos
654
+ assert_equal 2, ArrayScopeListMixin.find(3).pos
655
+ assert_equal 3, ArrayScopeListMixin.find(4).pos
656
+ end
657
+
658
+ end
659
+
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_list-rails3
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - David Heinemeier Hansson
14
+ - Swanand Pagnis
15
+ - Quinn Chaffee
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-05-30 00:00:00 -04:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: bundler
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ hash: 23
32
+ segments:
33
+ - 1
34
+ - 0
35
+ - 0
36
+ version: 1.0.0
37
+ type: :development
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: activerecord
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 15571
48
+ segments:
49
+ - 1
50
+ - 15
51
+ - 4
52
+ - 7794
53
+ version: 1.15.4.7794
54
+ type: :development
55
+ version_requirements: *id002
56
+ - !ruby/object:Gem::Dependency
57
+ name: rdoc
58
+ prerelease: false
59
+ requirement: &id003 !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ type: :development
69
+ version_requirements: *id003
70
+ - !ruby/object:Gem::Dependency
71
+ name: sqlite3-ruby
72
+ prerelease: false
73
+ requirement: &id004 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ type: :development
83
+ version_requirements: *id004
84
+ 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.
85
+ email:
86
+ - chaffeqa@gmail.com
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - .gitignore
95
+ - Gemfile
96
+ - README
97
+ - Rakefile
98
+ - acts_as_list.gemspec
99
+ - init.rb
100
+ - lib/acts_as_list.rb
101
+ - lib/acts_as_list/active_record/acts/list.rb
102
+ - lib/acts_as_list/version.rb
103
+ - test/helper.rb
104
+ - test/test_list.rb
105
+ has_rdoc: true
106
+ homepage: http://github.com/chaffeqa/acts_as_list
107
+ licenses: []
108
+
109
+ post_install_message:
110
+ rdoc_options: []
111
+
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ requirements: []
133
+
134
+ rubyforge_project: acts_as_list-rails3
135
+ rubygems_version: 1.6.2
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: A gem allowing a active_record model to act_as_list.
139
+ test_files: []
140
+