mongoid_orderable 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@mongoid_orderable --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mongoid_orderable.gemspec
4
+ gemspec
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,163 @@
1
+ module Mongoid::Orderable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ orderable
6
+ end
7
+
8
+ module ClassMethods
9
+ def orderable options = {}
10
+ configuration = {
11
+ :column => :position,
12
+ :scope => nil
13
+ }
14
+
15
+ configuration.update options if options.is_a?(Hash)
16
+ field configuration[:column], type: Integer
17
+ index configuration[:column]
18
+
19
+ configuration[:scope] = "#{configuration[:scope]}_id".to_sym if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
20
+
21
+ case configuration[:scope]
22
+ when Symbol then
23
+ scope :orderable_scope, lambda { |document| where(configuration[:scope] => document.send(configuration[:scope])) }
24
+ when Proc then
25
+ scope :orderable_scope, configuration[:scope]
26
+ else
27
+ scope :orderable_scope, lambda { |document| where }
28
+ end
29
+
30
+ define_method :orderable_column do
31
+ configuration[:column]
32
+ end
33
+
34
+ before_save :add_to_list
35
+ after_destroy :remove_from_list
36
+ end
37
+ end
38
+
39
+ module InstanceMethods
40
+
41
+ def move_to! target_position
42
+ @move_to = target_position
43
+ save
44
+ end
45
+ alias_method :insert_at!, :move_to!
46
+
47
+ def move_to target_position
48
+ @move_to = target_position
49
+ end
50
+ alias_method :insert_at, :move_to
51
+
52
+ def move_to= target_position
53
+ @move_to = target_position
54
+ end
55
+ alias_method :insert_at=, :move_to=
56
+
57
+ [:top, :bottom].each do |symbol|
58
+ define_method "move_to_#{symbol}" do
59
+ move_to symbol
60
+ end
61
+
62
+ define_method "move_to_#{symbol}!" do
63
+ move_to! symbol
64
+ end
65
+ end
66
+
67
+ [:higher, :lower].each do |symbol|
68
+ define_method "move_#{symbol}" do
69
+ move_to symbol
70
+ end
71
+
72
+ define_method "move_#{symbol}!" do
73
+ move_to! symbol
74
+ end
75
+ end
76
+
77
+ def first?
78
+ in_list? && orderable_position == 1
79
+ end
80
+
81
+ def last?
82
+ in_list? && orderable_position == bottom_orderable_position
83
+ end
84
+
85
+ def in_list?
86
+ !orderable_position.nil?
87
+ end
88
+
89
+ def add_to_list
90
+ apply_position @move_to
91
+ end
92
+
93
+ def remove_from_list
94
+ orderable_scoped.where(orderable_column.gt => orderable_position).inc(orderable_column => -1)
95
+ end
96
+
97
+ private
98
+
99
+ def orderable_position
100
+ send orderable_column
101
+ end
102
+
103
+ def orderable_position= value
104
+ send "#{orderable_column}=", value
105
+ end
106
+
107
+ def orderable_scoped
108
+ if embedded?
109
+ send(metadata.inverse).send(metadata.name).orderable_scope(self)
110
+ else
111
+ self.class.orderable_scope(self)
112
+ end
113
+ end
114
+
115
+ def orderable_scope_changed?
116
+ !orderable_scoped.where(:_id => _id).exists?
117
+ end
118
+
119
+ def apply_position target_position
120
+ if persisted? && !embedded? && orderable_scope_changed?
121
+ self.class.find(_id).remove_from_list
122
+ self.orderable_position = nil
123
+ end
124
+
125
+ return if !target_position && in_list?
126
+
127
+ target_position = target_position_to_position target_position
128
+
129
+ unless in_list?
130
+ orderable_scoped.where(orderable_column.gte => target_position).inc(orderable_column => 1)
131
+ else
132
+ orderable_scoped.where(orderable_column.gte => target_position, orderable_column.lt => orderable_position).inc(orderable_column => 1) if target_position < orderable_position
133
+ orderable_scoped.where(orderable_column.gt => orderable_position, orderable_column.lte => target_position).inc(orderable_column => -1) if target_position > orderable_position
134
+ end
135
+
136
+ self.orderable_position = target_position
137
+ end
138
+
139
+ def target_position_to_position target_position
140
+ target_position = :bottom unless target_position
141
+
142
+ target_position = case target_position.to_sym
143
+ when :top then 1
144
+ when :bottom then bottom_orderable_position
145
+ when :higher then orderable_position.pred
146
+ when :lower then orderable_position.next
147
+ end unless target_position.is_a? Numeric
148
+
149
+ target_position = 1 if target_position < 1
150
+ target_position = bottom_orderable_position if target_position > bottom_orderable_position
151
+ target_position
152
+ end
153
+
154
+ def bottom_orderable_position
155
+ @bottom_orderable_position = begin
156
+ max = orderable_scoped.max(orderable_column).to_i
157
+ in_list? ? max : max.next
158
+ end
159
+ end
160
+
161
+ end
162
+
163
+ end
@@ -0,0 +1,17 @@
1
+ module MongoidOrderable #:nodoc:
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Enumerable #:nodoc:
5
+ def inc attributes = {}
6
+ iterate do |doc|
7
+ attributes.each do |attribute, value|
8
+ doc.inc(attribute, value)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ Mongoid::Contexts::Enumerable.send :include, MongoidOrderable::Mongoid::Contexts::Enumerable
@@ -0,0 +1,18 @@
1
+ module MongoidOrderable #:nodoc:
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Mongo #:nodoc:
5
+ def inc attributes = {}
6
+ klass.collection.update(
7
+ selector,
8
+ { "$inc" => attributes },
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
@@ -0,0 +1,4 @@
1
+ Mongoid::Criteria.delegate :inc, :to => :context
2
+ Mongoid::Finders.send :define_method, :inc do |*args|
3
+ criteria.send :inc, *args
4
+ end
@@ -0,0 +1,3 @@
1
+ module MongoidOrderable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'mongoid'
2
+ require 'mongoid_orderable/version'
3
+ require 'mongoid_orderable/mongoid/contexts/mongo'
4
+ require 'mongoid_orderable/mongoid/contexts/enumerable'
5
+ require 'mongoid_orderable/mongoid/criteria'
6
+
7
+ require 'mongoid/orderable'
8
+
9
+ module MongoidOrderable
10
+
11
+ end
@@ -0,0 +1,24 @@
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 "rspec"
23
+ s.add_runtime_dependency "mongoid"
24
+ end
@@ -0,0 +1,254 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Orderable do
4
+ class SimpleOrderable
5
+ include Mongoid::Document
6
+ include Mongoid::Orderable
7
+ end
8
+
9
+ class ScopedGroup
10
+ include Mongoid::Document
11
+
12
+ has_many :scoped_orderables
13
+ end
14
+
15
+ class ScopedOrderable
16
+ include Mongoid::Document
17
+ include Mongoid::Orderable
18
+
19
+ belongs_to :scoped_group
20
+
21
+ orderable :scope => :group
22
+ end
23
+
24
+
25
+
26
+ class EmbedsOrderable
27
+ include Mongoid::Document
28
+
29
+ embeds_many :embedded_orderables
30
+ end
31
+
32
+ class EmbeddedOrderable
33
+ include Mongoid::Document
34
+ include Mongoid::Orderable
35
+
36
+ embedded_in :embeds_orderable
37
+ end
38
+
39
+
40
+ describe SimpleOrderable do
41
+ before :each do
42
+ SimpleOrderable.delete_all
43
+ 5.times do
44
+ SimpleOrderable.create!
45
+ end
46
+ end
47
+
48
+ def positions
49
+ SimpleOrderable.order_by(:position, :asc).map(&:position)
50
+ end
51
+
52
+ it 'should have proper position column' do
53
+ SimpleOrderable.fields.key?('position').should be_true
54
+ SimpleOrderable.fields['position'].options[:type].should == Integer
55
+ end
56
+
57
+ it 'should set proper position while creation' do
58
+ positions.should == [1, 2, 3, 4, 5]
59
+ end
60
+
61
+ describe 'removement' do
62
+
63
+ it 'top' do
64
+ SimpleOrderable.where(:position => 1).destroy
65
+ positions.should == [1, 2, 3, 4]
66
+ end
67
+
68
+ it 'bottom' do
69
+ SimpleOrderable.where(:position => 5).destroy
70
+ positions.should == [1, 2, 3, 4]
71
+ end
72
+
73
+ it 'middle' do
74
+ SimpleOrderable.where(:position => 3).destroy
75
+ positions.should == [1, 2, 3, 4]
76
+ end
77
+
78
+ end
79
+
80
+ describe 'inserting' do
81
+
82
+ it 'top' do
83
+ newbie = SimpleOrderable.create! :move_to => :top
84
+ positions.should == [1, 2, 3, 4, 5, 6]
85
+ end
86
+
87
+ it 'bottom' do
88
+ newbie = SimpleOrderable.create! :move_to => :bottom
89
+ positions.should == [1, 2, 3, 4, 5, 6]
90
+ end
91
+
92
+ it 'middle' do
93
+ newbie = SimpleOrderable.create! :move_to => 4
94
+ positions.should == [1, 2, 3, 4, 5, 6]
95
+ end
96
+
97
+ end
98
+
99
+ describe 'movement' do
100
+
101
+ it 'higher from top' do
102
+ record = SimpleOrderable.where(:position => 1).first
103
+ record.update_attributes :move_to => :higher
104
+ positions.should == [1, 2, 3, 4, 5]
105
+ record.reload.position.should == 1
106
+ end
107
+
108
+ it 'higher from bottom' do
109
+ record = SimpleOrderable.where(:position => 5).first
110
+ record.update_attributes :move_to => :higher
111
+ positions.should == [1, 2, 3, 4, 5]
112
+ record.reload.position.should == 4
113
+ end
114
+
115
+ it 'higher from middle' do
116
+ record = SimpleOrderable.where(:position => 3).first
117
+ record.update_attributes :move_to => :higher
118
+ positions.should == [1, 2, 3, 4, 5]
119
+ record.reload.position.should == 2
120
+ end
121
+
122
+ it 'lower from top' do
123
+ record = SimpleOrderable.where(:position => 1).first
124
+ record.update_attributes :move_to => :lower
125
+ positions.should == [1, 2, 3, 4, 5]
126
+ record.reload.position.should == 2
127
+ end
128
+
129
+ it 'lower from bottom' do
130
+ record = SimpleOrderable.where(:position => 5).first
131
+ record.update_attributes :move_to => :lower
132
+ positions.should == [1, 2, 3, 4, 5]
133
+ record.reload.position.should == 5
134
+ end
135
+
136
+ it 'lower from middle' do
137
+ record = SimpleOrderable.where(:position => 3).first
138
+ record.update_attributes :move_to => :lower
139
+ positions.should == [1, 2, 3, 4, 5]
140
+ record.reload.position.should == 4
141
+ end
142
+
143
+ it "does nothing if position not change" do
144
+ record = SimpleOrderable.where(:position => 3).first
145
+ record.save
146
+ positions.should == [1, 2, 3, 4, 5]
147
+ record.reload.position.should == 3
148
+ end
149
+
150
+ end
151
+
152
+ end
153
+
154
+ describe ScopedOrderable do
155
+ before :each do
156
+ ScopedOrderable.delete_all
157
+ 2.times do
158
+ ScopedOrderable.create! :group_id => 1
159
+ end
160
+ 3.times do
161
+ ScopedOrderable.create! :group_id => 2
162
+ end
163
+ end
164
+
165
+ def positions
166
+ ScopedOrderable.order_by([:group_id, :asc], [:position, :asc]).map(&:position)
167
+ end
168
+
169
+ it 'should set proper position while creation' do
170
+ positions.should == [1, 2, 1, 2, 3]
171
+ end
172
+
173
+ describe 'removement' do
174
+
175
+ it 'top' do
176
+ ScopedOrderable.where(:position => 1, :group_id => 1).destroy
177
+ positions.should == [1, 1, 2, 3]
178
+ end
179
+
180
+ it 'bottom' do
181
+ ScopedOrderable.where(:position => 3, :group_id => 2).destroy
182
+ positions.should == [1, 2, 1, 2]
183
+ end
184
+
185
+ it 'middle' do
186
+ ScopedOrderable.where(:position => 2, :group_id => 2).destroy
187
+ positions.should == [1, 2, 1, 2]
188
+ end
189
+
190
+ end
191
+
192
+ describe 'inserting' do
193
+
194
+ it 'top' do
195
+ newbie = ScopedOrderable.create! :move_to => :top, :group_id => 1
196
+ positions.should == [1, 2, 3, 1, 2, 3]
197
+ end
198
+
199
+ it 'bottom' do
200
+ newbie = ScopedOrderable.create! :move_to => :bottom, :group_id => 2
201
+ positions.should == [1, 2, 1, 2, 3, 4]
202
+ end
203
+
204
+ it 'middle' do
205
+ newbie = ScopedOrderable.create! :move_to => 2, :group_id => 2
206
+ positions.should == [1, 2, 1, 2, 3, 4]
207
+ end
208
+
209
+ end
210
+
211
+ describe "scope movement" do
212
+
213
+ it "without point on position" do
214
+ record = ScopedOrderable.where(:group_id => 2, :position => 2).first
215
+ record.update_attributes :group_id => 1
216
+ positions.should == [1, 2, 3, 1, 2]
217
+ record.reload.position.should == 3
218
+ end
219
+
220
+ it "with point on position" do
221
+ record = ScopedOrderable.where(:group_id => 2, :position => 2).first
222
+ record.update_attributes :group_id => 1, :move_to => :top
223
+ positions.should == [1, 2, 3, 1, 2]
224
+ record.reload.position.should == 1
225
+ end
226
+
227
+ end
228
+
229
+ end
230
+
231
+ describe EmbeddedOrderable do
232
+ before :each do
233
+ EmbedsOrderable.delete_all
234
+ eo = EmbedsOrderable.create!
235
+ 2.times do
236
+ eo.embedded_orderables.create!
237
+ end
238
+ eo = EmbedsOrderable.create!
239
+ 3.times do
240
+ eo.embedded_orderables.create!
241
+ end
242
+ end
243
+
244
+ def positions
245
+ EmbedsOrderable.order_by(:position).all.map { |eo| eo.embedded_orderables.order_by(:position).map(&:position) }
246
+ end
247
+
248
+ it 'should set proper position while creation' do
249
+ positions.should == [[1, 2], [1, 2, 3]]
250
+ end
251
+
252
+ end
253
+
254
+ end
@@ -0,0 +1,27 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ DATABASE_ID = Process.pid
5
+
6
+ Mongoid.configure do |config|
7
+ database = Mongo::Connection.new.db("mongoid_#{DATABASE_ID}")
8
+ database.add_user("mongoid", "test")
9
+ config.master = database
10
+ config.logger = nil
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.mock_with :rspec
15
+
16
+ config.before :suite do
17
+ #DatabaseCleaner[:mongoid].strategy = :truncation
18
+ end
19
+
20
+ config.after :each do
21
+ #DatabaseCleaner[:mongoid].clean
22
+ end
23
+
24
+ config.after(:suite) do
25
+ Mongoid.master.connection.drop_database("mongoid_#{DATABASE_ID}")
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_orderable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - pyromaniac
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-11 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &23255180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *23255180
25
+ - !ruby/object:Gem::Dependency
26
+ name: mongoid
27
+ requirement: &23303440 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *23303440
36
+ description: Gem allows mongoid model behave as orderable list
37
+ email:
38
+ - kinwizard@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .rspec
45
+ - .rvmrc
46
+ - Gemfile
47
+ - README
48
+ - Rakefile
49
+ - lib/mongoid/orderable.rb
50
+ - lib/mongoid_orderable.rb
51
+ - lib/mongoid_orderable/mongoid/contexts/enumerable.rb
52
+ - lib/mongoid_orderable/mongoid/contexts/mongo.rb
53
+ - lib/mongoid_orderable/mongoid/criteria.rb
54
+ - lib/mongoid_orderable/version.rb
55
+ - mongoid_orderable.gemspec
56
+ - spec/mongoid/orderable_spec.rb
57
+ - spec/spec_helper.rb
58
+ homepage: ''
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: mongoid_orderable
78
+ rubygems_version: 1.8.6
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Acts as list mongoid implementation
82
+ test_files: []