has_order 0.1.2 → 0.2.0

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.
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.