mongoid_orderable 5.0.0 → 6.0.2

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.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +83 -22
  3. data/LICENSE.txt +20 -0
  4. data/README.md +256 -149
  5. data/Rakefile +24 -6
  6. data/lib/config/locales/en.yml +12 -9
  7. data/lib/mongoid/orderable.rb +29 -22
  8. data/lib/mongoid/orderable/configs/field_config.rb +79 -0
  9. data/lib/mongoid/orderable/configs/global_config.rb +26 -0
  10. data/lib/mongoid/orderable/errors/invalid_target_position.rb +19 -18
  11. data/lib/mongoid/orderable/errors/transaction_failed.rb +20 -0
  12. data/lib/mongoid/orderable/generators/base.rb +21 -0
  13. data/lib/mongoid/orderable/generators/helpers.rb +29 -0
  14. data/lib/mongoid/orderable/generators/listable.rb +41 -0
  15. data/lib/mongoid/orderable/generators/lock_collection.rb +37 -0
  16. data/lib/mongoid/orderable/generators/movable.rb +62 -0
  17. data/lib/mongoid/orderable/generators/position.rb +26 -0
  18. data/lib/mongoid/orderable/generators/scope.rb +26 -0
  19. data/lib/mongoid/orderable/handlers/base.rb +167 -0
  20. data/lib/mongoid/orderable/handlers/document.rb +24 -0
  21. data/lib/mongoid/orderable/handlers/document_embedded.rb +14 -0
  22. data/lib/mongoid/orderable/handlers/document_transactional.rb +35 -0
  23. data/lib/mongoid/orderable/handlers/transaction.rb +71 -0
  24. data/lib/mongoid/orderable/installer.rb +63 -0
  25. data/lib/mongoid/orderable/mixins/callbacks.rb +43 -0
  26. data/lib/mongoid/orderable/mixins/helpers.rb +39 -0
  27. data/lib/mongoid/orderable/mixins/listable.rb +49 -0
  28. data/lib/mongoid/orderable/mixins/movable.rb +60 -0
  29. data/lib/mongoid/orderable/version.rb +7 -0
  30. data/lib/mongoid_orderable.rb +33 -54
  31. data/spec/integration/concurrency_spec.rb +232 -0
  32. data/spec/integration/customized_spec.rb +31 -0
  33. data/spec/integration/embedded_spec.rb +41 -0
  34. data/spec/integration/foreign_key_spec.rb +33 -0
  35. data/spec/integration/inherited_spec.rb +54 -0
  36. data/spec/integration/multiple_fields_spec.rb +554 -0
  37. data/spec/integration/multiple_scoped_spec.rb +63 -0
  38. data/spec/integration/no_indexed_spec.rb +23 -0
  39. data/spec/integration/scoped_spec.rb +151 -0
  40. data/spec/integration/simple_spec.rb +184 -0
  41. data/spec/integration/string_scoped_spec.rb +28 -0
  42. data/spec/integration/zero_based_spec.rb +161 -0
  43. data/spec/spec_helper.rb +42 -30
  44. data/spec/support/models.rb +122 -0
  45. metadata +75 -41
  46. data/.gitignore +0 -4
  47. data/.rspec +0 -2
  48. data/.rvmrc +0 -1
  49. data/.travis.yml +0 -14
  50. data/Gemfile +0 -18
  51. data/lib/mongoid/orderable/callbacks.rb +0 -75
  52. data/lib/mongoid/orderable/configuration.rb +0 -60
  53. data/lib/mongoid/orderable/errors.rb +0 -2
  54. data/lib/mongoid/orderable/errors/mongoid_orderable_error.rb +0 -14
  55. data/lib/mongoid/orderable/generator.rb +0 -34
  56. data/lib/mongoid/orderable/generator/helpers.rb +0 -29
  57. data/lib/mongoid/orderable/generator/listable.rb +0 -41
  58. data/lib/mongoid/orderable/generator/movable.rb +0 -62
  59. data/lib/mongoid/orderable/generator/position.rb +0 -26
  60. data/lib/mongoid/orderable/generator/scope.rb +0 -17
  61. data/lib/mongoid/orderable/helpers.rb +0 -50
  62. data/lib/mongoid/orderable/listable.rb +0 -49
  63. data/lib/mongoid/orderable/movable.rb +0 -58
  64. data/lib/mongoid/orderable/orderable_class.rb +0 -51
  65. data/lib/mongoid_orderable/mongoid/contexts/enumerable.rb +0 -15
  66. data/lib/mongoid_orderable/mongoid/contexts/mongo.rb +0 -18
  67. data/lib/mongoid_orderable/mongoid/contextual/memory.rb +0 -15
  68. data/lib/mongoid_orderable/mongoid/criteria.rb +0 -4
  69. data/lib/mongoid_orderable/version.rb +0 -3
  70. data/mongoid_orderable.gemspec +0 -26
  71. data/spec/mongoid/orderable_spec.rb +0 -1413
@@ -1,49 +0,0 @@
1
- module Mongoid
2
- module Orderable
3
- module Listable
4
-
5
- ##
6
- # Returns items above the current document.
7
- # Items with a position lower than this document's position.
8
- def previous_items(column=nil)
9
- column = column || default_orderable_column
10
- orderable_scoped(column).where(orderable_column(column).lt => send(column))
11
- end
12
- alias_method :prev_items, :previous_items
13
-
14
- ##
15
- # Returns items below the current document.
16
- # Items with a position greater than this document's position.
17
- def next_items(column=nil)
18
- column = column || default_orderable_column
19
- orderable_scoped(column).where(orderable_column(column).gt => send(column))
20
- end
21
-
22
- # returns the previous item in the list
23
- def previous_item(column=nil)
24
- column = column || default_orderable_column
25
- orderable_scoped(column).where(orderable_column(column) => send(column) - 1).first
26
- end
27
- alias_method :prev_item, :previous_item
28
-
29
- # returns the next item in the list
30
- def next_item(column=nil)
31
- column = column || default_orderable_column
32
- orderable_scoped(column).where(orderable_column(column) => send(column) + 1).first
33
- end
34
-
35
- def first?(column=nil)
36
- in_list?(column) && orderable_position(column) == orderable_base(column)
37
- end
38
-
39
- def last?(column=nil)
40
- in_list?(column) && orderable_position(column) == bottom_orderable_position(column)
41
- end
42
-
43
- def in_list?(column=nil)
44
- !orderable_position(column).nil?
45
- end
46
-
47
- end
48
- end
49
- end
@@ -1,58 +0,0 @@
1
- module Mongoid
2
- module Orderable
3
- module Movable
4
-
5
- def move_to!(target_position, options={})
6
- move_column_to target_position, options
7
- save
8
- end
9
- alias_method :insert_at!, :move_to!
10
-
11
- def move_to(target_position, options={})
12
- move_column_to target_position, options
13
- end
14
- alias_method :insert_at, :move_to
15
-
16
- def move_to=(target_position, options={})
17
- move_column_to target_position, options
18
- end
19
- alias_method :insert_at=, :move_to=
20
-
21
- [:top, :bottom].each do |symbol|
22
- class_eval <<-eos
23
- def move_to_#{symbol}(options = {})
24
- move_to :#{symbol}, options
25
- end
26
-
27
- def move_to_#{symbol}!(options = {})
28
- move_to! :#{symbol}, options
29
- end
30
- eos
31
- end
32
-
33
- [:higher, :lower].each do |symbol|
34
- class_eval <<-eos
35
- def move_#{symbol}(options = {})
36
- move_to :#{symbol}, options
37
- end
38
-
39
- def move_#{symbol}!(options = {})
40
- move_to! :#{symbol}, options
41
- end
42
- eos
43
- end
44
-
45
- protected
46
-
47
- def move_all
48
- @move_all || {}
49
- end
50
-
51
- def move_column_to(position, options)
52
- column = options[:column] || default_orderable_column
53
- @move_all = move_all.merge(column => position)
54
- end
55
-
56
- end
57
- end
58
- end
@@ -1,51 +0,0 @@
1
- module Mongoid
2
- module Orderable
3
- class OrderableClass
4
- include Mongoid::Orderable::Generator
5
-
6
- attr_reader :klass, :configuration
7
-
8
- def initialize klass, configuration
9
- @klass = klass
10
- @configuration = configuration
11
- end
12
-
13
- def setup
14
- add_db_field
15
- add_db_index if configuration[:index]
16
- save_configuration
17
- generate_all_helpers
18
- add_callbacks
19
- end
20
-
21
- def self.setup(klass, configuration={})
22
- new(klass, configuration).setup
23
- end
24
-
25
- protected
26
-
27
- def add_db_field
28
- klass.field configuration[:column], configuration[:field_opts]
29
- end
30
-
31
- def add_db_index
32
- spec = [[configuration[:column], 1]]
33
- spec.unshift([configuration[:scope], 1]) if configuration[:scope].is_a?(Symbol)
34
- if ::Mongoid::Compatibility::Version.mongoid2?
35
- klass.index(spec)
36
- else
37
- klass.index(Hash[spec])
38
- end
39
- end
40
-
41
- def save_configuration
42
- klass.orderable_configurations ||= {}
43
- klass.orderable_configurations = klass.orderable_configurations.merge(column_name => configuration)
44
- end
45
-
46
- def add_callbacks
47
- klass.add_orderable_callbacks
48
- end
49
- end
50
- end
51
- end
@@ -1,15 +0,0 @@
1
- module MongoidOrderable #:nodoc:
2
- module Mongoid #:nodoc:
3
- module Contexts #:nodoc:
4
- module Enumerable #:nodoc:
5
- def inc attribute, value
6
- iterate do |doc|
7
- MongoidOrderable.inc doc, attribute, value
8
- end
9
- end
10
- end
11
- end
12
- end
13
- end
14
-
15
- Mongoid::Contexts::Enumerable.send :include, MongoidOrderable::Mongoid::Contexts::Enumerable
@@ -1,18 +0,0 @@
1
- module MongoidOrderable #:nodoc:
2
- module Mongoid #:nodoc:
3
- module Contexts #:nodoc:
4
- module Mongo #:nodoc:
5
- def inc attribute, value
6
- klass.collection.update(
7
- selector,
8
- { "$inc" => {attribute => value} },
9
- :multi => true,
10
- :safe => ::Mongoid.persist_in_safe_mode
11
- )
12
- end
13
- end
14
- end
15
- end
16
- end
17
-
18
- Mongoid::Contexts::Mongo.send :include, MongoidOrderable::Mongoid::Contexts::Mongo
@@ -1,15 +0,0 @@
1
- module MongoidOrderable #:nodoc:
2
- module Mongoid #:nodoc:
3
- module Contextual #:nodoc:
4
- module Memory #:nodoc:
5
- def inc(* args)
6
- each do |document|
7
- document.inc *args
8
- end
9
- end
10
- end
11
- end
12
- end
13
- end
14
-
15
- Mongoid::Contextual::Memory.send :include, MongoidOrderable::Mongoid::Contextual::Memory
@@ -1,4 +0,0 @@
1
- Mongoid::Criteria.delegate :inc, :to => :context
2
- Mongoid::Finders.send :define_method, :inc do |*args|
3
- criteria.send :inc, *args
4
- end
@@ -1,3 +0,0 @@
1
- module MongoidOrderable
2
- VERSION = '5.0.0'
3
- end
@@ -1,26 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "mongoid_orderable/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "mongoid_orderable"
7
- s.version = MongoidOrderable::VERSION
8
- s.authors = ["pyromaniac"]
9
- s.email = ["kinwizard@gmail.com"]
10
- s.homepage = ""
11
- s.summary = %q{Acts as list mongoid implementation}
12
- s.description = %q{Gem allows mongoid model behave as orderable list}
13
-
14
- s.rubyforge_project = "mongoid_orderable"
15
-
16
- s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
- s.require_paths = ["lib"]
20
-
21
- # specify any dependencies here; for example:
22
- s.add_development_dependency "rake"
23
- s.add_development_dependency "rspec"
24
- s.add_runtime_dependency "mongoid"
25
- s.add_runtime_dependency "mongoid-compatibility"
26
- end
@@ -1,1413 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Mongoid::Orderable do
4
-
5
- class SimpleOrderable
6
- include Mongoid::Document
7
- include Mongoid::Orderable
8
-
9
- orderable
10
- end
11
-
12
- class ScopedGroup
13
- include Mongoid::Document
14
-
15
- has_many :scoped_orderables
16
- has_many :multiple_columns_orderables
17
- end
18
-
19
- class ScopedOrderable
20
- include Mongoid::Document
21
- include Mongoid::Orderable
22
-
23
- field :group_id
24
- belongs_to :scoped_group
25
-
26
- orderable :scope => :group
27
- end
28
-
29
- class StringScopedOrderable
30
- include Mongoid::Document
31
- include Mongoid::Orderable
32
-
33
- field :some_scope, :type => Integer
34
-
35
- orderable :scope => 'some_scope'
36
- end
37
-
38
- class EmbedsOrderable
39
- include Mongoid::Document
40
-
41
- embeds_many :embedded_orderables
42
- end
43
-
44
- class EmbeddedOrderable
45
- include Mongoid::Document
46
- include Mongoid::Orderable
47
-
48
- embedded_in :embeds_orderable
49
-
50
- orderable
51
- end
52
-
53
- class CustomizedOrderable
54
- include Mongoid::Document
55
- include Mongoid::Orderable
56
-
57
- orderable :column => :pos, :as => :my_position
58
- end
59
-
60
- class NoIndexOrderable
61
- include Mongoid::Document
62
- include Mongoid::Orderable
63
-
64
- orderable :index => false
65
- end
66
-
67
- class ZeroBasedOrderable
68
- include Mongoid::Document
69
- include Mongoid::Orderable
70
-
71
- orderable :base => 0
72
- end
73
-
74
- class Fruit
75
- include Mongoid::Document
76
- include Mongoid::Orderable
77
-
78
- orderable :inherited => true
79
- end
80
-
81
- class Apple < Fruit
82
- end
83
-
84
- class Orange < Fruit
85
- end
86
-
87
- class ForeignKeyDiffersOrderable
88
- include Mongoid::Document
89
- include Mongoid::Orderable
90
-
91
- belongs_to :different_scope, :class_name => "ForeignKeyDiffersOrderable",
92
- :foreign_key => "different_orderable_id"
93
-
94
- orderable :scope => :different_scope
95
- end
96
-
97
- class MultipleColumnsOrderable
98
- include Mongoid::Document
99
- include Mongoid::Orderable
100
-
101
- field :group_id
102
-
103
- belongs_to :scoped_group
104
-
105
- orderable :column => :pos, :base => 0, :index => false, :as => :position
106
- orderable :column => :serial_no, :default => true
107
- orderable :column => :groups, :scope => :group
108
- end
109
-
110
- class MultipleScopedOrderable
111
- include Mongoid::Document
112
- include Mongoid::Orderable
113
-
114
- belongs_to :apple
115
- belongs_to :orange
116
-
117
- orderable :column => :posa, :scope => :apple_id
118
- orderable :column => :poso, :scope => :orange_id
119
- end
120
-
121
- describe SimpleOrderable do
122
-
123
- before :each do
124
- SimpleOrderable.delete_all
125
- 5.times do
126
- SimpleOrderable.create!
127
- end
128
- end
129
-
130
- def positions
131
- SimpleOrderable.all.map(&:position).sort
132
- end
133
-
134
- it 'should have proper position column' do
135
- expect(SimpleOrderable.fields.key?('position')).to be true
136
- expect(SimpleOrderable.fields['position'].options[:type]).to eq(Integer)
137
- end
138
-
139
- it 'should have index on position column' do
140
- if ::Mongoid::Compatibility::Version.mongoid2?
141
- expect(SimpleOrderable.index_options[[[:position, 1]]]).not_to be_nil
142
- elsif ::Mongoid::Compatibility::Version.mongoid3?
143
- expect(SimpleOrderable.index_options[{:position => 1}]).not_to be_nil
144
- else
145
- expect(SimpleOrderable.index_specifications.detect { |spec| spec.key == {:position => 1} }).not_to be_nil
146
- end
147
- end
148
-
149
- it 'should have a orderable base of 1' do
150
- expect(SimpleOrderable.create!.orderable_base).to eq(1)
151
- end
152
-
153
- it 'should set proper position while creation' do
154
- expect(positions).to eq([1, 2, 3, 4, 5])
155
- end
156
-
157
- describe 'removement' do
158
-
159
- it 'top' do
160
- SimpleOrderable.where(:position => 1).destroy
161
- expect(positions).to eq([1, 2, 3, 4])
162
- end
163
-
164
- it 'bottom' do
165
- SimpleOrderable.where(:position => 5).destroy
166
- expect(positions).to eq([1, 2, 3, 4])
167
- end
168
-
169
- it 'middle' do
170
- SimpleOrderable.where(:position => 3).destroy
171
- expect(positions).to eq([1, 2, 3, 4])
172
- end
173
- end
174
-
175
- describe 'inserting' do
176
-
177
- it 'top' do
178
- newbie = SimpleOrderable.create! :move_to => :top
179
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
180
- expect(newbie.position).to eq(1)
181
- end
182
-
183
- it 'bottom' do
184
- newbie = SimpleOrderable.create! :move_to => :bottom
185
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
186
- expect(newbie.position).to eq(6)
187
- end
188
-
189
- it 'middle' do
190
- newbie = SimpleOrderable.create! :move_to => 4
191
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
192
- expect(newbie.position).to eq(4)
193
- end
194
-
195
- it 'middle (with a numeric string)' do
196
- newbie = SimpleOrderable.create! :move_to => '4'
197
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
198
- expect(newbie.position).to eq(4)
199
- end
200
-
201
- it 'middle (with a non-numeric string)' do
202
- expect do
203
- SimpleOrderable.create! :move_to => 'four'
204
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
205
- end
206
- end
207
-
208
- describe 'movement' do
209
-
210
- it 'higher from top' do
211
- record = SimpleOrderable.where(:position => 1).first
212
- record.update_attributes :move_to => :higher
213
- expect(positions).to eq([1, 2, 3, 4, 5])
214
- expect(record.reload.position).to eq(1)
215
- end
216
-
217
- it 'higher from bottom' do
218
- record = SimpleOrderable.where(:position => 5).first
219
- record.update_attributes :move_to => :higher
220
- expect(positions).to eq([1, 2, 3, 4, 5])
221
- expect(record.reload.position).to eq(4)
222
- end
223
-
224
- it 'higher from middle' do
225
- record = SimpleOrderable.where(:position => 3).first
226
- record.update_attributes :move_to => :higher
227
- expect(positions).to eq([1, 2, 3, 4, 5])
228
- expect(record.reload.position).to eq(2)
229
- end
230
-
231
- it 'lower from top' do
232
- record = SimpleOrderable.where(:position => 1).first
233
- record.update_attributes :move_to => :lower
234
- expect(positions).to eq([1, 2, 3, 4, 5])
235
- expect(record.reload.position).to eq(2)
236
- end
237
-
238
- it 'lower from bottom' do
239
- record = SimpleOrderable.where(:position => 5).first
240
- record.update_attributes :move_to => :lower
241
- expect(positions).to eq([1, 2, 3, 4, 5])
242
- expect(record.reload.position).to eq(5)
243
- end
244
-
245
- it 'lower from middle' do
246
- record = SimpleOrderable.where(:position => 3).first
247
- record.update_attributes :move_to => :lower
248
- expect(positions).to eq([1, 2, 3, 4, 5])
249
- expect(record.reload.position).to eq(4)
250
- end
251
-
252
- it 'does nothing if position not change' do
253
- record = SimpleOrderable.where(:position => 3).first
254
- record.save
255
- expect(positions).to eq([1, 2, 3, 4, 5])
256
- expect(record.reload.position).to eq(3)
257
- end
258
- end
259
-
260
- describe 'utiity methods' do
261
-
262
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
263
- record_1 = SimpleOrderable.where(:position => 1).first
264
- record_2 = SimpleOrderable.where(:position => 2).first
265
- record_3 = SimpleOrderable.where(:position => 3).first
266
- record_4 = SimpleOrderable.where(:position => 4).first
267
- record_5 = SimpleOrderable.where(:position => 5).first
268
- expect(record_1.next_items.to_a).to eq([record_2, record_3, record_4, record_5])
269
- expect(record_5.previous_items.to_a).to eq([record_1, record_2, record_3, record_4])
270
- expect(record_3.previous_items.to_a).to eq([record_1, record_2])
271
- expect(record_3.next_items.to_a).to eq([record_4, record_5])
272
- end
273
- end
274
- end
275
-
276
- describe ScopedOrderable do
277
-
278
- before :each do
279
- ScopedOrderable.delete_all
280
- 2.times do
281
- ScopedOrderable.create! :group_id => 1
282
- end
283
- 3.times do
284
- ScopedOrderable.create! :group_id => 2
285
- end
286
- end
287
-
288
- def positions
289
- ScopedOrderable.order_by([:group_id, :asc], [:position, :asc]).map(&:position)
290
- end
291
-
292
- it 'should set proper position while creation' do
293
- expect(positions).to eq([1, 2, 1, 2, 3])
294
- end
295
-
296
- describe 'removement' do
297
-
298
- it 'top' do
299
- ScopedOrderable.where(:position => 1, :group_id => 1).destroy
300
- expect(positions).to eq([1, 1, 2, 3])
301
- end
302
-
303
- it 'bottom' do
304
- ScopedOrderable.where(:position => 3, :group_id => 2).destroy
305
- expect(positions).to eq([1, 2, 1, 2])
306
- end
307
-
308
- it 'middle' do
309
- ScopedOrderable.where(:position => 2, :group_id => 2).destroy
310
- expect(positions).to eq([1, 2, 1, 2])
311
- end
312
- end
313
-
314
- describe 'inserting' do
315
-
316
- it 'top' do
317
- newbie = ScopedOrderable.create! :move_to => :top, :group_id => 1
318
- expect(positions).to eq([1, 2, 3, 1, 2, 3])
319
- expect(newbie.position).to eq(1)
320
- end
321
-
322
- it 'bottom' do
323
- newbie = ScopedOrderable.create! :move_to => :bottom, :group_id => 2
324
- expect(positions).to eq([1, 2, 1, 2, 3, 4])
325
- expect(newbie.position).to eq(4)
326
- end
327
-
328
- it 'middle' do
329
- newbie = ScopedOrderable.create! :move_to => 2, :group_id => 2
330
- expect(positions).to eq([1, 2, 1, 2, 3, 4])
331
- expect(newbie.position).to eq(2)
332
- end
333
-
334
- it 'middle (with a numeric string)' do
335
- newbie = ScopedOrderable.create! :move_to => '2', :group_id => 2
336
- expect(positions).to eq([1, 2, 1, 2, 3, 4])
337
- expect(newbie.position).to eq(2)
338
- end
339
-
340
- it 'middle (with a non-numeric string)' do
341
- expect do
342
- ScopedOrderable.create! :move_to => 'two', :group_id => 2
343
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
344
- end
345
- end
346
-
347
- describe 'index' do
348
-
349
- it 'is not on position alone' do
350
- if ::Mongoid::Compatibility::Version.mongoid2?
351
- expect(ScopedOrderable.index_options[[[:position, 1]]]).to be_nil
352
- elsif ::Mongoid::Compatibility::Version.mongoid3?
353
- expect(ScopedOrderable.index_options[{:position => 1}]).to be_nil
354
- else
355
- expect(ScopedOrderable.index_specifications.detect { |spec| spec.key == {:position => 1} }).to be_nil
356
- end
357
- end
358
-
359
- it 'is on compound fields' do
360
- if ::Mongoid::Compatibility::Version.mongoid2?
361
- expect(ScopedOrderable.index_options[[[:group_id, 1], [:position, 1]]]).to_not be_nil
362
- elsif ::Mongoid::Compatibility::Version.mongoid3?
363
- expect(ScopedOrderable.index_options[{:group_id => 1, :position => 1}]).to_not be_nil
364
- else
365
- expect(ScopedOrderable.index_specifications.detect { |spec| spec.key == {:group_id => 1, :position => 1} }).to_not be_nil
366
- end
367
- end
368
- end
369
-
370
- describe 'scope movement' do
371
-
372
- let(:record){ ScopedOrderable.where(:group_id => 2, :position => 2).first }
373
-
374
- it 'to a new scope group' do
375
- record.update_attributes :group_id => 3
376
- expect(positions).to eq([1, 2, 1, 2, 1])
377
- expect(record.position).to eq(1)
378
- end
379
-
380
- context 'when moving to an existing scope group' do
381
-
382
- it 'without a position' do
383
- record.update_attributes :group_id => 1
384
- expect(positions).to eq([1, 2, 3, 1, 2])
385
- expect(record.reload.position).to eq(3)
386
- end
387
-
388
- it 'with symbol position' do
389
- record.update_attributes :group_id => 1, :move_to => :top
390
- expect(positions).to eq([1, 2, 3, 1, 2])
391
- expect(record.reload.position).to eq(1)
392
- end
393
-
394
- it 'with point position' do
395
- record.update_attributes :group_id => 1, :move_to => 2
396
- expect(positions).to eq([1, 2, 3, 1, 2])
397
- expect(record.reload.position).to eq(2)
398
- end
399
-
400
- it 'with point position (with a numeric string)' do
401
- record.update_attributes :group_id => 1, :move_to => '2'
402
- expect(positions).to eq([1, 2, 3, 1, 2])
403
- expect(record.reload.position).to eq(2)
404
- end
405
-
406
- it 'with point position (with a non-numeric string)' do
407
- expect do
408
- record.update_attributes :group_id => 1, :move_to => 'two'
409
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
410
- end
411
- end
412
- end
413
-
414
- if defined?(Mongoid::IdentityMap)
415
-
416
- context 'when identity map is enabled' do
417
-
418
- let(:record){ ScopedOrderable.where(:group_id => 2, :position => 2).first }
419
-
420
- before do
421
- Mongoid.identity_map_enabled = true
422
- Mongoid::IdentityMap[ScopedOrderable.collection_name] = { record.id => record }
423
- end
424
-
425
- after do
426
- Mongoid.identity_map_enabled = false
427
- end
428
-
429
- it 'to a new scope group' do
430
- record.update_attributes :group_id => 3
431
- expect(positions).to eq([1, 2, 1, 2, 1])
432
- expect(record.position).to eq(1)
433
- end
434
-
435
- it 'to an existing scope group' do
436
- record.update_attributes :group_id => 1, :move_to => 2
437
- expect(positions).to eq([1, 2, 3, 1, 2])
438
- expect(record.reload.position).to eq(2)
439
- end
440
-
441
- it 'to an existing scope group (with a numeric string)' do
442
- record.update_attributes :group_id => 1, :move_to => '2'
443
- expect(positions).to eq([1, 2, 3, 1, 2])
444
- expect(record.reload.position).to eq(2)
445
- end
446
-
447
- it 'to an existing scope group (with a non-numeric string)' do
448
- expect do
449
- record.update_attributes :group_id => 1, :move_to => 'two'
450
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
451
- end
452
- end
453
- end
454
-
455
- describe 'utility methods' do
456
-
457
- before do
458
- ScopedOrderable.delete_all
459
- 5.times { ScopedOrderable.create! }
460
- end
461
-
462
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
463
- record_1 = ScopedOrderable.where(:position => 1).first
464
- record_2 = ScopedOrderable.where(:position => 2).first
465
- record_3 = ScopedOrderable.where(:position => 3).first
466
- record_4 = ScopedOrderable.where(:position => 4).first
467
- record_5 = ScopedOrderable.where(:position => 5).first
468
- expect(record_1.next_items.to_a).to eq([record_2, record_3, record_4, record_5])
469
- expect(record_5.previous_items.to_a).to eq([record_1, record_2, record_3, record_4])
470
- expect(record_3.previous_items.to_a).to eq([record_1, record_2])
471
- expect(record_3.next_items.to_a).to eq([record_4, record_5])
472
- # next_item & previous_item testing
473
- expect(record_1.next_item).to eq(record_2)
474
- expect(record_2.previous_item).to eq(record_1)
475
- expect(record_1.previous_item).to eq(nil)
476
- expect(record_5.next_item).to eq(nil)
477
- end
478
- end
479
- end
480
-
481
- describe StringScopedOrderable do
482
-
483
- it 'uses the foreign key of the relationship as scope' do
484
- orderable1 = StringScopedOrderable.create!(:some_scope => 1)
485
- orderable2 = StringScopedOrderable.create!(:some_scope => 1)
486
- orderable3 = StringScopedOrderable.create!(:some_scope => 2)
487
- expect(orderable1.position).to eq 1
488
- expect(orderable2.position).to eq 2
489
- expect(orderable3.position).to eq 1
490
- end
491
- end
492
-
493
- describe EmbeddedOrderable do
494
-
495
- before :each do
496
- EmbedsOrderable.delete_all
497
- eo = EmbedsOrderable.create!
498
- 2.times do
499
- eo.embedded_orderables.create!
500
- end
501
- eo = EmbedsOrderable.create!
502
- 3.times do
503
- eo.embedded_orderables.create!
504
- end
505
- end
506
-
507
- def positions
508
- EmbedsOrderable.order_by(:position => 1).all.map { |eo| eo.embedded_orderables.map(&:position).sort }
509
- end
510
-
511
- it 'sets proper position while creation' do
512
- expect(positions).to eq([[1, 2], [1, 2, 3]])
513
- end
514
-
515
- it 'moves an item returned by a query to position' do
516
- embedded_orderable_1 = EmbedsOrderable.first.embedded_orderables.where(:position => 1).first
517
- embedded_orderable_2 = EmbedsOrderable.first.embedded_orderables.where(:position => 2).first
518
- embedded_orderable_1.move_to! 2
519
- expect(embedded_orderable_2.reload.position).to eq(1)
520
- end
521
- end
522
-
523
- describe CustomizedOrderable do
524
-
525
- it 'does not have default position field' do
526
- expect(CustomizedOrderable.fields).not_to have_key('position')
527
- end
528
-
529
- it 'should have custom pos field' do
530
- expect(CustomizedOrderable.fields).to have_key('pos')
531
- end
532
-
533
- it 'should have an alias my_position which points to pos field on Mongoid 3+' do
534
- if CustomizedOrderable.respond_to?(:database_field_name)
535
- expect(CustomizedOrderable.database_field_name('my_position')).to eq('pos')
536
- end
537
- end
538
- end
539
-
540
- describe NoIndexOrderable do
541
-
542
- it 'should not have index on position column' do
543
- if ::Mongoid::Compatibility::Version.mongoid2? || ::Mongoid::Compatibility::Version.mongoid3?
544
- expect(NoIndexOrderable.index_options[[[:position, 1]]]).to be_nil
545
- else
546
- expect(NoIndexOrderable.index_specifications.detect { |spec| spec.key == :position }).to be_nil
547
- end
548
- end
549
- end
550
-
551
- describe ZeroBasedOrderable do
552
-
553
- before :each do
554
- ZeroBasedOrderable.delete_all
555
- 5.times do
556
- ZeroBasedOrderable.create!
557
- end
558
- end
559
-
560
- def positions
561
- ZeroBasedOrderable.all.map(&:position).sort
562
- end
563
-
564
- it 'should have a orderable base of 0' do
565
- expect(ZeroBasedOrderable.create!.orderable_base).to eq(0)
566
- end
567
-
568
- it 'should set proper position while creation' do
569
- expect(positions).to eq([0, 1, 2, 3, 4])
570
- end
571
-
572
- describe 'reset position' do
573
- before{ ZeroBasedOrderable.update_all({:position => nil}) }
574
- it 'should properly reset position' do
575
- ZeroBasedOrderable.all.map(&:save)
576
- expect(positions).to eq([0, 1, 2, 3, 4])
577
- end
578
- end
579
-
580
- describe 'removement' do
581
-
582
- it 'top' do
583
- ZeroBasedOrderable.where(:position => 0).destroy
584
- expect(positions).to eq([0, 1, 2, 3])
585
- end
586
-
587
- it 'bottom' do
588
- ZeroBasedOrderable.where(:position => 4).destroy
589
- expect(positions).to eq([0, 1, 2, 3])
590
- end
591
-
592
- it 'middle' do
593
- ZeroBasedOrderable.where(:position => 2).destroy
594
- expect(positions).to eq([0, 1, 2, 3])
595
- end
596
- end
597
-
598
- describe 'inserting' do
599
-
600
- it 'top' do
601
- newbie = ZeroBasedOrderable.create! :move_to => :top
602
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
603
- expect(newbie.position).to eq(0)
604
- end
605
-
606
- it 'bottom' do
607
- newbie = ZeroBasedOrderable.create! :move_to => :bottom
608
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
609
- expect(newbie.position).to eq(5)
610
- end
611
-
612
- it 'middle' do
613
- newbie = ZeroBasedOrderable.create! :move_to => 3
614
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
615
- expect(newbie.position).to eq(3)
616
- end
617
-
618
- it 'middle (with a numeric string)' do
619
- newbie = ZeroBasedOrderable.create! :move_to => '3'
620
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
621
- expect(newbie.position).to eq(3)
622
- end
623
-
624
- it 'middle (with a non-numeric string)' do
625
- expect do
626
- ZeroBasedOrderable.create! :move_to => 'three'
627
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
628
- end
629
- end
630
-
631
- describe 'movement' do
632
-
633
- it 'higher from top' do
634
- record = ZeroBasedOrderable.where(:position => 0).first
635
- record.update_attributes :move_to => :higher
636
- expect(positions).to eq([0, 1, 2, 3, 4])
637
- expect(record.reload.position).to eq(0)
638
- end
639
-
640
- it 'higher from bottom' do
641
- record = ZeroBasedOrderable.where(:position => 4).first
642
- record.update_attributes :move_to => :higher
643
- expect(positions).to eq([0, 1, 2, 3, 4])
644
- expect(record.reload.position).to eq(3)
645
- end
646
-
647
- it 'higher from middle' do
648
- record = ZeroBasedOrderable.where(:position => 3).first
649
- record.update_attributes :move_to => :higher
650
- expect(positions).to eq([0, 1, 2, 3, 4])
651
- expect(record.reload.position).to eq(2)
652
- end
653
-
654
- it 'lower from top' do
655
- record = ZeroBasedOrderable.where(:position => 0).first
656
- record.update_attributes :move_to => :lower
657
- expect(positions).to eq([0, 1, 2, 3, 4])
658
- expect(record.reload.position).to eq(1)
659
- end
660
-
661
- it 'lower from bottom' do
662
- record = ZeroBasedOrderable.where(:position => 4).first
663
- record.update_attributes :move_to => :lower
664
- expect(positions).to eq([0, 1, 2, 3, 4])
665
- expect(record.reload.position).to eq(4)
666
- end
667
-
668
- it 'lower from middle' do
669
- record = ZeroBasedOrderable.where(:position => 2).first
670
- record.update_attributes :move_to => :lower
671
- expect(positions).to eq([0, 1, 2, 3, 4])
672
- expect(record.reload.position).to eq(3)
673
- end
674
-
675
- it 'does nothing if position not change' do
676
- record = ZeroBasedOrderable.where(:position => 3).first
677
- record.save
678
- expect(positions).to eq([0, 1, 2, 3, 4])
679
- expect(record.reload.position).to eq(3)
680
- end
681
- end
682
-
683
- describe 'utiity methods' do
684
-
685
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
686
- record_1 = SimpleOrderable.where(:position => 1).first
687
- record_2 = SimpleOrderable.where(:position => 2).first
688
- record_3 = SimpleOrderable.where(:position => 3).first
689
- record_4 = SimpleOrderable.where(:position => 4).first
690
- record_5 = SimpleOrderable.where(:position => 5).first
691
- expect(record_1.next_items.to_a).to eq([record_2, record_3, record_4, record_5])
692
- expect(record_5.previous_items.to_a).to eq([record_1, record_2, record_3, record_4])
693
- expect(record_3.previous_items.to_a).to eq([record_1, record_2])
694
- expect(record_3.next_items.to_a).to eq([record_4, record_5])
695
- # next_item & previous_item testing
696
- expect(record_1.next_item).to eq(record_2)
697
- expect(record_2.previous_item).to eq(record_1)
698
- expect(record_1.previous_item).to eq(nil)
699
- expect(record_5.next_item).to eq(nil)
700
- end
701
- end
702
- end
703
-
704
- describe Fruit do
705
-
706
- it 'should set proper position' do
707
- fruit1 = Apple.create
708
- fruit2 = Orange.create
709
- expect(fruit1.position).to eq(1)
710
- expect(fruit2.position).to eq(2)
711
- end
712
-
713
- describe 'movement' do
714
- before :each do
715
- Fruit.delete_all
716
- 5.times do
717
- Apple.create!
718
- end
719
- end
720
-
721
- it 'with symbol position' do
722
- first_apple = Apple.asc(:_id).first
723
- top_pos = first_apple.position
724
- bottom_pos = Apple.asc(:_id).last.position
725
- expect do
726
- first_apple.move_to! :bottom
727
- end.to change(first_apple, :position).from(top_pos).to bottom_pos
728
- end
729
-
730
- it 'with point position' do
731
- first_apple = Apple.asc(:_id).first
732
- top_pos = first_apple.position
733
- bottom_pos = Apple.asc(:_id).last.position
734
- expect do
735
- first_apple.move_to! bottom_pos
736
- end.to change(first_apple, :position).from(top_pos).to bottom_pos
737
- end
738
- end
739
-
740
- describe 'add orderable configurations in inherited class' do
741
- it 'does not affect the orderable configurations of parent class and sibling class' do
742
- class Apple
743
- orderable :column => :serial
744
- end
745
- expect(Fruit.orderable_configurations).not_to eq Apple.orderable_configurations
746
- expect(Orange.orderable_configurations).not_to eq Apple.orderable_configurations
747
- expect(Fruit.orderable_configurations).to eq Orange.orderable_configurations
748
- end
749
- end
750
- end
751
-
752
- describe ForeignKeyDiffersOrderable do
753
-
754
- it 'uses the foreign key of the relationship as scope' do
755
- orderable1, orderable2, orderable3 = nil
756
- parent_scope1 = ForeignKeyDiffersOrderable.create
757
- parent_scope2 = ForeignKeyDiffersOrderable.create
758
- expect do
759
- orderable1 = ForeignKeyDiffersOrderable.create!(:different_scope => parent_scope1)
760
- orderable2 = ForeignKeyDiffersOrderable.create!(:different_scope => parent_scope1)
761
- orderable3 = ForeignKeyDiffersOrderable.create!(:different_scope => parent_scope2)
762
- end.to_not raise_error
763
- expect(orderable1.position).to eq 1
764
- expect(orderable2.position).to eq 2
765
- expect(orderable3.position).to eq 1
766
- end
767
- end
768
-
769
- describe MultipleColumnsOrderable do
770
-
771
- before :each do
772
- MultipleColumnsOrderable.delete_all
773
- 5.times do
774
- MultipleColumnsOrderable.create!
775
- end
776
- end
777
-
778
- context 'default orderable' do
779
- let(:serial_nos){ MultipleColumnsOrderable.all.map(&:serial_no).sort }
780
-
781
- describe 'inserting' do
782
- let(:newbie){ MultipleColumnsOrderable.create! }
783
-
784
- before { @position = newbie.position }
785
-
786
- it 'top' do
787
- newbie.move_to! :top
788
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
789
- expect(newbie.serial_no).to eq(1)
790
- expect(newbie.position).to eq(@position)
791
- end
792
-
793
- it 'bottom' do
794
- newbie.move_to! :bottom
795
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
796
- expect(newbie.serial_no).to eq(6)
797
- expect(newbie.position).to eq(@position)
798
- end
799
-
800
- it 'middle' do
801
- newbie.move_to! 4
802
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
803
- expect(newbie.serial_no).to eq(4)
804
- expect(newbie.position).to eq(@position)
805
- end
806
- end
807
-
808
- describe 'movement' do
809
- it 'higher from top' do
810
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
811
- position = record.position
812
- record.move_higher!
813
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
814
- expect(record.serial_no).to eq(1)
815
- expect(record.position).to eq(position)
816
- end
817
-
818
- it 'higher from bottom' do
819
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
820
- position = record.position
821
- record.move_higher!
822
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
823
- expect(record.serial_no).to eq(4)
824
- expect(record.position).to eq(position)
825
- end
826
-
827
- it 'higher from middle' do
828
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
829
- position = record.position
830
- record.move_higher!
831
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
832
- expect(record.serial_no).to eq(2)
833
- expect(record.position).to eq(position)
834
- end
835
-
836
- it 'lower from top' do
837
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
838
- position = record.position
839
- record.move_lower!
840
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
841
- expect(record.serial_no).to eq(2)
842
- expect(record.position).to eq(position)
843
- end
844
-
845
- it 'lower from bottom' do
846
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
847
- position = record.position
848
- record.move_lower!
849
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
850
- expect(record.serial_no).to eq(5)
851
- expect(record.position).to eq(position)
852
- end
853
-
854
- it 'lower from middle' do
855
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
856
- position = record.position
857
- record.move_lower!
858
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
859
- expect(record.serial_no).to eq(4)
860
- expect(record.position).to eq(position)
861
- end
862
- end
863
-
864
- describe 'utility methods' do
865
-
866
- before do
867
- @record_1 = MultipleColumnsOrderable.where(:serial_no => 1).first
868
- @record_2 = MultipleColumnsOrderable.where(:serial_no => 2).first
869
- @record_3 = MultipleColumnsOrderable.where(:serial_no => 3).first
870
- @record_4 = MultipleColumnsOrderable.where(:serial_no => 4).first
871
- @record_5 = MultipleColumnsOrderable.where(:serial_no => 5).first
872
- end
873
-
874
- it "should return the lower/higher item on the list for next_item/previous_item" do
875
- expect(@record_1.next_item).to eq(@record_2)
876
- expect(@record_3.next_item).to eq(@record_4)
877
- expect(@record_5.next_item).to eq(nil)
878
- expect(@record_1.prev_item).to eq(nil)
879
- expect(@record_3.prev_item).to eq(@record_2)
880
- expect(@record_5.prev_item).to eq(@record_4)
881
- end
882
-
883
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
884
- expect(@record_1.next_items.to_a).to eq([@record_2, @record_3, @record_4, @record_5])
885
- expect(@record_3.next_items.to_a).to eq([@record_4, @record_5])
886
- expect(@record_5.next_items.to_a).to eq([])
887
- expect(@record_1.previous_items.to_a).to eq([])
888
- expect(@record_3.previous_items.to_a).to eq([@record_1, @record_2])
889
- expect(@record_5.previous_items.to_a).to eq([@record_1, @record_2, @record_3, @record_4])
890
- end
891
- end
892
- end
893
-
894
- context 'serial_no orderable' do
895
-
896
- let(:serial_nos){ MultipleColumnsOrderable.all.map(&:serial_no).sort }
897
-
898
- it 'should have proper serial_no column' do
899
- expect(MultipleColumnsOrderable.fields.key?('serial_no')).to be true
900
- expect(MultipleColumnsOrderable.fields['serial_no'].options[:type]).to eq(Integer)
901
- end
902
-
903
- it 'should have index on serial_no column' do
904
- if ::Mongoid::Compatibility::Version.mongoid2?
905
- expect(MultipleColumnsOrderable.index_options[[[:serial_no, 1]]]).not_to be_nil
906
- elsif ::Mongoid::Compatibility::Version.mongoid3?
907
- expect(MultipleColumnsOrderable.index_options[{:serial_no => 1}]).not_to be_nil
908
- else
909
- expect(MultipleColumnsOrderable.index_specifications.detect { |spec| spec.key == {:serial_no => 1} }).not_to be_nil
910
- end
911
- end
912
-
913
- it 'should have a orderable base of 1' do
914
- expect(MultipleColumnsOrderable.first.orderable_base(:serial_no)).to eq(1)
915
- end
916
-
917
- it 'should set proper position while creation' do
918
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
919
- end
920
-
921
- describe 'removement' do
922
-
923
- it 'top' do
924
- MultipleColumnsOrderable.where(:serial_no => 1).destroy
925
- expect(serial_nos).to eq([1, 2, 3, 4])
926
- end
927
-
928
- it 'bottom' do
929
- MultipleColumnsOrderable.where(:serial_no => 5).destroy
930
- expect(serial_nos).to eq([1, 2, 3, 4])
931
- end
932
-
933
- it 'middle' do
934
- MultipleColumnsOrderable.where(:serial_no => 3).destroy
935
- expect(serial_nos).to eq([1, 2, 3, 4])
936
- end
937
- end
938
-
939
- describe 'inserting' do
940
- let(:newbie){ MultipleColumnsOrderable.create! }
941
-
942
- before { @position = newbie.position }
943
-
944
- it 'top' do
945
- newbie.move_serial_no_to! :top
946
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
947
- expect(newbie.serial_no).to eq(1)
948
- expect(newbie.position).to eq(@position)
949
- end
950
-
951
- it 'bottom' do
952
- newbie.move_serial_no_to! :bottom
953
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
954
- expect(newbie.serial_no).to eq(6)
955
- expect(newbie.position).to eq(@position)
956
- end
957
-
958
- it 'middle' do
959
- newbie.move_serial_no_to! 4
960
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
961
- expect(newbie.serial_no).to eq(4)
962
- expect(newbie.position).to eq(@position)
963
- end
964
- end
965
-
966
- describe 'movement' do
967
- it 'higher from top' do
968
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
969
- position = record.position
970
- record.move_serial_no_higher!
971
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
972
- expect(record.serial_no).to eq(1)
973
- expect(record.position).to eq(position)
974
- end
975
-
976
- it 'higher from bottom' do
977
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
978
- position = record.position
979
- record.move_serial_no_higher!
980
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
981
- expect(record.serial_no).to eq(4)
982
- expect(record.position).to eq(position)
983
- end
984
-
985
- it 'higher from middle' do
986
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
987
- position = record.position
988
- record.move_serial_no_higher!
989
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
990
- expect(record.serial_no).to eq(2)
991
- expect(record.position).to eq(position)
992
- end
993
-
994
- it 'lower from top' do
995
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
996
- position = record.position
997
- record.move_serial_no_lower!
998
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
999
- expect(record.serial_no).to eq(2)
1000
- expect(record.position).to eq(position)
1001
- end
1002
-
1003
- it 'lower from bottom' do
1004
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
1005
- position = record.position
1006
- record.move_serial_no_lower!
1007
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
1008
- expect(record.serial_no).to eq(5)
1009
- expect(record.position).to eq(position)
1010
- end
1011
-
1012
- it 'lower from middle' do
1013
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
1014
- position = record.position
1015
- record.move_serial_no_lower!
1016
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
1017
- expect(record.serial_no).to eq(4)
1018
- expect(record.position).to eq(position)
1019
- end
1020
- end
1021
-
1022
- describe 'utility methods' do
1023
-
1024
- before do
1025
- @record_1 = MultipleColumnsOrderable.where(:serial_no => 1).first
1026
- @record_2 = MultipleColumnsOrderable.where(:serial_no => 2).first
1027
- @record_3 = MultipleColumnsOrderable.where(:serial_no => 3).first
1028
- @record_4 = MultipleColumnsOrderable.where(:serial_no => 4).first
1029
- @record_5 = MultipleColumnsOrderable.where(:serial_no => 5).first
1030
- end
1031
-
1032
- it "should return the lower/higher item on the list for next_item/previous_item" do
1033
- expect(@record_1.next_serial_no_item).to eq(@record_2)
1034
- expect(@record_3.next_serial_no_item).to eq(@record_4)
1035
- expect(@record_5.next_serial_no_item).to eq(nil)
1036
- expect(@record_1.prev_serial_no_item).to eq(nil)
1037
- expect(@record_3.prev_serial_no_item).to eq(@record_2)
1038
- expect(@record_5.prev_serial_no_item).to eq(@record_4)
1039
- end
1040
-
1041
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
1042
- expect(@record_1.next_serial_no_items.to_a).to eq([@record_2, @record_3, @record_4, @record_5])
1043
- expect(@record_3.next_serial_no_items.to_a).to eq([@record_4, @record_5])
1044
- expect(@record_5.next_serial_no_items.to_a).to eq([])
1045
- expect(@record_1.previous_serial_no_items.to_a).to eq([])
1046
- expect(@record_3.previous_serial_no_items.to_a).to eq([@record_1, @record_2])
1047
- expect(@record_5.previous_serial_no_items.to_a).to eq([@record_1, @record_2, @record_3, @record_4])
1048
- end
1049
- end
1050
-
1051
- end
1052
-
1053
- context 'position orderable' do
1054
-
1055
- let(:positions){ MultipleColumnsOrderable.all.map(&:position).sort }
1056
-
1057
- it 'should not have default position field' do
1058
- expect(MultipleColumnsOrderable.fields).not_to have_key('position')
1059
- end
1060
-
1061
- it 'should have custom pos field' do
1062
- expect(MultipleColumnsOrderable.fields).to have_key('pos')
1063
- expect(MultipleColumnsOrderable.fields['pos'].options[:type]).to eq(Integer)
1064
- end
1065
-
1066
- it 'should have index on position column' do
1067
- if ::Mongoid::Compatibility::Version.mongoid2?
1068
- expect(MultipleColumnsOrderable.index_options[[[:position, 1]]]).to be_nil
1069
- elsif ::Mongoid::Compatibility::Version.mongoid3?
1070
- expect(MultipleColumnsOrderable.index_options[{:position => 1}]).to be_nil
1071
- else
1072
- expect(MultipleColumnsOrderable.index_specifications.detect { |spec| spec.key == {:position => 1} }).to be_nil
1073
- end
1074
- end
1075
-
1076
- it 'should have a orderable base of 0' do
1077
- expect(MultipleColumnsOrderable.first.orderable_base(:position)).to eq(0)
1078
- end
1079
-
1080
- it 'should set proper position while creation' do
1081
- expect(positions).to eq([0, 1, 2, 3, 4])
1082
- end
1083
-
1084
- describe 'removement' do
1085
-
1086
- it 'top' do
1087
- MultipleColumnsOrderable.where(:pos => 1).destroy
1088
- expect(positions).to eq([0, 1, 2, 3])
1089
- end
1090
-
1091
- it 'bottom' do
1092
- MultipleColumnsOrderable.where(:pos => 4).destroy
1093
- expect(positions).to eq([0, 1, 2, 3])
1094
- end
1095
-
1096
- it 'middle' do
1097
- MultipleColumnsOrderable.where(:pos => 3).destroy
1098
- expect(positions).to eq([0, 1, 2, 3])
1099
- end
1100
- end
1101
-
1102
- describe 'inserting' do
1103
- let(:newbie){ MultipleColumnsOrderable.create! }
1104
-
1105
- before { @serial_no = newbie.serial_no }
1106
-
1107
- it 'top' do
1108
- newbie.move_position_to! :top
1109
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
1110
- expect(newbie.position).to eq(0)
1111
- expect(newbie.serial_no).to eq(@serial_no)
1112
- end
1113
-
1114
- it 'bottom' do
1115
- newbie.move_position_to! :bottom
1116
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
1117
- expect(newbie.position).to eq(5)
1118
- expect(newbie.serial_no).to eq(@serial_no)
1119
- end
1120
-
1121
- it 'middle' do
1122
- newbie.move_position_to! 4
1123
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
1124
- expect(newbie.position).to eq(4)
1125
- expect(newbie.serial_no).to eq(@serial_no)
1126
- end
1127
- end
1128
-
1129
- describe 'movement' do
1130
- it 'higher from top' do
1131
- record = MultipleColumnsOrderable.where(:pos => 0).first
1132
- position = record.serial_no
1133
- record.move_position_higher!
1134
- expect(positions).to eq([0, 1, 2, 3, 4])
1135
- expect(record.position).to eq(0)
1136
- expect(record.serial_no).to eq(position)
1137
- end
1138
-
1139
- it 'higher from bottom' do
1140
- record = MultipleColumnsOrderable.where(:pos => 4).first
1141
- position = record.serial_no
1142
- record.move_position_higher!
1143
- expect(positions).to eq([0, 1, 2, 3, 4])
1144
- expect(record.position).to eq(3)
1145
- expect(record.serial_no).to eq(position)
1146
- end
1147
-
1148
- it 'higher from middle' do
1149
- record = MultipleColumnsOrderable.where(:pos => 3).first
1150
- position = record.serial_no
1151
- record.move_position_higher!
1152
- expect(positions).to eq([0, 1, 2, 3, 4])
1153
- expect(record.position).to eq(2)
1154
- expect(record.serial_no).to eq(position)
1155
- end
1156
-
1157
- it 'lower from top' do
1158
- record = MultipleColumnsOrderable.where(:pos => 0).first
1159
- position = record.serial_no
1160
- record.move_position_lower!
1161
- expect(positions).to eq([0, 1, 2, 3, 4])
1162
- expect(record.position).to eq(1)
1163
- expect(record.serial_no).to eq(position)
1164
- end
1165
-
1166
- it 'lower from bottom' do
1167
- record = MultipleColumnsOrderable.where(:pos => 4).first
1168
- position = record.serial_no
1169
- record.move_position_lower!
1170
- expect(positions).to eq([0, 1, 2, 3, 4])
1171
- expect(record.position).to eq(4)
1172
- expect(record.serial_no).to eq(position)
1173
- end
1174
-
1175
- it 'lower from middle' do
1176
- record = MultipleColumnsOrderable.where(:pos => 3).first
1177
- position = record.serial_no
1178
- record.move_position_lower!
1179
- expect(positions).to eq([0, 1, 2, 3, 4])
1180
- expect(record.position).to eq(4)
1181
- expect(record.serial_no).to eq(position)
1182
- end
1183
- end
1184
-
1185
- describe 'utility methods' do
1186
-
1187
- before do
1188
- @record_1 = MultipleColumnsOrderable.where(:pos => 0).first
1189
- @record_2 = MultipleColumnsOrderable.where(:pos => 1).first
1190
- @record_3 = MultipleColumnsOrderable.where(:pos => 2).first
1191
- @record_4 = MultipleColumnsOrderable.where(:pos => 3).first
1192
- @record_5 = MultipleColumnsOrderable.where(:pos => 4).first
1193
- end
1194
-
1195
- it "should return the lower/higher item on the list for next_item/previous_item" do
1196
- expect(@record_1.next_position_item).to eq(@record_2)
1197
- expect(@record_3.next_position_item).to eq(@record_4)
1198
- expect(@record_5.next_position_item).to eq(nil)
1199
- expect(@record_1.prev_position_item).to eq(nil)
1200
- expect(@record_3.prev_position_item).to eq(@record_2)
1201
- expect(@record_5.prev_position_item).to eq(@record_4)
1202
- end
1203
-
1204
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
1205
- expect(@record_1.next_position_items.to_a).to eq([@record_2, @record_3, @record_4, @record_5])
1206
- expect(@record_3.next_position_items.to_a).to eq([@record_4, @record_5])
1207
- expect(@record_5.next_position_items.to_a).to eq([])
1208
- expect(@record_1.previous_position_items.to_a).to eq([])
1209
- expect(@record_3.previous_position_items.to_a).to eq([@record_1, @record_2])
1210
- expect(@record_5.previous_position_items.to_a).to eq([@record_1, @record_2, @record_3, @record_4])
1211
- end
1212
- end
1213
-
1214
- end
1215
-
1216
- context 'group_count orderable' do
1217
- before :each do
1218
- MultipleColumnsOrderable.delete_all
1219
- 2.times { MultipleColumnsOrderable.create! :group_id => 1 }
1220
- 3.times { MultipleColumnsOrderable.create! :group_id => 2 }
1221
- end
1222
-
1223
- let(:all_groups){ MultipleColumnsOrderable.order_by([:group_id, :asc], [:groups, :asc]).map(&:groups) }
1224
-
1225
- it 'should set proper position while creation' do
1226
- expect(all_groups).to eq([1, 2, 1, 2, 3])
1227
- end
1228
-
1229
- describe 'removement' do
1230
-
1231
- it 'top' do
1232
- MultipleColumnsOrderable.where(:groups => 1, :group_id => 1).destroy
1233
- expect(all_groups).to eq([1, 1, 2, 3])
1234
- end
1235
-
1236
- it 'bottom' do
1237
- MultipleColumnsOrderable.where(:groups => 3, :group_id => 2).destroy
1238
- expect(all_groups).to eq([1, 2, 1, 2])
1239
- end
1240
-
1241
- it 'middle' do
1242
- MultipleColumnsOrderable.where(:groups => 2, :group_id => 2).destroy
1243
- expect(all_groups).to eq([1, 2, 1, 2])
1244
- end
1245
- end
1246
-
1247
- describe 'inserting' do
1248
-
1249
- it 'top' do
1250
- newbie = MultipleColumnsOrderable.create! :group_id => 1
1251
- newbie.move_groups_to! :top
1252
- expect(all_groups).to eq([1, 2, 3, 1, 2, 3])
1253
- expect(newbie.groups).to eq(1)
1254
- end
1255
-
1256
- it 'bottom' do
1257
- newbie = MultipleColumnsOrderable.create! :group_id => 2
1258
- newbie.move_groups_to! :bottom
1259
- expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
1260
- expect(newbie.groups).to eq(4)
1261
- end
1262
-
1263
- it 'middle' do
1264
- newbie = MultipleColumnsOrderable.create! :group_id => 2
1265
- newbie.move_groups_to! 2
1266
- expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
1267
- expect(newbie.groups).to eq(2)
1268
- end
1269
- end
1270
-
1271
- describe 'scope movement' do
1272
-
1273
- let(:record){ MultipleColumnsOrderable.where(:group_id => 2, :groups => 2).first }
1274
-
1275
- it 'to a new scope group' do
1276
- record.update_attributes :group_id => 3
1277
- expect(all_groups).to eq([1, 2, 1, 2, 1])
1278
- expect(record.groups).to eq(1)
1279
- end
1280
-
1281
- context 'when moving to an existing scope group' do
1282
-
1283
- it 'without a position' do
1284
- record.update_attributes :group_id => 1
1285
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1286
- expect(record.reload.groups).to eq(3)
1287
- end
1288
-
1289
- it 'with symbol position' do
1290
- record.update_attributes :group_id => 1
1291
- record.move_groups_to! :top
1292
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1293
- expect(record.reload.groups).to eq(1)
1294
- end
1295
-
1296
- it 'with point position' do
1297
- record.update_attributes :group_id => 1
1298
- record.move_groups_to! 2
1299
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1300
- expect(record.reload.groups).to eq(2)
1301
- end
1302
- end
1303
- end
1304
-
1305
- if defined?(Mongoid::IdentityMap)
1306
-
1307
- context 'when identity map is enabled' do
1308
-
1309
- let(:record){ MultipleColumnsOrderable.where(:group_id => 2, :groups => 2).first }
1310
-
1311
- before do
1312
- Mongoid.identity_map_enabled = true
1313
- Mongoid::IdentityMap[MultipleColumnsOrderable.collection_name] = { record.id => record }
1314
- end
1315
-
1316
- after { Mongoid.identity_map_enabled = false }
1317
-
1318
- it 'to a new scope group' do
1319
- record.update_attributes :group_id => 3
1320
- expect(all_groups).to eq([1, 2, 1, 2, 1])
1321
- expect(record.groups).to eq(1)
1322
- end
1323
-
1324
- it 'to an existing scope group' do
1325
- record.update_attributes :group_id => 1
1326
- record.move_groups_to! 2
1327
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1328
- expect(record.groups).to eq(2)
1329
- end
1330
- end
1331
- end
1332
-
1333
- describe 'utility methods' do
1334
-
1335
- before do
1336
- @record_1 = MultipleColumnsOrderable.where(:group_id => 2, :groups => 1).first
1337
- @record_2 = MultipleColumnsOrderable.where(:group_id => 2, :groups => 2).first
1338
- @record_3 = MultipleColumnsOrderable.where(:group_id => 2, :groups => 3).first
1339
- @record_4 = MultipleColumnsOrderable.where(:group_id => 1, :groups => 1).first
1340
- @record_5 = MultipleColumnsOrderable.where(:group_id => 1, :groups => 2).first
1341
- end
1342
-
1343
- it "should return the lower/higher item on the list for next_item/previous_item" do
1344
- expect(@record_1.next_groups_item).to eq(@record_2)
1345
- expect(@record_4.next_groups_item).to eq(@record_5)
1346
- expect(@record_3.next_groups_item).to eq(nil)
1347
- expect(@record_1.prev_groups_item).to eq(nil)
1348
- expect(@record_3.prev_groups_item).to eq(@record_2)
1349
- expect(@record_5.prev_groups_item).to eq(@record_4)
1350
- end
1351
-
1352
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
1353
- expect(@record_1.next_groups_items.to_a).to eq([@record_2, @record_3])
1354
- expect(@record_3.next_groups_items.to_a).to eq([])
1355
- expect(@record_4.next_groups_items.to_a).to eq([@record_5])
1356
- expect(@record_1.previous_groups_items.to_a).to eq([])
1357
- expect(@record_3.previous_groups_items.to_a).to eq([@record_1, @record_2])
1358
- expect(@record_5.previous_groups_items.to_a).to eq([@record_4])
1359
- end
1360
- end
1361
- end
1362
- end
1363
-
1364
- describe MultipleScopedOrderable do
1365
- before :each do
1366
- Apple.delete_all; Orange.delete_all;
1367
- MultipleScopedOrderable.delete_all
1368
-
1369
- 3.times do
1370
- Apple.create; Orange.create
1371
- end
1372
-
1373
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 1
1374
- MultipleScopedOrderable.create! :apple_id => 2, :orange_id => 1
1375
- MultipleScopedOrderable.create! :apple_id => 2, :orange_id => 2
1376
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 3
1377
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 1
1378
- MultipleScopedOrderable.create! :apple_id => 3, :orange_id => 3
1379
- MultipleScopedOrderable.create! :apple_id => 2, :orange_id => 3
1380
- MultipleScopedOrderable.create! :apple_id => 3, :orange_id => 2
1381
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 3
1382
- end
1383
-
1384
- def apple_positions
1385
- MultipleScopedOrderable.order_by([:apple_id, :asc], [:posa, :asc]).map(&:posa)
1386
- end
1387
-
1388
- def orange_positions
1389
- MultipleScopedOrderable.order_by([:orange_id, :asc], [:poso, :asc]).map(&:poso)
1390
- end
1391
-
1392
- describe 'default positions' do
1393
- it { expect(apple_positions).to eq([1, 2, 3, 4, 1, 2, 3, 1, 2]) }
1394
- it { expect(orange_positions).to eq([1, 2, 3, 1, 2, 1, 2, 3, 4]) }
1395
- end
1396
-
1397
- describe 'change the scope of the apple' do
1398
- let(:record) { MultipleScopedOrderable.first }
1399
- before do
1400
- record.update_attribute(:apple_id, 2)
1401
- end
1402
-
1403
- it 'should properly set the apple positions' do
1404
- expect(apple_positions).to eq([1, 2, 3, 1, 2, 3, 4, 1, 2])
1405
- end
1406
-
1407
- it 'should not affect the orange positions' do
1408
- expect(orange_positions).to eq([1, 2, 3, 1, 2, 1, 2, 3, 4])
1409
- end
1410
- end
1411
- end
1412
-
1413
- end