acts_as_restful_list 0.0.3 → 0.1.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/History.rdoc +9 -0
- data/README.rdoc +37 -2
- data/Todo.rdoc +6 -0
- data/VERSION +1 -1
- data/lib/acts_as_restful_list.rb +15 -4
- data/spec/acts_as_restful_list_spec.rb +115 -0
- metadata +4 -2
data/History.rdoc
ADDED
data/README.rdoc
CHANGED
@@ -1,12 +1,47 @@
|
|
1
1
|
= acts_as_restful_list
|
2
2
|
|
3
|
-
|
3
|
+
It's just like acts_as_list, but instead of having to clutter your code with
|
4
|
+
non-standard method calls like insert_at, acts_as_restful_list makes managing lists
|
5
|
+
simple. You update the position attribute just like you would update anything else
|
6
|
+
and the rest is taken care of for you.
|
7
|
+
|
8
|
+
For example:
|
9
|
+
|
10
|
+
class Item < ActiveRecord::Base
|
11
|
+
belongs_to :parent
|
12
|
+
acts_as_restful_list :scope => :parent
|
13
|
+
end
|
14
|
+
|
15
|
+
Now, simply CRUD away:
|
16
|
+
|
17
|
+
item = Item.create
|
18
|
+
item.position # 1
|
19
|
+
|
20
|
+
item2 = Item.create
|
21
|
+
item.position # 2
|
22
|
+
|
23
|
+
item3 = Item.create
|
24
|
+
item.position # 3
|
25
|
+
|
26
|
+
item.destroy
|
27
|
+
item2.position # 1
|
28
|
+
item3.position # 2
|
29
|
+
|
30
|
+
item3.position = 1
|
31
|
+
item3.save
|
32
|
+
item3.position # 1
|
33
|
+
item2.position # 2
|
34
|
+
|
35
|
+
different_parent = Item.create(:parent_id => 1)
|
36
|
+
different_parent.position # 1
|
37
|
+
|
38
|
+
And that's that.
|
4
39
|
|
5
40
|
== Note on Patches/Pull Requests
|
6
41
|
|
7
42
|
* Fork the project.
|
8
43
|
* Make your feature addition or bug fix.
|
9
|
-
* Add
|
44
|
+
* Add specs for it. This is important so I don't break it in a
|
10
45
|
future version unintentionally.
|
11
46
|
* Commit, do not mess with rakefile, version, or history.
|
12
47
|
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
data/Todo.rdoc
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/acts_as_restful_list.rb
CHANGED
@@ -9,6 +9,7 @@ module ActsAsRestfulList
|
|
9
9
|
# +acts_as_restful_list+ makes the class it is called on automatically behave like an
|
10
10
|
# ordered list. There are a number of options you can set:
|
11
11
|
# * +column+: The column to use as the position column. It's set to position by default.
|
12
|
+
# * +scope+: The column to scope the list to. It takes a symbol with our without the _id.
|
12
13
|
def acts_as_restful_list(options = {})
|
13
14
|
include InstanceMethods
|
14
15
|
|
@@ -21,27 +22,37 @@ module ActsAsRestfulList
|
|
21
22
|
define_method 'position_column' do
|
22
23
|
configuration[:column].to_s
|
23
24
|
end
|
25
|
+
|
26
|
+
define_method 'scope_condition' do
|
27
|
+
if configuration[:scope].nil?
|
28
|
+
nil
|
29
|
+
else
|
30
|
+
column = configuration[:scope].to_s.match(/_id$/) ? configuration[:scope].to_s : "#{configuration[:scope]}_id"
|
31
|
+
value = self.send(column)
|
32
|
+
value.nil? ? "#{column} IS NULL" : "#{column} = #{value}"
|
33
|
+
end
|
34
|
+
end
|
24
35
|
end
|
25
36
|
end
|
26
37
|
|
27
38
|
module InstanceMethods
|
28
39
|
def set_position
|
29
|
-
last_record = self.class.last( :order => "#{position_column} ASC" )
|
40
|
+
last_record = self.class.last( :conditions => scope_condition, :order => "#{position_column} ASC" )
|
30
41
|
self.send( "#{position_column}=", ( last_record.nil? ? 1 : last_record.send(position_column) + 1 ) )
|
31
42
|
end
|
32
43
|
|
33
44
|
def reset_order_after_update
|
34
45
|
if self.send( "#{position_column}_changed?" )
|
35
46
|
if self.send( "#{position_column}_was" ) > self.send( position_column )
|
36
|
-
self.class.update_all("#{position_column} = (#{position_column} + 1)", "#{position_column} >= #{self.send( position_column )}
|
47
|
+
self.class.update_all("#{position_column} = (#{position_column} + 1)", [scope_condition, "#{position_column} >= #{self.send( position_column )}", "id != #{id}"].compact.join(' AND '))
|
37
48
|
else
|
38
|
-
self.class.update_all("#{position_column} = (#{position_column} - 1)", "#{position_column} <= #{self.send( position_column )}
|
49
|
+
self.class.update_all("#{position_column} = (#{position_column} - 1)", [scope_condition, "#{position_column} <= #{self.send( position_column )}", "#{position_column} >= #{self.send( "#{position_column}_was" )}", "id != #{id}"].compact.join(' AND '))
|
39
50
|
end
|
40
51
|
end
|
41
52
|
end
|
42
53
|
|
43
54
|
def reset_order_after_destroy
|
44
|
-
self.class.update_all("#{position_column} = (#{position_column} - 1)", "#{position_column} > #{self.send( position_column )}")
|
55
|
+
self.class.update_all("#{position_column} = (#{position_column} - 1)", [scope_condition, "#{position_column} > #{self.send( position_column )}"].compact.join(' AND '))
|
45
56
|
end
|
46
57
|
end
|
47
58
|
end
|
@@ -92,6 +92,10 @@ describe "ActsAsRestfulList" do
|
|
92
92
|
Mixin.all(:order => 'position ASC').collect(&:position).should == [1,2,3]
|
93
93
|
end
|
94
94
|
end
|
95
|
+
|
96
|
+
it 'should return nil for scope_condition since it was not set' do
|
97
|
+
Mixin.new.scope_condition.should be_nil
|
98
|
+
end
|
95
99
|
end
|
96
100
|
|
97
101
|
|
@@ -182,4 +186,115 @@ describe "ActsAsRestfulList" do
|
|
182
186
|
end
|
183
187
|
end
|
184
188
|
end
|
189
|
+
|
190
|
+
describe 'declaring acts_as_restful_list and setting the scope' do
|
191
|
+
before(:all) do
|
192
|
+
ActiveRecord::Schema.define(:version => 1) do
|
193
|
+
create_table :mixins do |t|
|
194
|
+
t.column :position, :integer
|
195
|
+
t.column :parent_id, :integer
|
196
|
+
t.column :created_at, :datetime
|
197
|
+
t.column :updated_at, :datetime
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Mixin < ActiveRecord::Base
|
202
|
+
acts_as_restful_list :scope => :parent_id
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
after(:all) do
|
207
|
+
Object.send(:remove_const, :Mixin)
|
208
|
+
|
209
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
210
|
+
ActiveRecord::Base.connection.drop_table(table)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should define scope_condition as an instance method' do
|
215
|
+
Mixin.new.should respond_to(:scope_condition)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should return a scope condition that limits based on the parent_id' do
|
219
|
+
Mixin.new(:parent_id => 3).scope_condition.should == "parent_id = 3"
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'should return a scope limiting based parent_id being NULL if parent_id is nil' do
|
223
|
+
Mixin.new.scope_condition.should == "parent_id IS NULL"
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should set the position based on the scope list when adding a new item' do
|
227
|
+
Mixin.create!.position.should == 1
|
228
|
+
Mixin.create!(:parent_id => 1).position.should == 1
|
229
|
+
Mixin.create!(:parent_id => 1).position.should == 2
|
230
|
+
Mixin.create!(:parent_id => 2).position.should == 1
|
231
|
+
end
|
232
|
+
|
233
|
+
describe 'reordering on update' do
|
234
|
+
before(:each) do
|
235
|
+
(1..4).each{ Mixin.create!(:parent_id => 1) }
|
236
|
+
(1..6).each{ Mixin.create!(:parent_id => 2) }
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should automatically reorder the list if a record is updated with a lower position' do
|
240
|
+
fourth_mixin = Mixin.first( :conditions => { :position => 4, :parent_id => 1 } )
|
241
|
+
fourth_mixin.position = 2
|
242
|
+
fourth_mixin.save!
|
243
|
+
fourth_mixin.reload.position.should == 2
|
244
|
+
Mixin.all(:conditions => { :parent_id => 1 }, :order => 'position ASC').collect(&:position).should == [1,2,3,4]
|
245
|
+
Mixin.all(:conditions => { :parent_id => 2 }, :order => 'position ASC').collect(&:position).should == [1,2,3,4,5,6]
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should automatically reorder the list if a record is updated with a higher position' do
|
249
|
+
second_mixin = Mixin.first( :conditions => { :position => 2, :parent_id => 1 } )
|
250
|
+
second_mixin.position = 4
|
251
|
+
second_mixin.save!
|
252
|
+
second_mixin.reload.position.should == 4
|
253
|
+
Mixin.all(:conditions => { :parent_id => 1 }, :order => 'position ASC').collect(&:position).should == [1,2,3,4]
|
254
|
+
Mixin.all(:conditions => { :parent_id => 2 }, :order => 'position ASC').collect(&:position).should == [1,2,3,4,5,6]
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should automatically reorder the list scoped by parent if the record id deleted' do
|
259
|
+
(1..4).each{ Mixin.create!(:parent_id => 1) }
|
260
|
+
(1..6).each{ Mixin.create!(:parent_id => 2) }
|
261
|
+
second_mixin = Mixin.first( :conditions => { :position => 2, :parent_id => 1 } )
|
262
|
+
second_mixin.destroy
|
263
|
+
Mixin.all(:conditions => { :parent_id => 1 }, :order => 'position ASC').collect(&:position).should == [1,2,3]
|
264
|
+
Mixin.all(:conditions => { :parent_id => 2 }, :order => 'position ASC').collect(&:position).should == [1,2,3,4,5,6]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe 'declaring acts_as_restful_list and setting the scope without the _id' do
|
269
|
+
before(:all) do
|
270
|
+
ActiveRecord::Schema.define(:version => 1) do
|
271
|
+
create_table :mixins do |t|
|
272
|
+
t.column :position, :integer
|
273
|
+
t.column :parent_id, :integer
|
274
|
+
t.column :created_at, :datetime
|
275
|
+
t.column :updated_at, :datetime
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
class Mixin < ActiveRecord::Base
|
280
|
+
acts_as_restful_list :scope => :parent
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
after(:all) do
|
285
|
+
Object.send(:remove_const, :Mixin)
|
286
|
+
|
287
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
288
|
+
ActiveRecord::Base.connection.drop_table(table)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'should define scope_condition as an instance method' do
|
293
|
+
Mixin.new.should respond_to(:scope_condition)
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should return a scope condition that limits based on the parent_id' do
|
297
|
+
Mixin.new(:parent_id => 3).scope_condition.should == "parent_id = 3"
|
298
|
+
end
|
299
|
+
end
|
185
300
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_restful_list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "'Trey Bean'"
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-17 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -34,9 +34,11 @@ extra_rdoc_files:
|
|
34
34
|
files:
|
35
35
|
- .document
|
36
36
|
- .gitignore
|
37
|
+
- History.rdoc
|
37
38
|
- LICENSE
|
38
39
|
- README.rdoc
|
39
40
|
- Rakefile
|
41
|
+
- Todo.rdoc
|
40
42
|
- VERSION
|
41
43
|
- lib/acts_as_restful_list.rb
|
42
44
|
- spec/acts_as_restful_list_spec.rb
|