ranked-model-rails2 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ describe Duck do
4
+
5
+ before {
6
+ 200.times do |i|
7
+ Duck.create \
8
+ :name => "Duck #{i}"
9
+ end
10
+ }
11
+
12
+ describe "setting and fetching by position" do
13
+
14
+ describe '137' do
15
+
16
+ before {
17
+ @last = Duck.last
18
+ @last.update_attribute :row_position, 137
19
+ }
20
+
21
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(137).instance }
22
+
23
+ its(:id) { should == @last.id }
24
+
25
+ end
26
+
27
+ describe '2' do
28
+
29
+ before {
30
+ @last = Duck.last
31
+ @last.update_attribute :row_position, 2
32
+ }
33
+
34
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(2).instance }
35
+
36
+ its(:id) { should == @last.id }
37
+
38
+ end
39
+
40
+ describe 'last' do
41
+
42
+ before {
43
+ @last = Duck.last
44
+ @last.update_attribute :row_position, :last
45
+ }
46
+
47
+ subject { Duck.rank(:row).last }
48
+
49
+ its(:id) { should == @last.id }
50
+
51
+ end
52
+
53
+ describe 'first' do
54
+
55
+ before {
56
+ @last = Duck.last
57
+ @last.update_attribute :row_position, :first
58
+ }
59
+
60
+ subject { Duck.rank(:row).first }
61
+
62
+ its(:id) { should == @last.id }
63
+
64
+ end
65
+
66
+ end
67
+
68
+ describe "a rearrangement" do
69
+
70
+ describe "with max value" do
71
+
72
+ before {
73
+ @first = Duck.first
74
+ @second = Duck.offset(1).first
75
+ @ordered = Duck.rank(:row).where(%Q{"id" NOT IN (#{@first.id}, #{@second.id})}).collect {|d| d.id }
76
+ @first.update_attribute :row, RankedModel::MAX_RANK_VALUE
77
+ @second.update_attribute :row, RankedModel::MAX_RANK_VALUE
78
+ }
79
+
80
+ context {
81
+
82
+ subject { Duck.rank(:row).collect {|d| d.id } }
83
+
84
+ it { should == (@ordered[0..-2] + [@ordered[-1], @first.id, @second.id]) }
85
+
86
+ }
87
+
88
+ end
89
+
90
+ describe "with max value and with_same pond" do
91
+
92
+ before {
93
+ Duck.order("id ASC").limit(50).each_with_index do |d, index|
94
+ d.update_attributes :age => index % 10, :pond => "Pond #{index / 10}"
95
+ end
96
+ @duck_11 = Duck.offset(10).first
97
+ @duck_12 = Duck.offset(11).first
98
+ @ordered = Duck.where(:pond => 'Pond 1').rank(:age).where(%Q{"id" NOT IN (#{@duck_11.id}, #{@duck_12.id})}).collect {|d| d.id }
99
+ @duck_11.update_attribute :age, RankedModel::MAX_RANK_VALUE
100
+ @duck_12.update_attribute :age, RankedModel::MAX_RANK_VALUE
101
+ }
102
+
103
+ context {
104
+ subject { Duck.where(:pond => 'Pond 1').rank(:age).collect {|d| d.id } }
105
+
106
+ it { should == (@ordered[0..-2] + [@ordered[-1], @duck_11.id, @duck_12.id]) }
107
+ }
108
+
109
+ context {
110
+ subject { Duck.first.age }
111
+ it { should == 0}
112
+ }
113
+
114
+ end
115
+
116
+ describe "with min value" do
117
+
118
+ before {
119
+ @first = Duck.first
120
+ @second = Duck.offset(1).first
121
+ @ordered = Duck.rank(:row).where(%Q{"id" NOT IN (#{@first.id}, #{@second.id})}).collect {|d| d.id }
122
+ @first.update_attribute :row, RankedModel::MIN_RANK_VALUE
123
+ @second.update_attribute :row, RankedModel::MIN_RANK_VALUE
124
+ }
125
+
126
+ context {
127
+
128
+ subject { Duck.rank(:row).collect {|d| d.id } }
129
+
130
+ it { should == ([@second.id, @first.id] + @ordered) }
131
+
132
+ }
133
+
134
+ end
135
+
136
+ describe "with no more gaps" do
137
+
138
+ before {
139
+ @first = Duck.first
140
+ @second = Duck.where(:row => RankedModel::MAX_RANK_VALUE).first || Duck.offset(1).first
141
+ @third = Duck.offset(2).first
142
+ @fourth = Duck.offset(4).first
143
+ @lower = Duck.rank(:row).
144
+ where(%Q{"id" NOT IN (#{@first.id}, #{@second.id}, #{@third.id}, #{@fourth.id})}).
145
+ where(%Q{"row" < ?}, RankedModel::MAX_RANK_VALUE / 2).
146
+ collect {|d| d.id }
147
+ @upper = Duck.rank(:row).
148
+ where(%Q{"id" NOT IN (#{@first.id}, #{@second.id}, #{@third.id}, #{@fourth.id})}).
149
+ where(%Q{"row" >= ?}, RankedModel::MAX_RANK_VALUE / 2).
150
+ collect {|d| d.id }
151
+ @first.update_attribute :row, RankedModel::MIN_RANK_VALUE
152
+ @second.update_attribute :row, RankedModel::MAX_RANK_VALUE
153
+ @third.update_attribute :row, (RankedModel::MAX_RANK_VALUE / 2)
154
+ @fourth.update_attribute :row, @third.row
155
+ }
156
+
157
+ context {
158
+
159
+ subject { Duck.rank(:row).collect {|d| d.id } }
160
+
161
+ it { should == ([@first.id] + @lower + [@fourth.id, @third.id] + @upper + [@second.id]) }
162
+
163
+ }
164
+
165
+ end
166
+
167
+ end
168
+
169
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe WrongScopeDuck do
4
+
5
+ it "should raise an error because of an unknown scope" do
6
+
7
+ expect {
8
+ WrongScopeDuck.create(:name => 'Quocky', :pond => 'Shin')
9
+ }.to raise_error(RankedModel::InvalidScope, 'No scope called "non_existant_scope" found in model')
10
+
11
+ end
12
+
13
+ end
14
+
15
+ describe WrongFieldDuck do
16
+
17
+ it "should raise an error because of an unknown field" do
18
+
19
+ expect {
20
+ WrongFieldDuck.create(:name => 'Quicky', :pond => 'Shin')
21
+ }.to raise_error(RankedModel::InvalidField, 'No field called "non_existant_field" found in model')
22
+
23
+ end
24
+
25
+ end
26
+
27
+ describe ReallyWrongFieldDuck do
28
+
29
+ it "should raise an error because of a specific unknown field" do
30
+
31
+ expect {
32
+ ReallyWrongFieldDuck.create(:name => 'Quicky', :pond => 'Shin')
33
+ }.to raise_error(RankedModel::InvalidField, 'No field called "non_existant_field" found in model')
34
+ end
35
+
36
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ego do
4
+
5
+ before {
6
+ @egos = {
7
+ :bob => Ego.create(:name => 'Bob'),
8
+ :nick => Ego.create(:name => 'Nick'),
9
+ :sally => Ego.create(:name => 'Sally')
10
+ }
11
+ @egos.each { |name, ego|
12
+ ego.reload
13
+ ego.update_attribute :size_position, 0
14
+ ego.save!
15
+ }
16
+ @egos.each {|name, ego| ego.reload }
17
+ }
18
+
19
+ describe "sorting on size alternative primary key" do
20
+
21
+ before {
22
+ @egos[:nick].update_attribute :size_position, 0
23
+ @egos[:sally].update_attribute :size_position, 2
24
+ }
25
+
26
+ subject { Ego.rank(:size).to_a }
27
+
28
+ its(:size) { should == 3 }
29
+
30
+ its(:first) { should == @egos[:nick] }
31
+
32
+ its(:last) { should == @egos[:sally] }
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Player do
4
+
5
+ before {
6
+ @players = {}
7
+ @players[:dave] = Player.create!(:name => "Dave", :city => "Detroit")
8
+ @players[:bob] = Player.create!(:name => "Bob", :city => "Portland")
9
+ @players[:nigel] = Player.create!(:name => "Nigel", :city => "New York")
10
+ Player.class_eval do
11
+ include RankedModel
12
+ ranks :score
13
+ end
14
+
15
+ }
16
+
17
+ describe "setting the position of a record that already exists" do
18
+ it "sets the rank without error" do
19
+ expect{@players[:bob].update_attributes! :score_position => 1}.to_not raise_error
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe RankedModel::Ranker, 'initialized' do
4
+
5
+ subject {
6
+ RankedModel::Ranker.new \
7
+ :overview,
8
+ :column => :a_sorting_column,
9
+ :scope => :a_scope,
10
+ :with_same => :a_column,
11
+ :class_name => 'SomeClass',
12
+ :unless => :a_method
13
+ }
14
+
15
+ its(:name) { should == :overview }
16
+ its(:column) { should == :a_sorting_column }
17
+ its(:scope) { should == :a_scope }
18
+ its(:with_same) { should == :a_column }
19
+ its(:class_name) { should == 'SomeClass' }
20
+ its(:unless) { should == :a_method }
21
+ end
22
+
23
+ describe RankedModel::Ranker, 'unless as Symbol' do
24
+ let(:receiver) { mock('model') }
25
+
26
+ subject {
27
+ RankedModel::Ranker.new(:overview, :unless => :a_method).with(receiver)
28
+ }
29
+
30
+ context 'returns true' do
31
+ before { receiver.expects(:a_method).once.returns(true) }
32
+
33
+ its(:handle_ranking) { should == nil }
34
+ end
35
+
36
+ context 'returns false' do
37
+ before { receiver.expects(:a_method).once.returns(false) }
38
+
39
+ it {
40
+ subject.expects(:update_index_from_position).once
41
+ subject.expects(:assure_unique_position).once
42
+
43
+ subject.handle_ranking
44
+ }
45
+ end
46
+ end
47
+
48
+ describe RankedModel::Ranker, 'unless as Proc' do
49
+ context 'returns true' do
50
+ subject { RankedModel::Ranker.new(:overview, :unless => Proc.new { true }).with(Class.new) }
51
+ its(:handle_ranking) { should == nil }
52
+ end
53
+
54
+ context 'returns false' do
55
+ subject { RankedModel::Ranker.new(:overview, :unless => Proc.new { false }).with(Class.new) }
56
+
57
+ it {
58
+ subject.expects(:update_index_from_position).once
59
+ subject.expects(:assure_unique_position).once
60
+
61
+ subject.handle_ranking
62
+ }
63
+ end
64
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe RankedModel do
4
+
5
+ it { should define_constant(:VERSION) }
6
+
7
+ end
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'ranked-model'
5
+
6
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each {|f| require f}
7
+
8
+ # After the DB connection is setup
9
+ require 'database_cleaner'
10
+
11
+ RSpec.configure do |config|
12
+ config.mock_with :mocha
13
+
14
+ config.before(:suite) do
15
+ DatabaseCleaner.strategy = :transaction
16
+ DatabaseCleaner.clean_with(:truncation)
17
+ end
18
+
19
+ config.before(:each) do
20
+ DatabaseCleaner.start
21
+ end
22
+
23
+ config.after(:each) do
24
+ DatabaseCleaner.clean
25
+ end
26
+
27
+ end
28
+
29
+ RSpec::Matchers.define :define_constant do |expected|
30
+ match { |actual| actual.const_defined?(expected) }
31
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe Element do
4
+
5
+ before {
6
+ @elements = {
7
+ :chromium => TransitionMetal.create( :symbol => 'Cr' ),
8
+ :manganese => TransitionMetal.create( :symbol => 'Mn' ),
9
+ :argon => NobleGas.create( :symbol => 'Ar' ),
10
+ :helium => NobleGas.create( :symbol => 'He' ),
11
+ :xenon => NobleGas.create( :symbol => 'Xe' )
12
+ }
13
+ @elements.each { |name, element|
14
+ element.reload
15
+ element.update_attribute :combination_order_position, 0
16
+ }
17
+ @elements.each {|name, element| element.reload }
18
+ }
19
+
20
+ describe "rebalancing on an STI class should not affect the other class" do
21
+
22
+ before {
23
+ @elements[:helium].update_attribute :combination_order_position, :first
24
+ @elements[:xenon].update_attribute :combination_order_position, :first
25
+ @elements[:argon].update_attribute :combination_order_position, :last
26
+
27
+ TransitionMetal.ranker(:combination_order).with(@elements[:chromium]).instance_eval { rebalance_ranks }
28
+ }
29
+
30
+ subject { NobleGas.rank(:combination_order) }
31
+
32
+ its(:size) { should == 3 }
33
+
34
+ its(:first) { should == @elements[:xenon] }
35
+
36
+ its(:last) { should == @elements[:argon] }
37
+
38
+ end
39
+
40
+ describe "setting positions on STI classes" do
41
+
42
+ before {
43
+ @elements[:helium].update_attribute :combination_order_position, :first
44
+ @elements[:xenon].update_attribute :combination_order_position, :first
45
+ @elements[:argon].update_attribute :combination_order_position, :first
46
+
47
+ @elements[:chromium].update_attribute :combination_order_position, 1
48
+ @elements[:manganese].update_attribute :combination_order_position, 1
49
+ @elements[:manganese].update_attribute :combination_order_position, 0
50
+ @elements[:chromium].update_attribute :combination_order_position, 0
51
+ @elements[:manganese].update_attribute :combination_order_position, 0
52
+ @elements[:chromium].update_attribute :combination_order_position, 0
53
+ }
54
+
55
+ describe "NobleGas" do
56
+
57
+ subject { NobleGas.rank(:combination_order) }
58
+
59
+ its(:size) { should == 3 }
60
+
61
+ its(:first) { should == @elements[:argon] }
62
+
63
+ its(:last) { should == @elements[:helium] }
64
+
65
+ end
66
+
67
+ describe "TransitionMetal" do
68
+
69
+ subject { TransitionMetal.rank(:combination_order) }
70
+
71
+ its(:size) { should == 2 }
72
+
73
+ its(:first) { should == @elements[:chromium] }
74
+
75
+ its(:last) { should == @elements[:manganese] }
76
+
77
+ end
78
+
79
+ end
80
+
81
+ describe "setting positions on STI classes" do
82
+
83
+ before {
84
+ @elements[:helium].update_attribute :combination_order_position, :first
85
+ @elements[:xenon].update_attribute :combination_order_position, :first
86
+ @elements[:argon].update_attribute :combination_order_position, :first
87
+
88
+ @elements[:chromium].update_attribute :combination_order_position, 1
89
+ @elements[:manganese].update_attribute :combination_order_position, 1
90
+ @elements[:manganese].update_attribute :combination_order_position, 0
91
+ @elements[:chromium].update_attribute :combination_order_position, 0
92
+ @elements[:manganese].update_attribute :combination_order_position, 0
93
+ @elements[:chromium].update_attribute :combination_order_position, 0
94
+ }
95
+
96
+ describe "NobleGas" do
97
+
98
+ subject { NobleGas.rank(:combination_order) }
99
+
100
+ its(:size) { should == 3 }
101
+
102
+ its(:first) { should == @elements[:argon] }
103
+
104
+ its(:last) { should == @elements[:helium] }
105
+
106
+ end
107
+
108
+ describe "TransitionMetal" do
109
+
110
+ subject { TransitionMetal.rank(:combination_order) }
111
+
112
+ its(:size) { should == 2 }
113
+
114
+ its(:first) { should == @elements[:chromium] }
115
+
116
+ its(:last) { should == @elements[:manganese] }
117
+
118
+ end
119
+
120
+ end
121
+
122
+ end