mongo_mapper_acts_as_list 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.
- data/README.rdoc +44 -0
- data/lib/mongo_mapper/plugins/acts_as_list.rb +234 -0
- data/lib/mongo_mapper/plugins/version.rb +8 -0
- data/test/mongo_mapper_acts_as_list_test.rb +335 -0
- data/test/test_helper.rb +31 -0
- metadata +98 -0
data/README.rdoc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= mongo_mapper_acts_as_list
|
2
|
+
|
3
|
+
This is a port of classic Rails' {acts_as_list}[http://github.com/rails/acts_as_list] to Mongo Mapper. This MongoMapper plugin provides the capabilities for sorting and reordering a number of objects in a list. If you do not specify custom position column in the options, a key named position will be used by default.
|
4
|
+
|
5
|
+
It has (almost) the same functionality and passes the original test-suite. Scope needs to be defined as symbol or array of symbols. It does not work for Embedded Documents.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
mongo_mapper_acts_as_list is available as RubyGem:
|
10
|
+
|
11
|
+
gem install mongo_mapper_acts_as_list
|
12
|
+
|
13
|
+
== Example
|
14
|
+
|
15
|
+
class TodoList
|
16
|
+
include MongoMapper::Document
|
17
|
+
|
18
|
+
many :todo_items, :order => "position"
|
19
|
+
end
|
20
|
+
|
21
|
+
class TodoItem
|
22
|
+
include MongoMapper::Document
|
23
|
+
plugin MongoMapper::Plugins::ActsAsList
|
24
|
+
|
25
|
+
key :todo_list_id, ObjectId
|
26
|
+
belongs_to :todo_list
|
27
|
+
|
28
|
+
acts_as_list :scope => :todo_list_id
|
29
|
+
end
|
30
|
+
|
31
|
+
todo_list.first.move_to_bottom
|
32
|
+
todo_list.last.move_higher
|
33
|
+
|
34
|
+
== Note on Patches/Pull Requests
|
35
|
+
|
36
|
+
* Fork the project.
|
37
|
+
* Make your feature addition or bug fix.
|
38
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
39
|
+
* Commit, do not mess with rakefile, version, or history. (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)
|
40
|
+
* Send me a pull request. Bonus points for topic branches.
|
41
|
+
|
42
|
+
== Copyright
|
43
|
+
|
44
|
+
Original Rails' acts_as_list Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
|
@@ -0,0 +1,234 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module ActsAsList
|
4
|
+
|
5
|
+
require 'mongo_mapper'
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def acts_as_list(options = {})
|
9
|
+
configuration = { :column => "position", :scope => {} }
|
10
|
+
configuration.update(options) if options.is_a?(Hash)
|
11
|
+
|
12
|
+
configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
|
13
|
+
|
14
|
+
if configuration[:scope].is_a?(Symbol)
|
15
|
+
scope_condition_method = %(
|
16
|
+
def scope_condition
|
17
|
+
{ "#{configuration[:scope].to_s}" => send(:#{configuration[:scope].to_s}) }.symbolize_keys!
|
18
|
+
end
|
19
|
+
)
|
20
|
+
elsif configuration[:scope].is_a?(Array)
|
21
|
+
scope_condition_method = %(
|
22
|
+
def scope_condition
|
23
|
+
attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo, column|
|
24
|
+
memo[column.intern] = send(column.intern)
|
25
|
+
memo
|
26
|
+
end
|
27
|
+
attrs.symbolize_keys!
|
28
|
+
end
|
29
|
+
)
|
30
|
+
else
|
31
|
+
scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
|
32
|
+
end
|
33
|
+
|
34
|
+
class_eval <<-EOV
|
35
|
+
include MongoMapper::Plugins::ActsAsList::InstanceMethods
|
36
|
+
|
37
|
+
def acts_as_list_class
|
38
|
+
::#{self.name}
|
39
|
+
end
|
40
|
+
|
41
|
+
def position_column
|
42
|
+
'#{configuration[:column]}'
|
43
|
+
end
|
44
|
+
|
45
|
+
#{scope_condition_method}
|
46
|
+
|
47
|
+
before_destroy :decrement_positions_on_lower_items
|
48
|
+
before_create :add_to_list_bottom
|
49
|
+
EOV
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
module InstanceMethods
|
57
|
+
# Insert the item at the given position (defaults to the top position of 1).
|
58
|
+
def insert_at(position = 1)
|
59
|
+
insert_at_position(position)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Swap positions with the next lower item, if one exists.
|
63
|
+
def move_lower
|
64
|
+
return unless lower_item
|
65
|
+
lower_item.decrement_position
|
66
|
+
increment_position
|
67
|
+
end
|
68
|
+
|
69
|
+
# Swap positions with the next higher item, if one exists.
|
70
|
+
def move_higher
|
71
|
+
return unless higher_item
|
72
|
+
higher_item.increment_position
|
73
|
+
decrement_position
|
74
|
+
end
|
75
|
+
|
76
|
+
# Move to the bottom of the list. If the item is already in the list, the items below it have their
|
77
|
+
# position adjusted accordingly.
|
78
|
+
def move_to_bottom
|
79
|
+
return unless in_list?
|
80
|
+
decrement_positions_on_lower_items
|
81
|
+
assume_bottom_position
|
82
|
+
end
|
83
|
+
|
84
|
+
# Move to the top of the list. If the item is already in the list, the items above it have their
|
85
|
+
# position adjusted accordingly.
|
86
|
+
def move_to_top
|
87
|
+
return unless in_list?
|
88
|
+
increment_positions_on_higher_items
|
89
|
+
assume_top_position
|
90
|
+
end
|
91
|
+
|
92
|
+
def update_position(value=nil)
|
93
|
+
# update_attribute position_column, value
|
94
|
+
self.set( { position_column.to_sym => value } )
|
95
|
+
self[position_column] = value
|
96
|
+
end
|
97
|
+
|
98
|
+
# Removes the item from the list.
|
99
|
+
def remove_from_list
|
100
|
+
if in_list?
|
101
|
+
decrement_positions_on_lower_items
|
102
|
+
update_position( nil )
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Increase the position of this item without adjusting the rest of the list.
|
107
|
+
def increment_position
|
108
|
+
return unless in_list?
|
109
|
+
update_position( self.send(position_column).to_i+1 )
|
110
|
+
end
|
111
|
+
|
112
|
+
# Decrease the position of this item without adjusting the rest of the list.
|
113
|
+
def decrement_position
|
114
|
+
return unless in_list?
|
115
|
+
update_position( self.send(position_column).to_i-1 )
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return +true+ if this object is the first in the list.
|
119
|
+
def first?
|
120
|
+
return false unless in_list?
|
121
|
+
self.send(position_column) == 1
|
122
|
+
end
|
123
|
+
|
124
|
+
# Return +true+ if this object is the last in the list.
|
125
|
+
def last?
|
126
|
+
return false unless in_list?
|
127
|
+
self.send(position_column) == bottom_position_in_list
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return the next higher item in the list.
|
131
|
+
def higher_item
|
132
|
+
return nil unless in_list?
|
133
|
+
conditions = scope_condition
|
134
|
+
conditions.merge!( { position_column.to_sym => send(position_column).to_i-1 } )
|
135
|
+
acts_as_list_class.where(conditions).first
|
136
|
+
end
|
137
|
+
|
138
|
+
# Return the next lower item in the list.
|
139
|
+
def lower_item
|
140
|
+
return nil unless in_list?
|
141
|
+
conditions = scope_condition
|
142
|
+
conditions.merge!( { position_column.to_sym => send(position_column).to_i+1 } )
|
143
|
+
acts_as_list_class.where(conditions).first
|
144
|
+
end
|
145
|
+
|
146
|
+
# Test if this record is in a list
|
147
|
+
def in_list?
|
148
|
+
!send(position_column).nil?
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def add_to_list_top
|
154
|
+
increment_positions_on_all_items
|
155
|
+
end
|
156
|
+
|
157
|
+
def add_to_list_bottom
|
158
|
+
self[position_column] = bottom_position_in_list.to_i+1
|
159
|
+
end
|
160
|
+
|
161
|
+
def scope_condition() "1" end
|
162
|
+
|
163
|
+
# Returns the bottom position number in the list.
|
164
|
+
# bottom_position_in_list # => 2
|
165
|
+
def bottom_position_in_list(except = nil)
|
166
|
+
item = bottom_item(except)
|
167
|
+
item ? item.send(position_column) : 0
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the bottom item
|
171
|
+
def bottom_item(except = nil)
|
172
|
+
conditions = scope_condition
|
173
|
+
conditions.merge!( { :id.ne => except.id } ) if except
|
174
|
+
acts_as_list_class.where(conditions).sort(position_column.to_sym.desc).first
|
175
|
+
end
|
176
|
+
|
177
|
+
# Forces item to assume the bottom position in the list.
|
178
|
+
def assume_bottom_position
|
179
|
+
update_position( bottom_position_in_list(self).to_i+1 )
|
180
|
+
end
|
181
|
+
|
182
|
+
# Forces item to assume the top position in the list.
|
183
|
+
def assume_top_position
|
184
|
+
update_position 1
|
185
|
+
end
|
186
|
+
|
187
|
+
# This has the effect of moving all the higher items up one.
|
188
|
+
def decrement_positions_on_higher_items(position)
|
189
|
+
conditions = scope_condition
|
190
|
+
conditions.merge!( { position_column.to_sym.lt => position } )
|
191
|
+
acts_as_list_class.decrement( conditions, { position_column.to_sym => 1 } )
|
192
|
+
end
|
193
|
+
|
194
|
+
# This has the effect of moving all the lower items up one.
|
195
|
+
def decrement_positions_on_lower_items
|
196
|
+
return unless in_list?
|
197
|
+
conditions = scope_condition
|
198
|
+
conditions.merge!( { position_column.to_sym.gt => send(position_column).to_i } )
|
199
|
+
acts_as_list_class.decrement( conditions, { position_column => 1 } )
|
200
|
+
end
|
201
|
+
|
202
|
+
# This has the effect of moving all the higher items down one.
|
203
|
+
def increment_positions_on_higher_items
|
204
|
+
return unless in_list?
|
205
|
+
conditions = scope_condition
|
206
|
+
conditions.merge!( { position_column.to_sym.lt => send(position_column).to_i } )
|
207
|
+
acts_as_list_class.increment( conditions, { position_column.to_sym => 1 } )
|
208
|
+
end
|
209
|
+
|
210
|
+
# This has the effect of moving all the lower items down one.
|
211
|
+
def increment_positions_on_lower_items(position)
|
212
|
+
conditions = scope_condition
|
213
|
+
conditions.merge!( { position_column.to_sym.gte => position } )
|
214
|
+
acts_as_list_class.increment( conditions, { position_column.to_sym => 1 } )
|
215
|
+
end
|
216
|
+
|
217
|
+
# Increments position (<tt>position_column</tt>) of all items in the list.
|
218
|
+
def increment_positions_on_all_items
|
219
|
+
conditions = scope_condition
|
220
|
+
acts_as_list_class.increment( conditions, { position_column.to_sym => 1 } )
|
221
|
+
end
|
222
|
+
|
223
|
+
def insert_at_position(position)
|
224
|
+
remove_from_list
|
225
|
+
increment_positions_on_lower_items( position )
|
226
|
+
update_position( position )
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,335 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
# CLASS SETUP
|
6
|
+
|
7
|
+
class ListMixin
|
8
|
+
include MongoMapper::Document
|
9
|
+
|
10
|
+
plugin MongoMapper::Plugins::ActsAsList
|
11
|
+
|
12
|
+
key :pos, Integer
|
13
|
+
key :parent_id, Integer
|
14
|
+
key :original_id, Integer
|
15
|
+
|
16
|
+
acts_as_list :column => :pos, :scope => :parent_id
|
17
|
+
end
|
18
|
+
|
19
|
+
class ListMixinSub1 < ListMixin
|
20
|
+
end
|
21
|
+
|
22
|
+
class ListMixinSub2 < ListMixin
|
23
|
+
end
|
24
|
+
|
25
|
+
class ListMixinWithArrayScope
|
26
|
+
include MongoMapper::Document
|
27
|
+
|
28
|
+
plugin MongoMapper::Plugins::ActsAsList
|
29
|
+
|
30
|
+
key :pos, Integer
|
31
|
+
key :parent_id, Integer
|
32
|
+
|
33
|
+
acts_as_list :column => :pos, :scope => [:parent_id, :original_id]
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
# TESTS
|
39
|
+
|
40
|
+
class ScopeTest < ActiveSupport::TestCase
|
41
|
+
|
42
|
+
def setup
|
43
|
+
@lm1 = ListMixin.create! :pos => 1, :parent_id => 5, :original_id => 1
|
44
|
+
@lm2 = ListMixinWithArrayScope.create! :pos => 1, :parent_id => 5, :original_id => 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_symbol_scope
|
48
|
+
assert_equal @lm1.scope_condition, { :parent_id => 5 }
|
49
|
+
assert_equal @lm2.scope_condition, { :parent_id => 5, :original_id => 1 }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
class ActiveSupport::TestCase
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def mixins_with_parent_id(parent_id)
|
61
|
+
ListMixin.where(:parent_id => parent_id).sort(:pos).all.map(&:original_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def mixin_with_original_id(original_id)
|
65
|
+
ListMixin.where(:original_id => original_id).first
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
class ListTest < ActiveSupport::TestCase
|
72
|
+
|
73
|
+
def setup
|
74
|
+
(1..4).each{ |counter| ListMixin.create! :pos => counter, :parent_id => 5, :original_id => counter }
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_reordering
|
78
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
79
|
+
|
80
|
+
mixin_with_original_id(2).move_lower
|
81
|
+
assert_equal [1, 3, 2, 4], mixins_with_parent_id(5)
|
82
|
+
|
83
|
+
mixin_with_original_id(2).move_higher
|
84
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
85
|
+
|
86
|
+
mixin_with_original_id(1).move_to_bottom
|
87
|
+
assert_equal [2, 3, 4, 1], mixins_with_parent_id(5)
|
88
|
+
|
89
|
+
mixin_with_original_id(1).move_to_top
|
90
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
91
|
+
|
92
|
+
mixin_with_original_id(2).move_to_bottom
|
93
|
+
assert_equal [1, 3, 4, 2], mixins_with_parent_id(5)
|
94
|
+
|
95
|
+
mixin_with_original_id(4).move_to_top
|
96
|
+
assert_equal [4, 1, 3, 2], mixins_with_parent_id(5)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_move_to_bottom_with_next_to_last_item
|
100
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
101
|
+
mixin_with_original_id(3).move_to_bottom
|
102
|
+
assert_equal [1, 2, 4, 3], mixins_with_parent_id(5)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_next_prev
|
106
|
+
assert_equal mixin_with_original_id(2), mixin_with_original_id(1).lower_item
|
107
|
+
assert_nil mixin_with_original_id(1).higher_item
|
108
|
+
assert_equal mixin_with_original_id(3), mixin_with_original_id(4).higher_item
|
109
|
+
assert_nil mixin_with_original_id(4).lower_item
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_injection
|
113
|
+
item = ListMixin.new(:parent_id => 1)
|
114
|
+
assert_equal item.scope_condition, {:parent_id => 1}
|
115
|
+
assert_equal "pos", item.position_column
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_insert
|
119
|
+
new = ListMixin.create(:parent_id => 20)
|
120
|
+
assert_equal 1, new.pos
|
121
|
+
assert new.first?
|
122
|
+
assert new.last?
|
123
|
+
|
124
|
+
new = ListMixin.create(:parent_id => 20)
|
125
|
+
assert_equal 2, new.pos
|
126
|
+
assert !new.first?
|
127
|
+
assert new.last?
|
128
|
+
|
129
|
+
new = ListMixin.create(:parent_id => 20)
|
130
|
+
assert_equal 3, new.pos
|
131
|
+
assert !new.first?
|
132
|
+
assert new.last?
|
133
|
+
|
134
|
+
new = ListMixin.create(:parent_id => 0)
|
135
|
+
assert_equal 1, new.pos
|
136
|
+
assert new.first?
|
137
|
+
assert new.last?
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_insert_at
|
141
|
+
new = ListMixin.create(:parent_id => 20)
|
142
|
+
assert_equal 1, new.pos
|
143
|
+
|
144
|
+
new = ListMixin.create(:parent_id => 20)
|
145
|
+
assert_equal 2, new.pos
|
146
|
+
|
147
|
+
new = ListMixin.create(:parent_id => 20)
|
148
|
+
assert_equal 3, new.pos
|
149
|
+
|
150
|
+
new4 = ListMixin.create(:parent_id => 20)
|
151
|
+
assert_equal 4, new4.pos
|
152
|
+
|
153
|
+
new4.insert_at(3)
|
154
|
+
assert_equal 3, new4.pos
|
155
|
+
|
156
|
+
new.reload
|
157
|
+
assert_equal 4, new.pos
|
158
|
+
|
159
|
+
new.insert_at(2)
|
160
|
+
assert_equal 2, new.pos
|
161
|
+
|
162
|
+
new4.reload
|
163
|
+
assert_equal 4, new4.pos
|
164
|
+
|
165
|
+
new5 = ListMixin.create(:parent_id => 20)
|
166
|
+
assert_equal 5, new5.pos
|
167
|
+
|
168
|
+
new5.insert_at(1)
|
169
|
+
assert_equal 1, new5.pos
|
170
|
+
|
171
|
+
new4.reload
|
172
|
+
assert_equal 5, new4.pos
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_delete_middle
|
176
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
177
|
+
|
178
|
+
mixin_with_original_id(2).destroy
|
179
|
+
|
180
|
+
assert_equal [1, 3, 4], mixins_with_parent_id(5)
|
181
|
+
|
182
|
+
assert_equal 1, mixin_with_original_id(1).pos
|
183
|
+
assert_equal 2, mixin_with_original_id(3).pos
|
184
|
+
assert_equal 3, mixin_with_original_id(4).pos
|
185
|
+
|
186
|
+
mixin_with_original_id(1).destroy
|
187
|
+
|
188
|
+
assert_equal [3, 4], mixins_with_parent_id(5)
|
189
|
+
|
190
|
+
assert_equal 1, mixin_with_original_id(3).pos
|
191
|
+
assert_equal 2, mixin_with_original_id(4).pos
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_nil_scope
|
195
|
+
new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
|
196
|
+
new2.move_higher
|
197
|
+
assert_equal [new2, new1, new3], ListMixin.where(:parent_id => nil).sort(:pos).all
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_remove_from_list_should_then_fail_in_list?
|
201
|
+
assert_equal true, mixin_with_original_id(1).in_list?
|
202
|
+
mixin_with_original_id(1).remove_from_list
|
203
|
+
assert_equal false, mixin_with_original_id(1).in_list?
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_remove_from_list_should_set_position_to_nil
|
207
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
208
|
+
|
209
|
+
mixin_with_original_id(2).remove_from_list
|
210
|
+
|
211
|
+
assert_equal [2, 1, 3, 4], mixins_with_parent_id(5)
|
212
|
+
|
213
|
+
assert_equal 1, mixin_with_original_id(1).pos
|
214
|
+
assert_equal nil, mixin_with_original_id(2).pos
|
215
|
+
assert_equal 2, mixin_with_original_id(3).pos
|
216
|
+
assert_equal 3, mixin_with_original_id(4).pos
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_remove_before_destroy_does_not_shift_lower_items_twice
|
220
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5)
|
221
|
+
|
222
|
+
mixin_with_original_id(2).remove_from_list
|
223
|
+
mixin_with_original_id(2).destroy
|
224
|
+
|
225
|
+
assert_equal [1, 3, 4], mixins_with_parent_id(5)
|
226
|
+
|
227
|
+
assert_equal 1, mixin_with_original_id(1).pos
|
228
|
+
assert_equal 2, mixin_with_original_id(3).pos
|
229
|
+
assert_equal 3, mixin_with_original_id(4).pos
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
class ListSubTest < ActiveSupport::TestCase
|
235
|
+
|
236
|
+
def setup
|
237
|
+
(1..4).each{ |i| ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).create! :pos => i, :parent_id => 5000, :original_id => i }
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_reordering
|
241
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5000)
|
242
|
+
|
243
|
+
mixin_with_original_id(2).move_lower
|
244
|
+
assert_equal [1, 3, 2, 4], mixins_with_parent_id(5000)
|
245
|
+
|
246
|
+
mixin_with_original_id(2).move_higher
|
247
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5000)
|
248
|
+
|
249
|
+
mixin_with_original_id(1).move_to_bottom
|
250
|
+
assert_equal [2, 3, 4, 1], mixins_with_parent_id(5000)
|
251
|
+
|
252
|
+
mixin_with_original_id(1).move_to_top
|
253
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5000)
|
254
|
+
|
255
|
+
mixin_with_original_id(2).move_to_bottom
|
256
|
+
assert_equal [1, 3, 4, 2], mixins_with_parent_id(5000)
|
257
|
+
|
258
|
+
mixin_with_original_id(4).move_to_top
|
259
|
+
assert_equal [4, 1, 3, 2], mixins_with_parent_id(5000)
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_move_to_bottom_with_next_to_last_item
|
263
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5000)
|
264
|
+
mixin_with_original_id(3).move_to_bottom
|
265
|
+
assert_equal [1, 2, 4, 3], mixins_with_parent_id(5000)
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_next_prev
|
269
|
+
assert_equal mixin_with_original_id(2), mixin_with_original_id(1).lower_item
|
270
|
+
assert_nil mixin_with_original_id(1).higher_item
|
271
|
+
assert_equal mixin_with_original_id(3), mixin_with_original_id(4).higher_item
|
272
|
+
assert_nil mixin_with_original_id(4).lower_item
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_injection
|
276
|
+
item = ListMixin.new(:parent_id => 1)
|
277
|
+
assert_equal item.scope_condition, { :parent_id => 1 }
|
278
|
+
assert_equal "pos", item.position_column
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_insert_at
|
282
|
+
new = ListMixin.create(:parent_id => 20)
|
283
|
+
assert_equal 1, new.pos
|
284
|
+
|
285
|
+
new = ListMixinSub1.create(:parent_id => 20)
|
286
|
+
assert_equal 2, new.pos
|
287
|
+
|
288
|
+
new = ListMixinSub2.create(:parent_id => 20)
|
289
|
+
assert_equal 3, new.pos
|
290
|
+
|
291
|
+
new4 = ListMixin.create(:parent_id => 20)
|
292
|
+
assert_equal 4, new4.pos
|
293
|
+
|
294
|
+
new4.insert_at(3)
|
295
|
+
assert_equal 3, new4.pos
|
296
|
+
|
297
|
+
new.reload
|
298
|
+
assert_equal 4, new.pos
|
299
|
+
|
300
|
+
new.insert_at(2)
|
301
|
+
assert_equal 2, new.pos
|
302
|
+
|
303
|
+
new4.reload
|
304
|
+
assert_equal 4, new4.pos
|
305
|
+
|
306
|
+
new5 = ListMixinSub1.create(:parent_id => 20)
|
307
|
+
assert_equal 5, new5.pos
|
308
|
+
|
309
|
+
new5.insert_at(1)
|
310
|
+
assert_equal 1, new5.pos
|
311
|
+
|
312
|
+
new4.reload
|
313
|
+
assert_equal 5, new4.pos
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_delete_middle
|
317
|
+
assert_equal [1, 2, 3, 4], mixins_with_parent_id(5000)
|
318
|
+
|
319
|
+
mixin_with_original_id(2).destroy
|
320
|
+
|
321
|
+
assert_equal [1, 3, 4], mixins_with_parent_id(5000)
|
322
|
+
|
323
|
+
assert_equal 1, mixin_with_original_id(1).pos
|
324
|
+
assert_equal 2, mixin_with_original_id(3).pos
|
325
|
+
assert_equal 3, mixin_with_original_id(4).pos
|
326
|
+
|
327
|
+
mixin_with_original_id(1).destroy
|
328
|
+
|
329
|
+
assert_equal [3, 4], mixins_with_parent_id(5000)
|
330
|
+
|
331
|
+
assert_equal 1, mixin_with_original_id(3).pos
|
332
|
+
assert_equal 2, mixin_with_original_id(4).pos
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
|
4
|
+
require 'mongo_mapper'
|
5
|
+
require 'mongo_mapper/plugins/acts_as_list'
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
class ActiveSupport::TestCase
|
11
|
+
|
12
|
+
# Drop all collections after each test case.
|
13
|
+
def teardown
|
14
|
+
MongoMapper.database.collections.each { |coll| coll.remove }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Make sure that each test case has a teardown
|
18
|
+
# method to clear the db after each test.
|
19
|
+
def inherited(base)
|
20
|
+
base.define_method teardown do
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017)
|
30
|
+
MongoMapper.database = "mongo_mapper_acts_as_list_test"
|
31
|
+
MongoMapper.database.collections.each { |c| c.drop_indexes }
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongo_mapper_acts_as_list
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Tomas Celizna
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-31 01:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: mongo_mapper
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rake
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description:
|
49
|
+
email:
|
50
|
+
- tomas.celizna@gmail.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- lib/mongo_mapper/plugins/acts_as_list.rb
|
59
|
+
- lib/mongo_mapper/plugins/version.rb
|
60
|
+
- test/mongo_mapper_acts_as_list_test.rb
|
61
|
+
- test/test_helper.rb
|
62
|
+
- README.rdoc
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/tomasc/mongo_mapper_acts_as_list
|
65
|
+
licenses: []
|
66
|
+
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
requirements: []
|
91
|
+
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 1.3.7
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: Port of classic Rails ActsAsList for MongoMapper
|
97
|
+
test_files: []
|
98
|
+
|