mongoid_acts_as_list 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.vimrc.local +2 -0
- data/Gemfile +3 -0
- data/README.markdown +134 -0
- data/Rakefile +1 -0
- data/acts_as_list.gemspec +27 -0
- data/lib/mongoid/acts_as_list/configuration.rb +12 -0
- data/lib/mongoid/acts_as_list/list/embedded.rb +47 -0
- data/lib/mongoid/acts_as_list/list/root.rb +38 -0
- data/lib/mongoid/acts_as_list/list.rb +295 -0
- data/lib/mongoid/acts_as_list/version.rb +5 -0
- data/lib/mongoid_acts_as_list.rb +21 -0
- data/spec/acts_as_list/embeds_many_spec.rb +47 -0
- data/spec/acts_as_list/relational_spec.rb +62 -0
- data/spec/fixtures/embeds_many_models.rb +15 -0
- data/spec/fixtures/relational_models.rb +13 -0
- data/spec/mongoid_acts_as_list_spec.rb +7 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/list_examples.rb +523 -0
- metadata +121 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.vimrc.local
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
ActsAsList for Mongoid
|
2
|
+
=====================
|
3
|
+
|
4
|
+
## Description
|
5
|
+
|
6
|
+
Mongoid::ActsAsList provides the ability of ordering and sorting a number of objects in a list using Mongoid as an ODM.
|
7
|
+
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
Place the following in your Gemfile:
|
12
|
+
|
13
|
+
``` ruby
|
14
|
+
gem 'mongoid_acts_as_list', '~> 0.0.3'
|
15
|
+
```
|
16
|
+
|
17
|
+
Then run `bundle install`
|
18
|
+
|
19
|
+
|
20
|
+
## Configuration
|
21
|
+
|
22
|
+
Configure defaults values used by ActsAsList:
|
23
|
+
|
24
|
+
|
25
|
+
``` ruby
|
26
|
+
Mongoid::ActsAsList.configure do |config|
|
27
|
+
|
28
|
+
# These are the default values. Modify as you see fit:
|
29
|
+
|
30
|
+
config.default_position_field = :position
|
31
|
+
config.start_list_at = 0
|
32
|
+
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
Make sure it is loaded before calling ` acts_as_list `. You can place this code in an initializer file for example.
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
Activate ActsAsList in your models.
|
41
|
+
|
42
|
+
In has_many/belongs_to associations, you will need to provide a `:scope` option:
|
43
|
+
|
44
|
+
``` ruby
|
45
|
+
class List
|
46
|
+
include Mongoid::Document
|
47
|
+
|
48
|
+
has_many :items
|
49
|
+
end
|
50
|
+
|
51
|
+
class Item
|
52
|
+
include Mongoid::Document
|
53
|
+
include Mongoid::ActsAsList
|
54
|
+
|
55
|
+
belongs_to :list
|
56
|
+
acts_as_list scope: :list
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
On embedded document, the scope option is not necessary:
|
61
|
+
|
62
|
+
``` ruby
|
63
|
+
class List
|
64
|
+
include Mongoid::Document
|
65
|
+
|
66
|
+
embeds_many :items
|
67
|
+
end
|
68
|
+
|
69
|
+
class Item
|
70
|
+
include Mongoid::Document
|
71
|
+
include Mongoid::ActsAsList
|
72
|
+
|
73
|
+
embedded_in :list
|
74
|
+
acts_as_list
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
## Class Methods
|
81
|
+
|
82
|
+
list.items.order_by_position #=> returns all items in `list` ordered by position
|
83
|
+
|
84
|
+
## Instance Methods
|
85
|
+
|
86
|
+
item.move to: 2 #=> moves item to the 2nd position
|
87
|
+
item.move to: :start #=> moves item to the first position in the list
|
88
|
+
item.move to: :end #=> moves item to the last position in the list
|
89
|
+
item.move before: other_item #=> moves item before other_item
|
90
|
+
item.move after: other_item #=> moves item after other_item
|
91
|
+
item.move forward: 3 #=> move item 3 positions closer to the end of the list
|
92
|
+
item.move backward: 2 #=> move item 2 positions closer to the start of the list
|
93
|
+
item.move :forward #=> same as item.move(forward: 1)
|
94
|
+
item.move :backward #=> same as item.move(backward: 1)
|
95
|
+
|
96
|
+
item.in_list? #=> true
|
97
|
+
item.remove_from_list #=> sets the position to nil and reorders other items
|
98
|
+
item.not_in_list? #=> true
|
99
|
+
|
100
|
+
item.first?
|
101
|
+
item.last?
|
102
|
+
|
103
|
+
item.next_item #=> returns the item immediately following `item` in the list
|
104
|
+
item.previous_item #=> returns the item immediately preceding `item` in the list
|
105
|
+
```
|
106
|
+
|
107
|
+
Other methods are available, as well as all the methods from the original ActiveRecord ActsAsList gem.
|
108
|
+
Check the source and documentation to find out more!
|
109
|
+
|
110
|
+
|
111
|
+
## Requirements
|
112
|
+
|
113
|
+
Tested with Mongoid 2.4.6 on Ruby 1.9.3-p125, Rails 3.2.2, and Mongo 2.x
|
114
|
+
|
115
|
+
|
116
|
+
## Roadmap
|
117
|
+
|
118
|
+
* Test with several layers of embedding documents
|
119
|
+
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
- Fork the project
|
124
|
+
- Start a feature/bugfix branch
|
125
|
+
- Start writing tests
|
126
|
+
- Commit and push until all tests are green and you are happy with your contribution
|
127
|
+
|
128
|
+
- If you're using Vim, source the .vimrc.local file. It provides 2 shortcuts for running the specs:
|
129
|
+
- `<Leader>r` runs the current spec file
|
130
|
+
- `<Leader>R` runs all spec files
|
131
|
+
|
132
|
+
## Copyright
|
133
|
+
|
134
|
+
Copyright (c) 2012 Olivier Melcher, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mongoid/acts_as_list/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mongoid_acts_as_list"
|
7
|
+
s.version = Mongoid::ActsAsList::VERSION
|
8
|
+
s.authors = ["Olivier Melcher"]
|
9
|
+
s.email = ["olivier.melcher@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Implementation of the acts as list gem for Mongoid}
|
12
|
+
s.description = %q{}
|
13
|
+
|
14
|
+
s.rubyforge_project = "acts_as_list"
|
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 "rspec"
|
23
|
+
s.add_development_dependency "bson_ext", "~> 1.5"
|
24
|
+
s.add_development_dependency "database_cleaner"
|
25
|
+
s.add_development_dependency "pry"
|
26
|
+
s.add_runtime_dependency "mongoid", [">= 2.0.1"]
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Mongoid::ActsAsList
|
2
|
+
module List::Embedded
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
raise List::ScopeMissingError, "Mongoid::ActsAsList::Embedded can only be included in embedded documents" unless embedded?
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
private
|
11
|
+
|
12
|
+
def define_position_scope(scope_name)
|
13
|
+
define_method(:scope_condition) { {position_field.ne => nil} }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
## InstanceMethods
|
18
|
+
private
|
19
|
+
|
20
|
+
def shift_position options = {}
|
21
|
+
criteria = options.fetch(:for, to_criteria)
|
22
|
+
by_how_much = options.fetch(:by, 1)
|
23
|
+
|
24
|
+
criteria = criteria.to_criteria if criteria.is_a? self.class
|
25
|
+
|
26
|
+
criteria.each do |doc|
|
27
|
+
doc.inc(position_field, by_how_much)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_criteria
|
32
|
+
embedded_collection.where(_id: _id)
|
33
|
+
end
|
34
|
+
|
35
|
+
def items_in_list
|
36
|
+
embedded_collection.where(scope_condition)
|
37
|
+
end
|
38
|
+
|
39
|
+
def root_collection
|
40
|
+
_parent.db.collection(_parent.collection.name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def embedded_collection
|
44
|
+
_parent.send(metadata.name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Mongoid::ActsAsList
|
2
|
+
module List
|
3
|
+
module Root
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
private
|
8
|
+
|
9
|
+
def define_position_scope(scope_name)
|
10
|
+
raise List::ScopeMissingError, "#acts_as_list requires a scope option" if scope_name.blank?
|
11
|
+
|
12
|
+
scope_name = "#{scope_name}_id".intern if scope_name.to_s !~ /_id$/
|
13
|
+
define_method(:scope_condition) { {scope_name => self[scope_name]} }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
## InstanceMethods
|
18
|
+
private
|
19
|
+
|
20
|
+
def shift_position options = {}
|
21
|
+
criteria = options.fetch(:for, to_criteria)
|
22
|
+
by_how_much = options.fetch(:by, 1)
|
23
|
+
|
24
|
+
criteria = criteria.to_criteria if criteria.is_a? self.class
|
25
|
+
|
26
|
+
db.collection(collection.name).update(criteria.selector, {"$inc" => { position_field => by_how_much }}, {multi: true})
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_criteria
|
30
|
+
self.class.where(_id: _id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def items_in_list
|
34
|
+
self.class.where(scope_condition).and(position_field.ne => nil)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
module Mongoid::ActsAsList
|
2
|
+
module List
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
autoload :Root , 'mongoid/acts_as_list/list/root.rb'
|
6
|
+
autoload :Embedded , 'mongoid/acts_as_list/list/embedded.rb'
|
7
|
+
|
8
|
+
class ScopeMissingError < RuntimeError; end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def acts_as_list options = {}
|
12
|
+
field = options.fetch(:field, Mongoid::ActsAsList.configuration.default_position_field).try(:to_sym)
|
13
|
+
scope = options.fetch(:scope, nil).try(:to_sym)
|
14
|
+
|
15
|
+
include list_submodule
|
16
|
+
define_position_field field
|
17
|
+
define_position_scope scope
|
18
|
+
end
|
19
|
+
|
20
|
+
def order_by_position(conditions = {}, order = :asc)
|
21
|
+
order, conditions = [conditions || :asc, {}] unless conditions.is_a? Hash
|
22
|
+
where( conditions ).order_by [[position_field, order], [:created_at, order]]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def list_submodule
|
28
|
+
embedded? ? Embedded : Root
|
29
|
+
end
|
30
|
+
|
31
|
+
def define_position_field(field_name)
|
32
|
+
field field_name, type: Integer
|
33
|
+
|
34
|
+
set_callback :validation, :before, if: -> { new? && not_in_list? } do |doc|
|
35
|
+
doc[field_name] = doc.send(:next_available_position_in_list)
|
36
|
+
end
|
37
|
+
|
38
|
+
set_callback :destroy, :after, :shift_later_items_towards_start_of_list, if: -> { in_list? }
|
39
|
+
|
40
|
+
[:define_method, :define_singleton_method].each do |define_method|
|
41
|
+
send(define_method, :position_field) { field_name }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
## InstanceMethods
|
47
|
+
|
48
|
+
# Public: Moves the item to new position in the list
|
49
|
+
#
|
50
|
+
# where - a Hash specifying where to move the item
|
51
|
+
# :to - an Integer representing a position number
|
52
|
+
# or a Symbol from the list :start, :top, :end, :bottom
|
53
|
+
# :before, :above - another object in the list
|
54
|
+
# :after: , :below - another object in the list
|
55
|
+
# :forward, :lower - an Integer specify by how much to move the item forward.
|
56
|
+
# will stop moving the item when it reaches the end of the list
|
57
|
+
# :backward, :higher - an Integer specify by how much to move the item forward.
|
58
|
+
# will stop moving the item when it reaches the end of the list
|
59
|
+
#
|
60
|
+
# or a Symbol in :forward, :lower, :backward, :higher
|
61
|
+
#
|
62
|
+
# Examples
|
63
|
+
#
|
64
|
+
# item.move to: 3
|
65
|
+
# #=> moves item to the 3rd position
|
66
|
+
#
|
67
|
+
# item.move to: :start
|
68
|
+
# #=> moves item to the first position in the list
|
69
|
+
#
|
70
|
+
# other_item.position #=> 3
|
71
|
+
#
|
72
|
+
# item.move before: other_item
|
73
|
+
# #=> moves item to position 3 and other_item to position 4
|
74
|
+
#
|
75
|
+
# item.move after: other_item
|
76
|
+
# #=> moves item to position 4
|
77
|
+
#
|
78
|
+
# item.move backward: 3
|
79
|
+
# #=> move item 3 positions closer to the start of the list
|
80
|
+
#
|
81
|
+
# item.move :forward
|
82
|
+
# #=> same as item.move(forward: 1)
|
83
|
+
#
|
84
|
+
# Returns nothing
|
85
|
+
def move(where = {})
|
86
|
+
if where.is_a? Hash
|
87
|
+
options = [:to, :before, :above, :after, :below, :forward, :forwards, :lower, :backward, :backwards, :higher]
|
88
|
+
|
89
|
+
prefix, destination = where.each.select { |k, _| options.include? k }.first
|
90
|
+
raise ArgumentError, "#move requires one of the following options: #{options.join(', ')}" unless prefix
|
91
|
+
|
92
|
+
send("move_#{prefix}", destination)
|
93
|
+
else
|
94
|
+
destination = where
|
95
|
+
|
96
|
+
send("move_#{destination}")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def move_to(destination)
|
101
|
+
if destination.is_a? Symbol
|
102
|
+
send("move_to_#{destination}")
|
103
|
+
else
|
104
|
+
destination = position_within_list_boundaries(destination)
|
105
|
+
insert_at destination
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def move_to_end
|
110
|
+
new_position = in_list? ? last_position_in_list : next_available_position_in_list
|
111
|
+
insert_at new_position
|
112
|
+
end
|
113
|
+
alias_method :move_to_bottom, :move_to_end
|
114
|
+
|
115
|
+
def move_to_start
|
116
|
+
insert_at start_position_in_list
|
117
|
+
end
|
118
|
+
alias_method :move_to_top, :move_to_start
|
119
|
+
|
120
|
+
def move_forwards by_how_much = 1
|
121
|
+
move_to(self[position_field] + by_how_much) unless last?
|
122
|
+
end
|
123
|
+
alias_method :move_lower , :move_forwards
|
124
|
+
alias_method :move_forward, :move_forwards
|
125
|
+
|
126
|
+
def move_backwards by_how_much = 1
|
127
|
+
move_to(self[position_field] - by_how_much) unless first?
|
128
|
+
end
|
129
|
+
alias_method :move_higher , :move_backwards
|
130
|
+
alias_method :move_forward, :move_forwards
|
131
|
+
|
132
|
+
def move_before(other_item)
|
133
|
+
destination = other_item[position_field]
|
134
|
+
origin = self[position_field]
|
135
|
+
|
136
|
+
if origin > destination
|
137
|
+
insert_at destination
|
138
|
+
else
|
139
|
+
insert_at destination - 1
|
140
|
+
end
|
141
|
+
end
|
142
|
+
alias_method :move_above, :move_before
|
143
|
+
|
144
|
+
def move_after(other_item)
|
145
|
+
destination = other_item[position_field]
|
146
|
+
origin = self[position_field]
|
147
|
+
|
148
|
+
if origin > destination
|
149
|
+
insert_at destination + 1
|
150
|
+
else
|
151
|
+
insert_at destination
|
152
|
+
end
|
153
|
+
end
|
154
|
+
alias_method :move_below, :move_after
|
155
|
+
|
156
|
+
# Public: Removes the item from the list
|
157
|
+
#
|
158
|
+
# Returns true if the item was removed, false if not
|
159
|
+
def remove_from_list
|
160
|
+
return true unless in_list?
|
161
|
+
shift_later_items_towards_start_of_list
|
162
|
+
update_attributes(position_field => nil)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Public: Indicates if an item is in the list
|
166
|
+
#
|
167
|
+
# Returns true if the item is in the list or false if not
|
168
|
+
def in_list?
|
169
|
+
self[position_field].present?
|
170
|
+
end
|
171
|
+
|
172
|
+
# Public: Indicates if an item is not in the list
|
173
|
+
#
|
174
|
+
# Returns true if the item is not in the list or false if it is
|
175
|
+
def not_in_list?
|
176
|
+
!in_list?
|
177
|
+
end
|
178
|
+
|
179
|
+
# Public: Indicates if an item is the first of the list
|
180
|
+
#
|
181
|
+
# Returns true if the item is the first in the list or false if not
|
182
|
+
def first?
|
183
|
+
self[position_field] == start_position_in_list
|
184
|
+
end
|
185
|
+
|
186
|
+
# Public: Indicates if an item is the last of the list
|
187
|
+
#
|
188
|
+
# Returns true if the item is the last in the list or false if not
|
189
|
+
def last?
|
190
|
+
self[position_field] == last_item_in_list[position_field]
|
191
|
+
end
|
192
|
+
|
193
|
+
# Public: Gets the following item in the list
|
194
|
+
#
|
195
|
+
# Returns the next item in the list
|
196
|
+
# or nil if there isn't a next item
|
197
|
+
def next_item
|
198
|
+
return unless in_list?
|
199
|
+
items_in_list.where(position_field => self[position_field]+1).first
|
200
|
+
end
|
201
|
+
alias_method :higher_item, :next_item
|
202
|
+
|
203
|
+
# Public: Gets the preceding item in the list
|
204
|
+
#
|
205
|
+
# Returns the previous item in the list
|
206
|
+
# or nil if there isn't a previous item
|
207
|
+
def previous_item
|
208
|
+
return unless in_list?
|
209
|
+
items_in_list.where(position_field => self[position_field]-1).first
|
210
|
+
end
|
211
|
+
alias_method :lower_item, :previous_item
|
212
|
+
|
213
|
+
# Public: Insert at a given position in the list
|
214
|
+
#
|
215
|
+
# new_position - an Integer indicating the position to insert the item at
|
216
|
+
#
|
217
|
+
# Returns nothing
|
218
|
+
def insert_at(new_position)
|
219
|
+
insert_space_at(new_position)
|
220
|
+
update_attribute(position_field, new_position)
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
# Internal: Make space in the list at a given position number
|
226
|
+
# used when moving a item to a new position in the list.
|
227
|
+
#
|
228
|
+
# position - an Integer representing the position number
|
229
|
+
#
|
230
|
+
# Returns nothing
|
231
|
+
def insert_space_at(position)
|
232
|
+
from = self[position_field] || next_available_position_in_list
|
233
|
+
to = position
|
234
|
+
|
235
|
+
if from < to
|
236
|
+
shift_position for: items_between(from, to + 1), by: -1
|
237
|
+
else
|
238
|
+
shift_position for: items_between(to - 1, from), by: 1
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def items_between(from, to, options = {})
|
243
|
+
strict = options.fetch(:strict, true)
|
244
|
+
if strict
|
245
|
+
items_in_list.where(position_field.gt => from, position_field.lt => to)
|
246
|
+
else
|
247
|
+
items_in_list.where(position_field.gte => from, position_field.lte => to)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def last_item_in_list
|
252
|
+
items_in_list.order_by_position.last
|
253
|
+
end
|
254
|
+
|
255
|
+
def last_position_in_list
|
256
|
+
last_item_in_list.try(position_field)
|
257
|
+
end
|
258
|
+
|
259
|
+
def previous_items_in_list
|
260
|
+
items_in_list.where(position_field.lt => self[position_field])
|
261
|
+
end
|
262
|
+
|
263
|
+
def next_items_in_list
|
264
|
+
items_in_list.where(position_field.gt => self[position_field])
|
265
|
+
end
|
266
|
+
|
267
|
+
def shift_later_items_towards_start_of_list
|
268
|
+
return unless in_list?
|
269
|
+
shift_position for: next_items_in_list, by: -1
|
270
|
+
end
|
271
|
+
|
272
|
+
def next_available_position_in_list
|
273
|
+
if item = last_item_in_list
|
274
|
+
item[position_field] + 1
|
275
|
+
else
|
276
|
+
start_position_in_list
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def first_position_in_list
|
281
|
+
Mongoid::ActsAsList.configuration.start_list_at
|
282
|
+
end
|
283
|
+
alias_method :start_position_in_list, :first_position_in_list
|
284
|
+
|
285
|
+
def position_within_list_boundaries(position)
|
286
|
+
if position < start_position_in_list
|
287
|
+
position = start_position_in_list
|
288
|
+
elsif position > last_position_in_list
|
289
|
+
position = last_position_in_list
|
290
|
+
end
|
291
|
+
|
292
|
+
position
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'mongoid/acts_as_list/list'
|
2
|
+
require_relative 'mongoid/acts_as_list/configuration'
|
3
|
+
require_relative 'mongoid/acts_as_list/version'
|
4
|
+
|
5
|
+
module Mongoid
|
6
|
+
module ActsAsList
|
7
|
+
class << self
|
8
|
+
attr_accessor :configuration
|
9
|
+
|
10
|
+
def configure
|
11
|
+
self.configuration ||= Configuration.new
|
12
|
+
yield(configuration) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def included base
|
16
|
+
self.configure
|
17
|
+
base.send :include, List
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongoid::ActsAsList::List do
|
4
|
+
[:position, :number].each do |default_field_name|
|
5
|
+
let(:position_field) { default_field_name }
|
6
|
+
|
7
|
+
before do
|
8
|
+
Mongoid::ActsAsList.configure do |config|
|
9
|
+
config.default_position_field = position_field
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'fixtures/embeds_many_models'
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Mongoid::ActsAsList::List::Embedded do
|
16
|
+
let(:category_1) { Category.create! }
|
17
|
+
let(:category_2) { Category.create! }
|
18
|
+
let(:category_3) { Category.create! }
|
19
|
+
|
20
|
+
before do
|
21
|
+
[category_1, category_2].each do |cat|
|
22
|
+
3.times do |n|
|
23
|
+
cat.items.create! position_field => n
|
24
|
+
end
|
25
|
+
cat.should have(3).items
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be embedded" do
|
30
|
+
EmbeddedItem.should be_embedded
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not include ActsAsList::Relational" do
|
34
|
+
EmbeddedItem.included_modules.should_not include Mongoid::ActsAsList::List::Root
|
35
|
+
end
|
36
|
+
|
37
|
+
it_behaves_like 'a list'
|
38
|
+
|
39
|
+
describe ".acts_as_list" do
|
40
|
+
it "defines #scope_condition" do
|
41
|
+
item = category_1.items.first
|
42
|
+
item.scope_condition.should == {position_field.ne => nil}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|