mongoid_orderable 4.1.0 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/.rvmrc +1 -1
- data/CHANGELOG.md +4 -0
- data/README.md +54 -0
- data/lib/config/locales/en.yml +9 -0
- data/lib/mongoid/orderable.rb +10 -227
- data/lib/mongoid/orderable/callbacks.rb +75 -0
- data/lib/mongoid/orderable/configuration.rb +60 -0
- data/lib/mongoid/orderable/errors.rb +2 -0
- data/lib/mongoid/orderable/errors/invalid_target_position.rb +18 -0
- data/lib/mongoid/orderable/errors/mongoid_orderable_error.rb +14 -0
- data/lib/mongoid/orderable/generator.rb +34 -0
- data/lib/mongoid/orderable/generator/helpers.rb +29 -0
- data/lib/mongoid/orderable/generator/listable.rb +41 -0
- data/lib/mongoid/orderable/generator/movable.rb +62 -0
- data/lib/mongoid/orderable/generator/position.rb +26 -0
- data/lib/mongoid/orderable/generator/scope.rb +17 -0
- data/lib/mongoid/orderable/helpers.rb +50 -0
- data/lib/mongoid/orderable/listable.rb +49 -0
- data/lib/mongoid/orderable/movable.rb +58 -0
- data/lib/mongoid/orderable/orderable_class.rb +51 -0
- data/lib/mongoid_orderable.rb +19 -0
- data/lib/mongoid_orderable/version.rb +1 -1
- data/spec/mongoid/orderable_spec.rb +885 -86
- metadata +27 -11
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mongoid::Orderable
|
2
|
+
module Errors
|
3
|
+
class InvalidTargetPosition < Mongoid::Orderable::Errors::MongoidOrderableError
|
4
|
+
def initialize value
|
5
|
+
super _compose_message(value)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def _compose_message value
|
10
|
+
if MongoidOrderable.mongoid2?
|
11
|
+
translate 'invalid_target_position', { :value => value.inspect }
|
12
|
+
else
|
13
|
+
compose_message 'invalid_target_position', { :value => value.inspect }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Mongoid::Orderable
|
2
|
+
module Errors
|
3
|
+
class MongoidOrderableError < ::Mongoid::Errors::MongoidError
|
4
|
+
|
5
|
+
if MongoidOrderable.mongoid2?
|
6
|
+
def translate key, options
|
7
|
+
[:message, :summary, :resolution].map do |section|
|
8
|
+
::I18n.translate "#{BASE_KEY}.#{key}.#{section}", options
|
9
|
+
end.join ' '
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Generator
|
4
|
+
include Mongoid::Orderable::Generator::Scope
|
5
|
+
include Mongoid::Orderable::Generator::Position
|
6
|
+
include Mongoid::Orderable::Generator::Movable
|
7
|
+
include Mongoid::Orderable::Generator::Listable
|
8
|
+
include Mongoid::Orderable::Generator::Helpers
|
9
|
+
|
10
|
+
def column_name
|
11
|
+
configuration[:field_opts][:as] || configuration[:column]
|
12
|
+
end
|
13
|
+
|
14
|
+
def order_scope
|
15
|
+
configuration[:scope]
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_all_helpers
|
19
|
+
generate_scope_helpers(column_name, order_scope)
|
20
|
+
generate_position_helpers(column_name)
|
21
|
+
generate_movable_helpers(column_name)
|
22
|
+
generate_listable_helpers(column_name)
|
23
|
+
generate_orderable_helpers
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def generate_method(name, &block)
|
29
|
+
klass.send(:define_method, name, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Generator
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
def generate_orderable_helpers
|
7
|
+
self_class = klass
|
8
|
+
|
9
|
+
klass.class_eval <<-eos
|
10
|
+
def orderable_base(column = nil)
|
11
|
+
column ||= default_orderable_column
|
12
|
+
#{self_class}.orderable_configurations[column][:base]
|
13
|
+
end
|
14
|
+
|
15
|
+
def orderable_column(column = nil)
|
16
|
+
column ||= default_orderable_column
|
17
|
+
#{self_class}.orderable_configurations[column][:column]
|
18
|
+
end
|
19
|
+
eos
|
20
|
+
|
21
|
+
generate_method(:orderable_inherited_class) do
|
22
|
+
self_class.orderable_configurations.any?{ |col, conf| conf[:inherited] } ? self_class : self.class
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Generator
|
4
|
+
module Listable
|
5
|
+
|
6
|
+
def generate_listable_helpers(column_name)
|
7
|
+
generate_list_helpers(column_name)
|
8
|
+
generate_aliased_helpers(column_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def generate_list_helpers(column_name)
|
14
|
+
generate_method("next_#{column_name}_item") do
|
15
|
+
next_item(column_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
generate_method("next_#{column_name}_items") do
|
19
|
+
next_items(column_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
generate_method("previous_#{column_name}_item") do
|
23
|
+
previous_item(column_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
generate_method("previous_#{column_name}_items") do
|
27
|
+
previous_items(column_name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_aliased_helpers(column_name)
|
32
|
+
klass.class_eval do
|
33
|
+
alias_method "prev_#{column_name}_items", "previous_#{column_name}_items"
|
34
|
+
alias_method "prev_#{column_name}_item", "previous_#{column_name}_item"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Generator
|
4
|
+
module Movable
|
5
|
+
|
6
|
+
def generate_movable_helpers(column_name)
|
7
|
+
generate_move_to_helpers(column_name)
|
8
|
+
generate_insert_at_helpers(column_name)
|
9
|
+
generate_shorthand_helpers(column_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def generate_move_to_helpers(column_name)
|
15
|
+
generate_method("move_#{column_name}_to") do |target_position|
|
16
|
+
move_column_to target_position, :column => column_name
|
17
|
+
end
|
18
|
+
|
19
|
+
generate_method("move_#{column_name}_to!") do |target_position|
|
20
|
+
move_column_to target_position, :column => column_name
|
21
|
+
save
|
22
|
+
end
|
23
|
+
|
24
|
+
generate_method("move_#{column_name}_to=") do |target_position|
|
25
|
+
move_column_to target_position, :column => column_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_insert_at_helpers(column_name)
|
30
|
+
klass.class_eval do
|
31
|
+
alias_method "insert_#{column_name}_at!", "move_#{column_name}_to!"
|
32
|
+
alias_method "insert_#{column_name}_at", "move_#{column_name}_to"
|
33
|
+
alias_method "insert_#{column_name}_at=", "move_#{column_name}_to="
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_shorthand_helpers(column_name)
|
38
|
+
[:top, :bottom].each do |symbol|
|
39
|
+
generate_method "move_#{column_name}_to_#{symbol}" do
|
40
|
+
move_to symbol, :column => column_name
|
41
|
+
end
|
42
|
+
|
43
|
+
generate_method "move_#{column_name}_to_#{symbol}!" do
|
44
|
+
move_to! symbol, :column => column_name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
[:higher, :lower].each do |symbol|
|
49
|
+
generate_method "move_#{column_name}_#{symbol}" do
|
50
|
+
move_to symbol, :column => column_name
|
51
|
+
end
|
52
|
+
|
53
|
+
generate_method "move_#{column_name}_#{symbol}!" do
|
54
|
+
move_to! symbol, :column => column_name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Generator
|
4
|
+
module Position
|
5
|
+
|
6
|
+
def generate_position_helpers(column_name)
|
7
|
+
klass.class_eval <<-eos
|
8
|
+
def orderable_position(column = nil)
|
9
|
+
column ||= default_orderable_column
|
10
|
+
send "orderable_\#{column}_position"
|
11
|
+
end
|
12
|
+
eos
|
13
|
+
|
14
|
+
generate_method("orderable_#{column_name}_position") do
|
15
|
+
send column_name
|
16
|
+
end
|
17
|
+
|
18
|
+
generate_method("orderable_#{column_name}_position=") do |value|
|
19
|
+
send "#{column_name}=", value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Generator
|
4
|
+
module Scope
|
5
|
+
def generate_scope_helpers(column_name, order_scope)
|
6
|
+
klass.class_eval do
|
7
|
+
scope "orderable_#{column_name}_scope", case order_scope
|
8
|
+
when Symbol then lambda { |document| where(order_scope => document.send(order_scope)) }
|
9
|
+
when Proc then order_scope
|
10
|
+
else lambda { |document| where({}) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Orderable
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def orderable_keys
|
6
|
+
Array orderable_inherited_class.orderable_configurations.try(:keys)
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_orderable_column
|
10
|
+
self.class.orderable_configurations.detect{ |c, conf| conf[:default] }.try(:first) || self.orderable_keys.first
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def orderable_scoped(column = nil)
|
16
|
+
column ||= default_orderable_column
|
17
|
+
|
18
|
+
if embedded?
|
19
|
+
send(MongoidOrderable.metadata(self).inverse).send(MongoidOrderable.metadata(self).name).send("orderable_#{column}_scope", self)
|
20
|
+
else
|
21
|
+
self.orderable_inherited_class.send("orderable_#{column}_scope", self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def orderable_scope_changed?(column)
|
26
|
+
if Mongoid.respond_to?(:unit_of_work)
|
27
|
+
Mongoid.unit_of_work do
|
28
|
+
orderable_scope_changed_query(column)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
orderable_scope_changed_query(column)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def orderable_scope_changed_query(column)
|
36
|
+
!orderable_scoped(column).where(:_id => _id).exists?
|
37
|
+
end
|
38
|
+
|
39
|
+
def bottom_orderable_position(column = nil)
|
40
|
+
column ||= default_orderable_column
|
41
|
+
@bottom_orderable_position = begin
|
42
|
+
positions_list = orderable_scoped(column).distinct(orderable_column(column)).compact
|
43
|
+
return orderable_base(column) if positions_list.empty?
|
44
|
+
max = positions_list.map(&:to_i).max.to_i
|
45
|
+
in_list?(column) ? max : max.next
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
@@ -0,0 +1,58 @@
|
|
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
|
@@ -0,0 +1,51 @@
|
|
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 MongoidOrderable.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
|