has_order 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e557f1236be3b4b07640fbb1d983e1505ca8ee31
4
- data.tar.gz: 0b3cd15c4df5733973286f6b7d38754708f3d2f3
3
+ metadata.gz: 7548cd9cfc471c16deef5e247548fbd06f4e0308
4
+ data.tar.gz: 32acddb9839f96d19282e3fe9e9d70f32a5debbb
5
5
  SHA512:
6
- metadata.gz: b83accc8697a18904838273af96ea7851d16f2b51dbd67b61d202295330d3eb07e427965b16307488a9f02612a3ba29ec59939b8b96083a2a3836d2a5eb85e03
7
- data.tar.gz: 6bcb95921c60c534e4203d90c3d0b9d6bc1d2a3581d47883bceadcfc7a8fee37c85aa57d1105173a37bff527853b896e7f329dac17c9f610000194e7020c9208
6
+ metadata.gz: 70b29b7e60b8d11ba435f16c6aed6f7f0369ca0807c66c52d261fe93932e98886e68df15e806f04e9660d72105e153ea46b54f1445375bc4896f7cfd607382b7
7
+ data.tar.gz: 2441f0889017894d863c0d966d8a1f1bb7c82cad31e3ec6890a4dddf6a717d0b2c1c8fca45cc80542564e231e59546e6a5145559eadfc1a2e28a59d7d5653c1a
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+
8
+ env:
9
+ - HAS_ORDER_ORM=mongoid
10
+ - HAS_ORDER_ORM=activerecord
11
+
12
+ matrix:
13
+ fast_finish: true
14
+
15
+ services:
16
+ - mongodb
17
+
18
+ addons:
19
+ code_climate:
20
+ repo_token: 0ecd8512477ddbf9e06702762f756620b21ff1b7441a8c1dd275265acbefaf66
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/has_order.svg)](http://badge.fury.io/rb/has_order)
2
+ [![Build Status](https://travis-ci.org/kolesnikovde/has_order.svg?branch=master)](https://travis-ci.org/kolesnikovde/has_order)
3
+ [![Code Climate](https://codeclimate.com/github/kolesnikovde/has_order/badges/gpa.svg)](https://codeclimate.com/github/kolesnikovde/has_order)
4
+ [![Test Coverage](https://codeclimate.com/github/kolesnikovde/has_order/badges/coverage.svg)](https://codeclimate.com/github/kolesnikovde/has_order)
2
5
 
3
6
  # has_order
4
7
 
5
- Provides tree behavior to active_record models.
8
+ Ordering behavior for ActiveRecord models and Mongoid documents.
6
9
 
7
10
  ## Installation
8
11
 
@@ -16,14 +19,37 @@ And then execute:
16
19
 
17
20
  ## Usage
18
21
 
22
+ Example model:
23
+ ```sh
24
+ $ rails g model Item \
25
+ name:string \
26
+ position:integer # Do not specify default value.
27
+ ```
19
28
  ```ruby
20
29
  class Item < ActiveRecord::Base
21
- # :scope - optional, proc, symbol or an array of symbols.
22
- # :position_column - optional, default 'position'.
23
- # :shift_interval - optional, default 1000.
24
30
  has_order
25
31
  end
32
+ ```
33
+
34
+ or Mongoid document:
35
+ ```ruby
36
+ class Item
37
+ include Mongoid::Document
38
+ include Mongoid::HasOrder
26
39
 
40
+ has_order
41
+ end
42
+ ```
43
+
44
+ Options:
45
+ ```
46
+ scope - optional, proc, symbol or an array of symbols.
47
+ position_column - optional, default 'position'.
48
+ shift_interval - optional, default 1000.
49
+ ```
50
+
51
+ Methods:
52
+ ```ruby
27
53
  foo, bar, baz, qux = Item.create([
28
54
  { name: 'foo' },
29
55
  { name: 'bar' },
@@ -32,19 +58,27 @@ foo, bar, baz, qux = Item.create([
32
58
  ])
33
59
 
34
60
  Item.at(foo.position) # => foo
35
- baz.higher # => [ qux ]
36
- baz.and_higher # => [ baz, qux ]
37
- baz.lower # => [ foo, bar ]
38
- baz.and_lower # => [ foo, bar, baz ]
61
+ Item.ordered # => [ foo, bar, baz, qux ]
62
+
63
+ baz.higher # => [ qux ]
64
+ baz.and_higher # => [ baz, qux ]
65
+ baz.lower # => [ foo, bar ]
66
+ baz.and_lower # => [ foo, bar, baz ]
67
+ baz.previous # => bar
68
+ baz.prev # => bar
69
+ baz.next # => qux
39
70
 
40
- baz.move_before(bar)
41
- Item.ordered # => [ foo, baz, bar, qux ]
71
+ baz.move_before(bar)
72
+ Item.ordered
73
+ # => [ foo, baz, bar, qux ]
42
74
 
43
75
  foo.move_after(qux)
44
- Item.ordered # => [ baz, bar, qux, foo ]
76
+ Item.ordered
77
+ # => [ baz, bar, qux, foo ]
45
78
 
46
79
  baz.move_to(qux)
47
- Item.ordered # => [ bar, baz, qux, foo ]
80
+ Item.ordered
81
+ # => [ bar, baz, qux, foo ]
48
82
  ```
49
83
 
50
84
  ## License
data/has_order.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
 
10
10
  spec.authors = ['Kolesnikov Danil']
11
11
  spec.email = ['kolesnikovde@gmail.com']
12
- spec.description = 'Provides list behavior to active_record models.'
13
- spec.summary = 'Provides list behavior to active_record models.'
12
+ spec.description = 'Ordering behavior for ActiveRecord models and Mongoid documents.'
13
+ spec.summary = 'Ordering behavior for ActiveRecord models and Mongoid documents.'
14
14
  spec.homepage = 'https://github.com/kolesnikovde/has_order'
15
15
  spec.license = 'MIT'
16
16
 
@@ -18,12 +18,15 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^spec/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency 'bundler', '~> 1'
22
- spec.add_development_dependency 'rake', '~> 10'
23
- spec.add_development_dependency 'rspec', '~> 3'
24
- spec.add_development_dependency 'sqlite3', '~> 1'
25
- spec.add_development_dependency 'simplecov'
21
+ spec.add_development_dependency 'bundler', '~> 1'
22
+ spec.add_development_dependency 'rake', '~> 10'
23
+ spec.add_development_dependency 'rspec', '~> 3'
24
+
25
+ spec.add_development_dependency 'sqlite3', '~> 1'
26
+ spec.add_development_dependency 'activerecord', '~> 4'
27
+ spec.add_development_dependency 'mongoid', '~> 4'
28
+
29
+ spec.add_development_dependency 'codeclimate-test-reporter'
26
30
 
27
- spec.add_runtime_dependency 'activerecord', '~> 4'
28
31
  spec.add_runtime_dependency 'activesupport', '~> 4'
29
32
  end
data/lib/has_order.rb CHANGED
@@ -1,25 +1,23 @@
1
- require 'active_record'
2
1
  require 'has_order/version'
2
+ require 'has_order/orm_adapter'
3
3
 
4
4
  module HasOrder
5
- # options - :scope - optional, proc, symbol or an array of symbols.
6
- # :position_column - optional, default 'position'.
7
- # :shift_interval - optional, default 1000.
8
- def has_order options = {}
9
- extend ClassMethods
5
+ DEFAULT_OPTIONS = {
6
+ position_column: :position,
7
+ shift_interval: 1000
8
+ }
9
+
10
+ def has_order(options = {})
10
11
  include InstanceMethods
12
+ extend ClassMethods
11
13
 
12
- cattr_accessor :position_column do
13
- options[:position_column] || :position
14
- end
15
-
16
- cattr_accessor :position_shift_interval do
17
- options[:shift_interval] || 1000
18
- end
14
+ setup_has_order_options(options)
19
15
 
20
16
  before_save :set_default_position, if: :set_default_position?
21
17
 
22
18
  define_list_scope(options[:scope])
19
+
20
+ include HasOrder::OrmAdapter
23
21
  end
24
22
 
25
23
  module ClassMethods
@@ -27,17 +25,26 @@ module HasOrder
27
25
  where(position_column => pos)
28
26
  end
29
27
 
30
- def ordered
31
- order(arel_table[position_column].asc)
32
- end
28
+ protected
29
+
30
+ def setup_has_order_options(options)
31
+ options.assert_valid_keys(:scope, :position_column, :shift_interval)
32
+
33
+ options.reverse_merge!(DEFAULT_OPTIONS)
33
34
 
34
- def shift!
35
- col = position_column
36
- update_all("#{col} = #{col} + #{position_shift_interval}")
35
+ cattr_accessor(:position_column) { options[:position_column] }
36
+ cattr_accessor(:position_shift_interval) { options[:shift_interval] }
37
37
  end
38
38
 
39
- def next_position
40
- maximum(position_column).to_i + position_shift_interval
39
+ def define_list_scope(list_scope)
40
+ scope :list_scope, case list_scope
41
+ when Proc
42
+ list_scope
43
+ when nil
44
+ ->(model) { where(nil) }
45
+ else
46
+ ->(model) { where(Hash[Array(list_scope).map{ |s| [ s, model[s] ] }]) }
47
+ end
41
48
  end
42
49
  end
43
50
 
@@ -70,12 +77,14 @@ module HasOrder
70
77
  lower.ordered.last
71
78
  end
72
79
 
80
+ alias_method :previous, :prev
81
+
73
82
  def next
74
83
  higher.ordered.first
75
84
  end
76
85
 
77
86
  def move_to(pos)
78
- ActiveRecord::Base.transaction do
87
+ self.class.transaction do
79
88
  if node = list_scope.at(pos).first
80
89
  node.and_higher.shift!
81
90
  end
@@ -86,10 +95,10 @@ module HasOrder
86
95
  end
87
96
 
88
97
  def move_before(node)
89
- node_pos = node.position
90
- pos = node_pos > 0 ? node_pos - 1 : node_pos
98
+ self.class.transaction do
99
+ node_pos = node.position
100
+ pos = node_pos > 0 ? node_pos - 1 : node_pos
91
101
 
92
- ActiveRecord::Base.transaction do
93
102
  if list_scope.at(pos).exists?
94
103
  pos = node_pos
95
104
  node.and_higher.shift!
@@ -101,10 +110,10 @@ module HasOrder
101
110
  end
102
111
 
103
112
  def move_after(node)
104
- node_pos = node.position
105
- pos = node_pos + 1
113
+ self.class.transaction do
114
+ node_pos = node.position
115
+ pos = node_pos + 1
106
116
 
107
- ActiveRecord::Base.transaction do
108
117
  if list_scope.at(pos).exists?
109
118
  node.higher.shift!
110
119
  end
@@ -120,11 +129,6 @@ module HasOrder
120
129
  self.class.list_scope(self)
121
130
  end
122
131
 
123
- def where_position(cmp)
124
- col = self.class.arel_table[position_column]
125
- list_scope.where(col.send(cmp, position))
126
- end
127
-
128
132
  def set_default_position
129
133
  self.position = list_scope.next_position
130
134
  end
@@ -133,19 +137,4 @@ module HasOrder
133
137
  position.nil?
134
138
  end
135
139
  end
136
-
137
- protected
138
-
139
- def define_list_scope(list_scope)
140
- scope :list_scope, case list_scope
141
- when Proc
142
- list_scope
143
- when nil
144
- ->(model) { self }
145
- else
146
- ->(model) { where(Hash[Array(list_scope).map{ |s| [ s, model[s] ] }]) }
147
- end
148
- end
149
140
  end
150
-
151
- ActiveRecord::Base.extend(HasOrder)
@@ -0,0 +1,29 @@
1
+ module HasOrder
2
+ # :nocov:
3
+ module OrmAdapter
4
+ if defined?(::ActiveRecord)
5
+ ::ActiveRecord::Base.extend(HasOrder)
6
+ end
7
+
8
+ if defined?(::Mongoid)
9
+ module ::Mongoid::HasOrder
10
+ def self.included(base)
11
+ base.extend(::HasOrder)
12
+ end
13
+ end
14
+ end
15
+
16
+ def self.included(base)
17
+ base.class_eval do
18
+ if defined?(::ActiveRecord) and self < ::ActiveRecord::Base
19
+ require 'has_order/orm_adapter/active_record'
20
+ include ActiveRecord
21
+ elsif defined?(::Mongoid) and self < ::Mongoid::Document
22
+ require 'has_order/orm_adapter/mongoid'
23
+ include Mongoid
24
+ end
25
+ end
26
+ end
27
+ end
28
+ # :nocov:
29
+ end
@@ -0,0 +1,34 @@
1
+ module HasOrder
2
+ module OrmAdapter
3
+ module ActiveRecord
4
+ def self.included(base)
5
+ base.class_eval do
6
+ extend ClassMethods
7
+ include InstanceMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def ordered
13
+ order(arel_table[position_column].asc)
14
+ end
15
+
16
+ def shift!
17
+ col = position_column
18
+ update_all("#{col} = #{col} + #{position_shift_interval}")
19
+ end
20
+
21
+ def next_position
22
+ maximum(position_column).to_i + position_shift_interval
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ def where_position(cmp)
28
+ col = self.class.arel_table[position_column]
29
+ list_scope.where(col.send(cmp, position))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ module HasOrder
2
+ module OrmAdapter
3
+ module Mongoid
4
+ def self.included(base)
5
+ base.class_eval do
6
+ extend ClassMethods
7
+ include InstanceMethods
8
+
9
+ field position_column, type: Integer
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def transaction(&blk)
15
+ yield
16
+ end
17
+
18
+ def ordered
19
+ asc(position_column)
20
+ end
21
+
22
+ def shift!
23
+ scoped.inc(position_column => position_shift_interval)
24
+ end
25
+
26
+ def next_position
27
+ max(position_column).to_i + position_shift_interval
28
+ end
29
+ end
30
+
31
+ module InstanceMethods
32
+ def where_position(cmp)
33
+ cmp = { gteq: :gte, lteq: :lte }[cmp] || cmp
34
+ list_scope.where(position_column.send(cmp) => position)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module HasOrder
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,194 +1,14 @@
1
1
  require 'spec_helper'
2
+ require 'list'
2
3
 
3
- describe HasOrder do
4
- before(:each) do
5
- @foo, @bar, @baz, @qux = Item.create([
6
- { name: 'foo', category: 'A' },
7
- { name: 'bar', category: 'A' },
8
- { name: 'baz', category: 'B' },
9
- { name: 'qux', category: 'B' }
10
- ])
11
- end
12
-
13
- def reload_items
14
- [ @foo, @bar, @baz, @qux ].each(&:reload)
15
- end
16
-
17
- describe 'position column' do
18
- it 'defaults to "position"' do
19
- expect(Item.position_column).to eq(:position)
20
- end
21
-
22
- it 'provides accessor' do
23
- item = Item.new
24
- item.position = 5
25
- expect(item.position).to eq(5)
26
- end
27
- end
28
-
29
- describe '.at' do
30
- it 'scopes position' do
31
- @foo.position = 5
32
- @foo.save
33
-
34
- expect(Item.at(5).first).to eq(@foo)
35
- end
36
- end
37
-
38
- describe '.ordered' do
39
- it 'ranks items according to position' do
40
- @foo.update_attribute(:position, 2)
41
- @bar.update_attribute(:position, 1)
42
- @baz.update_attribute(:position, 4)
43
- @qux.update_attribute(:position, 3)
44
-
45
- expect(Item.ordered).to eq([ @bar, @foo, @qux, @baz ])
46
- end
47
- end
48
-
49
- describe '.next_position' do
50
- it 'returns next available position' do
51
- @quux = Item.create!(name: 'quux')
52
-
53
- expect(@quux.position).to be < Item.next_position
54
- end
55
- end
56
-
57
- describe '#lower' do
58
- it 'returns items with position less or equal to the item position' do
59
- expect(@qux.lower).to match_array([ @foo, @bar, @baz ])
60
- end
61
- end
62
-
63
- describe '#and_lower' do
64
- it 'returns items with position less than or equal to the item position' do
65
- expect(@baz.and_lower).to match_array([ @foo, @bar, @baz ])
66
- end
67
- end
68
-
69
- describe '#higher' do
70
- it 'returns items with position greater than the item position' do
71
- expect(@foo.higher).to match_array([ @bar, @baz, @qux ])
72
- end
73
- end
74
-
75
- describe '#and_higher' do
76
- it 'returns items with position greater than or equal to the item' do
77
- expect(@bar.and_higher).to match_array([ @bar, @baz, @qux ])
78
- end
79
- end
80
-
81
- describe '#prev' do
82
- it 'returns previous item' do
83
- expect(@baz.prev).to eq(@bar)
84
- end
85
- end
86
-
87
- describe '#next' do
88
- it 'returns next item' do
89
- expect(@bar.next).to eq(@baz)
90
- end
91
- end
92
-
93
- context 'first item' do
94
- subject { @foo }
95
-
96
- it { expect(subject.lower).to be_empty }
97
- it { expect(subject.and_lower).to eq([ subject ]) }
98
- it { expect(subject.prev).to be_nil }
99
- end
100
-
101
- context 'last item' do
102
- subject { @qux }
103
-
104
- it { expect(subject.higher).to be_empty }
105
- it { expect(subject.and_higher).to eq([ subject ]) }
106
- it { expect(subject.next).to be_nil }
107
- end
108
-
109
- describe '#move_to' do
110
- it 'shifts item and higher items when the position is occupied' do
111
- @foo.update_attribute(:position, 1)
112
- @bar.update_attribute(:position, 2)
113
- @baz.update_attribute(:position, 3)
114
- @qux.update_attribute(:position, 4)
115
-
116
- @qux.move_to(@bar.position)
117
-
118
- reload_items
119
-
120
- expect(Item.ordered).to eq([ @foo, @qux, @bar, @baz ])
121
- end
122
- end
123
-
124
- describe '#move_before' do
125
- it 'decreases item position' do
126
- prev_qux_position = @qux.position
127
- @qux.move_before(@foo)
128
-
129
- expect(@qux.position).to be < prev_qux_position
130
- end
131
-
132
- it 'shifts item and higher items when the position is occupied' do
133
- @foo.update_attribute(:position, 1)
134
- @bar.update_attribute(:position, 2)
135
- @baz.update_attribute(:position, 3)
136
- @qux.update_attribute(:position, 4)
137
-
138
- @baz.move_before(@bar)
139
-
140
- reload_items
141
-
142
- expect(Item.ordered).to eq([ @foo, @baz, @bar, @qux ])
143
- end
144
- end
145
-
146
- describe '#move_after' do
147
- it 'increases item position' do
148
- prev_foo_position = @foo.position
149
- @foo.move_after(@qux)
150
-
151
- expect(@foo.position).to be > prev_foo_position
152
- end
153
-
154
- it 'shifts higher items when the position is occupied' do
155
- @foo.update_attribute(:position, 1)
156
- @bar.update_attribute(:position, 2)
157
- @baz.update_attribute(:position, 3)
158
- @qux.update_attribute(:position, 4)
159
-
160
- @foo.move_after(@bar)
161
-
162
- reload_items
163
-
164
- expect(Item.ordered).to eq([ @bar, @foo, @baz, @qux ])
165
- end
166
- end
167
-
168
- describe 'scoping' do
169
- shared_examples 'scoped' do
170
- it { expect(subject.higher).to be_empty }
171
- it { expect(subject.and_lower).to match_array([ @bar, @foo ]) }
172
- end
173
-
174
- describe 'via attributes' do
175
- before(:all) do
176
- Item.has_order scope: :category
177
- end
178
-
179
- subject { @bar }
180
-
181
- it_behaves_like 'scoped'
182
- end
183
-
184
- describe 'via proc' do
185
- before(:all) do
186
- Item.has_order scope: ->(i){ Item.where(category: i.category) }
187
- end
4
+ describe ListItem do
5
+ it_behaves_like 'list'
6
+ end
188
7
 
189
- subject { @bar }
8
+ describe ScopedWithColumnListItem do
9
+ it_behaves_like 'scoped list'
10
+ end
190
11
 
191
- it_behaves_like 'scoped'
192
- end
193
- end
12
+ describe ScopedWithLambdaListItem do
13
+ it_behaves_like 'scoped list'
194
14
  end
data/spec/list.rb ADDED
@@ -0,0 +1,184 @@
1
+ shared_examples 'list' do
2
+ before(:each) do
3
+ @foo, @bar, @baz, @qux = described_class.create([
4
+ { name: 'foo' },
5
+ { name: 'bar' },
6
+ { name: 'baz' },
7
+ { name: 'qux' }
8
+ ])
9
+ end
10
+
11
+ def reload_items
12
+ [ @foo, @bar, @baz, @qux ].each(&:reload)
13
+ end
14
+
15
+ describe 'position column' do
16
+ it 'defaults to "position"' do
17
+ expect(described_class.position_column).to eq(:position)
18
+ end
19
+
20
+ it 'provides accessor' do
21
+ item = described_class.new
22
+ item.position = 5
23
+ expect(item.position).to eq(5)
24
+ end
25
+ end
26
+
27
+ describe '.at' do
28
+ it 'scopes position' do
29
+ @foo.position = 5
30
+ @foo.save
31
+
32
+ expect(described_class.at(5).first).to eq(@foo)
33
+ end
34
+ end
35
+
36
+ describe '.ordered' do
37
+ it 'ranks items according to position' do
38
+ @foo.update_attribute(:position, 2)
39
+ @bar.update_attribute(:position, 1)
40
+ @baz.update_attribute(:position, 4)
41
+ @qux.update_attribute(:position, 3)
42
+
43
+ expect(described_class.ordered).to eq([ @bar, @foo, @qux, @baz ])
44
+ end
45
+ end
46
+
47
+ describe '.next_position' do
48
+ it 'returns next available position' do
49
+ @quux = described_class.create!(name: 'quux')
50
+
51
+ expect(@quux.position).to be < described_class.next_position
52
+ end
53
+ end
54
+
55
+ describe '#lower' do
56
+ it 'returns items with position less or equal to the item position' do
57
+ expect(@qux.lower).to match_array([ @foo, @bar, @baz ])
58
+ end
59
+ end
60
+
61
+ describe '#and_lower' do
62
+ it 'returns items with position less than or equal to the item position' do
63
+ expect(@baz.and_lower).to match_array([ @foo, @bar, @baz ])
64
+ end
65
+ end
66
+
67
+ describe '#higher' do
68
+ it 'returns items with position greater than the item position' do
69
+ expect(@foo.higher).to match_array([ @bar, @baz, @qux ])
70
+ end
71
+ end
72
+
73
+ describe '#and_higher' do
74
+ it 'returns items with position greater than or equal to the item' do
75
+ expect(@bar.and_higher).to match_array([ @bar, @baz, @qux ])
76
+ end
77
+ end
78
+
79
+ describe '#prev' do
80
+ it 'returns previous item' do
81
+ expect(@baz.prev).to eq(@bar)
82
+ expect(@baz.previous).to eq(@bar)
83
+ end
84
+ end
85
+
86
+ describe '#next' do
87
+ it 'returns next item' do
88
+ expect(@bar.next).to eq(@baz)
89
+ end
90
+ end
91
+
92
+ context 'first item' do
93
+ subject { @foo }
94
+
95
+ it { expect(subject.lower).to be_empty }
96
+ it { expect(subject.and_lower).to eq([ subject ]) }
97
+ it { expect(subject.prev).to be_nil }
98
+ end
99
+
100
+ context 'last item' do
101
+ subject { @qux }
102
+
103
+ it { expect(subject.higher).to be_empty }
104
+ it { expect(subject.and_higher).to eq([ subject ]) }
105
+ it { expect(subject.next).to be_nil }
106
+ end
107
+
108
+ describe '#move_to' do
109
+ it 'shifts item and higher items when the position is occupied' do
110
+ @foo.update_attribute(:position, 1)
111
+ @bar.update_attribute(:position, 2)
112
+ @baz.update_attribute(:position, 3)
113
+ @qux.update_attribute(:position, 4)
114
+
115
+ @qux.move_to(@bar.position)
116
+
117
+ reload_items
118
+
119
+ expect(described_class.ordered).to eq([ @foo, @qux, @bar, @baz ])
120
+ end
121
+ end
122
+
123
+ describe '#move_before' do
124
+ it 'decreases item position' do
125
+ prev_qux_position = @qux.position
126
+ @qux.move_before(@foo)
127
+
128
+ expect(@qux.position).to be < prev_qux_position
129
+ end
130
+
131
+ it 'shifts item and higher items when the position is occupied' do
132
+ @foo.update_attribute(:position, 1)
133
+ @bar.update_attribute(:position, 2)
134
+ @baz.update_attribute(:position, 3)
135
+ @qux.update_attribute(:position, 4)
136
+
137
+ @baz.move_before(@bar)
138
+
139
+ reload_items
140
+
141
+ expect(described_class.ordered).to eq([ @foo, @baz, @bar, @qux ])
142
+ end
143
+ end
144
+
145
+ describe '#move_after' do
146
+ it 'increases item position' do
147
+ prev_foo_position = @foo.position
148
+ @foo.move_after(@qux)
149
+
150
+ expect(@foo.position).to be > prev_foo_position
151
+ end
152
+
153
+ it 'shifts higher items when the position is occupied' do
154
+ @foo.update_attribute(:position, 1)
155
+ @bar.update_attribute(:position, 2)
156
+ @baz.update_attribute(:position, 3)
157
+ @qux.update_attribute(:position, 4)
158
+
159
+ @foo.move_after(@bar)
160
+
161
+ reload_items
162
+
163
+ expect(described_class.ordered).to eq([ @bar, @foo, @baz, @qux ])
164
+ end
165
+ end
166
+ end
167
+
168
+ shared_examples 'scoped list' do
169
+ before(:each) do
170
+ @foo, @bar, @baz, @qux = described_class.create([
171
+ { name: 'foo', category: 'A' },
172
+ { name: 'bar', category: 'A' },
173
+ { name: 'baz', category: 'B' },
174
+ { name: 'qux', category: 'B' }
175
+ ])
176
+ end
177
+
178
+ it 'restricts scope' do
179
+ expect(@bar.higher).to be_empty
180
+ expect(@bar.and_lower).to match_array([ @bar, @foo ])
181
+ expect(@baz.higher).to match_array([ @qux ])
182
+ expect(@baz.lower).to be_empty
183
+ end
184
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,28 +1,11 @@
1
- if ENV['SIMPLECOV']
2
- require 'simplecov'
3
- SimpleCov.start 'rails'
4
- end
5
-
6
- require 'fileutils'
7
- require 'logger'
8
- require 'sqlite3'
9
- require 'active_record'
10
-
11
- FileUtils.mkdir_p('tmp/logs/')
12
- ActiveRecord::Base.logger = Logger.new('tmp/logs/test.log')
13
- ActiveRecord::Base.logger.level = Logger::DEBUG
14
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
15
- ActiveRecord::Schema.verbose = false
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
16
3
 
17
- require File.expand_path('../db/schema.rb', __FILE__)
18
- require File.expand_path('../support/models.rb', __FILE__)
4
+ orm_adapter = ENV['HAS_ORDER_ORM']
19
5
 
20
- RSpec.configure do |config|
21
- config.around :each do |example|
22
- ActiveRecord::Base.transaction do
23
- example.run
24
-
25
- raise ActiveRecord::Rollback
26
- end
27
- end
6
+ unless %w[activerecord mongoid mongo_mapper].include?(orm_adapter)
7
+ raise 'Unknown ORM.'
28
8
  end
9
+
10
+ require "support/orm/#{orm_adapter}/setup"
11
+ require 'support/models'
@@ -1,5 +1,11 @@
1
- require 'has_order'
2
-
3
- class Item < ActiveRecord::Base
1
+ class ListItem < Item
4
2
  has_order
5
3
  end
4
+
5
+ class ScopedWithColumnListItem < Item
6
+ has_order scope: :category
7
+ end
8
+
9
+ class ScopedWithLambdaListItem < Item
10
+ has_order scope: ->(item){ where(category: item.category) }
11
+ end
@@ -2,7 +2,11 @@ ActiveRecord::Schema.define(version: 0) do
2
2
  create_table :items, force: true do |t|
3
3
  t.string :name
4
4
  t.string :category
5
+ t.string :type
5
6
 
6
7
  t.integer :position
7
8
  end
8
9
  end
10
+
11
+ class Item < ActiveRecord::Base
12
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_record'
2
+ require 'sqlite3'
3
+ require 'has_order'
4
+
5
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
6
+ ActiveRecord::Schema.verbose = false
7
+
8
+ require_relative 'item_model'
9
+
10
+ RSpec.configure do |config|
11
+ config.around :each do |example|
12
+ ActiveRecord::Base.transaction do
13
+ example.run
14
+
15
+ raise ActiveRecord::Rollback
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ class Item
2
+ include Mongoid::Document
3
+ include Mongoid::HasOrder
4
+
5
+ field :name, type: String
6
+ field :category, type: String
7
+ end
@@ -0,0 +1,12 @@
1
+ require 'mongoid'
2
+ require 'has_order'
3
+
4
+ Mongoid.configure do |config|
5
+ config.connect_to('mongoid_has_order_test')
6
+ end
7
+
8
+ require_relative 'item_model'
9
+
10
+ RSpec.configure do |config|
11
+ config.after(:each) { Mongoid.purge! }
12
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_order
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kolesnikov Danil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-21 00:00:00.000000000 Z
11
+ date: 2014-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,33 +67,47 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1'
69
69
  - !ruby/object:Gem::Dependency
70
- name: simplecov
70
+ name: activerecord
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '4'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '4'
83
83
  - !ruby/object:Gem::Dependency
84
- name: activerecord
84
+ name: mongoid
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '4'
90
- type: :runtime
90
+ type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: codeclimate-test-reporter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: activesupport
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +122,7 @@ dependencies:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: '4'
111
- description: Provides list behavior to active_record models.
125
+ description: Ordering behavior for ActiveRecord models and Mongoid documents.
112
126
  email:
113
127
  - kolesnikovde@gmail.com
114
128
  executables: []
@@ -118,17 +132,24 @@ files:
118
132
  - ".editorconfig"
119
133
  - ".gitignore"
120
134
  - ".rspec"
121
- - CHANGELOG.md
135
+ - ".travis.yml"
122
136
  - Gemfile
123
137
  - README.md
124
138
  - Rakefile
125
139
  - has_order.gemspec
126
140
  - lib/has_order.rb
141
+ - lib/has_order/orm_adapter.rb
142
+ - lib/has_order/orm_adapter/active_record.rb
143
+ - lib/has_order/orm_adapter/mongoid.rb
127
144
  - lib/has_order/version.rb
128
- - spec/db/schema.rb
129
145
  - spec/has_order_spec.rb
146
+ - spec/list.rb
130
147
  - spec/spec_helper.rb
131
148
  - spec/support/models.rb
149
+ - spec/support/orm/activerecord/item_model.rb
150
+ - spec/support/orm/activerecord/setup.rb
151
+ - spec/support/orm/mongoid/item_model.rb
152
+ - spec/support/orm/mongoid/setup.rb
132
153
  homepage: https://github.com/kolesnikovde/has_order
133
154
  licenses:
134
155
  - MIT
@@ -152,9 +173,13 @@ rubyforge_project:
152
173
  rubygems_version: 2.2.2
153
174
  signing_key:
154
175
  specification_version: 4
155
- summary: Provides list behavior to active_record models.
176
+ summary: Ordering behavior for ActiveRecord models and Mongoid documents.
156
177
  test_files:
157
- - spec/db/schema.rb
158
178
  - spec/has_order_spec.rb
179
+ - spec/list.rb
159
180
  - spec/spec_helper.rb
160
181
  - spec/support/models.rb
182
+ - spec/support/orm/activerecord/item_model.rb
183
+ - spec/support/orm/activerecord/setup.rb
184
+ - spec/support/orm/mongoid/item_model.rb
185
+ - spec/support/orm/mongoid/setup.rb
data/CHANGELOG.md DELETED
@@ -1,19 +0,0 @@
1
- # Changelog
2
-
3
- ## 0.1.2
4
-
5
- - Added README.md.
6
- - Updated codestyle.
7
- - Fixed rspec deprication warnings.
8
- - Updated development dependencies.
9
-
10
- ## 0.1.1
11
-
12
- - Fixed scopes (always using lambdas).
13
-
14
- ## 0.1.0
15
-
16
- - Added `#move_to`.
17
- - Added `#shift_interval` option.
18
- - Added `#set_default_position?`.
19
- - Added lambda scopes support.