mongoid_orderable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []