ranked-model 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+
5
+ Gemfile.lock
data/Readme.mkd CHANGED
@@ -1,4 +1,4 @@
1
- **ranked-model** is a modern row sorting library built for Rails 3. It uses ARel aggressivly and is better optimized than most other libraries.
1
+ **ranked-model** is a modern row sorting library built for Rails 3. It uses ARel aggressively and is better optimized than most other libraries.
2
2
 
3
3
  Installation
4
4
  ------------
@@ -5,7 +5,8 @@ module RankedModel
5
5
 
6
6
  # Signed MEDIUMINT in MySQL
7
7
  #
8
- MAX_RANK_VALUE = 65534
8
+ MAX_RANK_VALUE = 8388607
9
+ MIN_RANK_VALUE = -8388607
9
10
 
10
11
  def self.included base
11
12
 
@@ -1,5 +1,8 @@
1
1
  module RankedModel
2
2
 
3
+ class InvalidScope < StandardError; end
4
+ class InvalidField < StandardError; end
5
+
3
6
  class Ranker
4
7
  attr_accessor :name, :column, :scope, :with_same
5
8
 
@@ -22,12 +25,24 @@ module RankedModel
22
25
  def initialize ranker, instance
23
26
  self.ranker = ranker
24
27
  self.instance = instance
28
+
29
+ validate_ranker_for_instance!
25
30
  end
26
31
 
32
+ def validate_ranker_for_instance!
33
+ if ranker.scope && !instance.class.respond_to?(ranker.scope)
34
+ raise RankedModel::InvalidScope, %Q{No scope called "#{ranker.scope}" found in model}
35
+ end
36
+
37
+ if ranker.with_same && !instance.respond_to?(ranker.with_same)
38
+ raise RankedModel::InvalidField, %Q{No field called "#{ranker.with_same}" found in model}
39
+ end
40
+ end
41
+
27
42
  def handle_ranking
28
43
  update_index_from_position
29
44
  assure_unique_position
30
- end
45
+ end
31
46
 
32
47
  def update_rank! value
33
48
  # Bypass callbacks
@@ -43,6 +58,12 @@ module RankedModel
43
58
  instance.send "#{ranker.column}"
44
59
  end
45
60
 
61
+ def current_at_position _pos
62
+ if (ordered_instance = finder.offset(_pos).first)
63
+ RankedModel::Ranker::Mapper.new ranker, ordered_instance
64
+ end
65
+ end
66
+
46
67
  private
47
68
 
48
69
  def position_at value
@@ -65,25 +86,30 @@ module RankedModel
65
86
  def update_index_from_position
66
87
  case position
67
88
  when :first
68
- if !current_order.empty? && current_order.first.rank
69
- rank_at( current_order.first.rank / 2 )
89
+ if current_first && current_first.rank
90
+ rank_at( ( ( RankedModel::MIN_RANK_VALUE - current_first.rank ).to_f / 2 ).ceil + current_first.rank)
70
91
  else
71
- rank_at 0
92
+ position_at :middle
72
93
  end
73
94
  when :last
74
- if !current_order.empty? && current_order.last.rank
75
- rank_at( ( RankedModel::MAX_RANK_VALUE + current_order.last.rank ) / 2 )
95
+ if current_last && current_last.rank
96
+ rank_at( ( ( RankedModel::MAX_RANK_VALUE - current_last.rank ).to_f / 2 ).ceil + current_last.rank )
76
97
  else
77
- rank_at RankedModel::MAX_RANK_VALUE
98
+ position_at :middle
78
99
  end
100
+ when :middle
101
+ rank_at( ( ( RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE ).to_f / 2 ).ceil + RankedModel::MIN_RANK_VALUE )
79
102
  when String
80
103
  position_at position.to_i
81
104
  when 0
82
105
  position_at :first
83
106
  when Integer
84
- if current_order[position]
85
- rank_at( ( current_order[position-1].rank + current_order[position].rank ) / 2 )
86
- else
107
+ neighbors = neighbors_at_position(position)
108
+ min = (neighbors[:lower] ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE)
109
+ max = (neighbors[:upper] ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE)
110
+ rank_at( ( ( max - min ).to_f / 2 ).ceil + min )
111
+ when NilClass
112
+ if !rank
87
113
  position_at :last
88
114
  end
89
115
  end
@@ -92,24 +118,38 @@ module RankedModel
92
118
  def assure_unique_position
93
119
  if ( new_record? || rank_changed? )
94
120
  unless rank
95
- rank_at RankedModel::MAX_RANK_VALUE
121
+ rank_at( RankedModel::MAX_RANK_VALUE )
96
122
  end
97
123
 
98
- if (rank > RankedModel::MAX_RANK_VALUE) || (current_order.find do |rankable|
99
- rankable.rank.nil? ||
100
- rankable.rank == rank
101
- end)
102
- rebalance_ranks
124
+ if (rank > RankedModel::MAX_RANK_VALUE) || current_at_rank(rank)
125
+ rearrange_ranks
103
126
  end
104
127
  end
105
128
  end
106
-
129
+
130
+ def rearrange_ranks
131
+ if current_last.rank < (RankedModel::MAX_RANK_VALUE - 1) && rank < current_last.rank
132
+ instance.class.
133
+ where( instance.class.arel_table[:id].not_eq(instance.id) ).
134
+ where( instance.class.arel_table[ranker.column].gteq(rank) ).
135
+ update_all( "#{ranker.column} = #{ranker.column} + 1" )
136
+ elsif current_first.rank > RankedModel::MIN_RANK_VALUE && rank > current_first.rank
137
+ instance.class.
138
+ where( instance.class.arel_table[:id].not_eq(instance.id) ).
139
+ where( instance.class.arel_table[ranker.column].lt(rank) ).
140
+ update_all( "#{ranker.column} = #{ranker.column} - 1" )
141
+ rank_at( rank - 1 )
142
+ else
143
+ rebalance_ranks
144
+ end
145
+ end
146
+
107
147
  def rebalance_ranks
108
148
  total = current_order.size + 2
109
149
  has_set_self = false
110
150
  total.times do |index|
111
151
  next if index == 0 || index == total
112
- rank_value = RankedModel::MAX_RANK_VALUE / total * index
152
+ rank_value = ((((RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE).to_f / total) * index ).ceil + RankedModel::MIN_RANK_VALUE)
113
153
  index = index - 1
114
154
  if has_set_self
115
155
  index = index - 1
@@ -126,26 +166,81 @@ module RankedModel
126
166
  end
127
167
  end
128
168
 
129
- def current_order
130
- @current_order ||= begin
131
- finder = instance.class
169
+ def finder
170
+ @finder ||= begin
171
+ _finder = instance.class
132
172
  if ranker.scope
133
- finder = finder.send ranker.scope
173
+ _finder = _finder.send ranker.scope
134
174
  end
135
175
  if ranker.with_same
136
- finder = finder.where \
176
+ _finder = _finder.where \
137
177
  instance.class.arel_table[ranker.with_same].eq(instance.attributes["#{ranker.with_same}"])
138
178
  end
139
179
  if !new_record?
140
- finder = finder.where \
180
+ _finder = _finder.where \
141
181
  instance.class.arel_table[:id].not_eq(instance.id)
142
182
  end
143
- finder.order(ranker.column).select([:id, ranker.column]).collect { |ordered_instance|
183
+ _finder.order(instance.class.arel_table[ranker.column].asc).select([:id, ranker.column])
184
+ end
185
+ end
186
+
187
+ def current_order
188
+ @current_order ||= begin
189
+ finder.collect { |ordered_instance|
144
190
  RankedModel::Ranker::Mapper.new ranker, ordered_instance
145
191
  }
146
192
  end
147
193
  end
148
194
 
195
+ def current_first
196
+ @current_first ||= begin
197
+ if (ordered_instance = finder.first)
198
+ RankedModel::Ranker::Mapper.new ranker, ordered_instance
199
+ end
200
+ end
201
+ end
202
+
203
+ def current_last
204
+ @current_last ||= begin
205
+ if (ordered_instance = finder.
206
+ except( :order ).
207
+ order( instance.class.arel_table[ranker.column].desc ).
208
+ first)
209
+ RankedModel::Ranker::Mapper.new ranker, ordered_instance
210
+ end
211
+ end
212
+ end
213
+
214
+ def current_at_rank _rank
215
+ if (ordered_instance = finder.
216
+ except( :order ).
217
+ where( ranker.column => _rank ).
218
+ first)
219
+ RankedModel::Ranker::Mapper.new ranker, ordered_instance
220
+ end
221
+ end
222
+
223
+ def neighbors_at_position _pos
224
+ if _pos > 0
225
+ if (ordered_instances = finder.offset(_pos-1).limit(2).all)
226
+ if ordered_instances[1]
227
+ { :lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ),
228
+ :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[1] ) }
229
+ elsif ordered_instances[0]
230
+ { :lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ) }
231
+ else
232
+ { :lower => current_last }
233
+ end
234
+ end
235
+ else
236
+ if (ordered_instance = finder.first)
237
+ { :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instance ) }
238
+ else
239
+ {}
240
+ end
241
+ end
242
+ end
243
+
149
244
  end
150
245
 
151
246
  end
@@ -1,3 +1,3 @@
1
1
  module RankedModel
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.email = ["matt.beale@madhatted.com"]
11
11
  s.homepage = "https://github.com/harvesthq/ranked-model"
12
12
  s.summary = %q{An acts_as_sortable replacement built for Rails 3}
13
- s.description = %q{ranked-model is a modern row sorting library built for Rails 3. It uses ARel aggressivly and is better optimized than most other libraries.}
13
+ s.description = %q{ranked-model is a modern row sorting library built for Rails 3. It uses ARel aggressively and is better optimized than most other libraries.}
14
14
 
15
15
  s.add_dependency "activerecord", ">= 3.0.3"
16
16
  s.add_development_dependency "rspec"
@@ -25,9 +25,9 @@ describe Duck do
25
25
  }
26
26
  @ducks.each { |name, duck|
27
27
  duck.reload
28
- duck.row_position = 0
29
- duck.size_position = 0
30
- duck.age_position = 0
28
+ duck.update_attribute :row_position, 0
29
+ duck.update_attribute :size_position, 0
30
+ duck.update_attribute :age_position, 0
31
31
  duck.save!
32
32
  }
33
33
  @ducks.each {|name, duck| duck.reload }
@@ -124,4 +124,150 @@ describe Duck do
124
124
 
125
125
  end
126
126
 
127
+ describe "setting and fetching by positioning" do
128
+
129
+ describe "in the middle" do
130
+
131
+ before {
132
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
133
+ @ducks[:wingy].update_attribute :row_position, 2
134
+ }
135
+
136
+ context {
137
+
138
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(2).instance }
139
+
140
+ its(:id) { should == @ducks[:wingy].id }
141
+
142
+ }
143
+
144
+ context {
145
+
146
+ subject { Duck.rank(:row).collect {|duck| duck.id } }
147
+
148
+ it { subject[0..1].should == @ordered[0..1] }
149
+
150
+ it { subject[3..subject.length].should == @ordered[2..@ordered.length] }
151
+
152
+ }
153
+
154
+ end
155
+
156
+ describe "at the start" do
157
+
158
+ before {
159
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
160
+ @ducks[:wingy].update_attribute :row_position, 0
161
+ }
162
+
163
+ context {
164
+
165
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(0).instance }
166
+
167
+ its(:id) { should == @ducks[:wingy].id }
168
+
169
+ }
170
+
171
+ context {
172
+
173
+ subject { Duck.ranker(:row).with(Duck.new).instance_eval { current_first }.instance }
174
+
175
+ its(:id) { should == @ducks[:wingy].id }
176
+
177
+ }
178
+
179
+ context {
180
+
181
+ subject { Duck.rank(:row).collect {|duck| duck.id } }
182
+
183
+ it { subject[1..subject.length].should == @ordered }
184
+
185
+ }
186
+
187
+ end
188
+
189
+ describe "at the end" do
190
+
191
+ before {
192
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
193
+ @ducks[:wingy].update_attribute :row_position, (@ducks.size - 1)
194
+ }
195
+
196
+ context {
197
+
198
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
199
+
200
+ its(:id) { should == @ducks[:wingy].id }
201
+
202
+ }
203
+
204
+ context {
205
+
206
+ subject { Duck.rank(:row).last }
207
+
208
+ its(:id) { should == @ducks[:wingy].id }
209
+
210
+ }
211
+
212
+ context {
213
+
214
+ subject { Duck.ranker(:row).with(Duck.new).instance_eval { current_last }.instance }
215
+
216
+ its(:id) { should == @ducks[:wingy].id }
217
+
218
+ }
219
+
220
+ context {
221
+
222
+ subject { Duck.rank(:row).collect {|duck| duck.id } }
223
+
224
+ it { subject[0..-2].should == @ordered }
225
+
226
+ }
227
+
228
+ end
229
+
230
+ describe "at the end with symbol" do
231
+
232
+ before {
233
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
234
+ @ducks[:wingy].update_attribute :row_position, :last
235
+ }
236
+
237
+ context {
238
+
239
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
240
+
241
+ its(:id) { should == @ducks[:wingy].id }
242
+
243
+ }
244
+
245
+ context {
246
+
247
+ subject { Duck.rank(:row).last }
248
+
249
+ its(:id) { should == @ducks[:wingy].id }
250
+
251
+ }
252
+
253
+ context {
254
+
255
+ subject { Duck.ranker(:row).with(Duck.new).instance_eval { current_last }.instance }
256
+
257
+ its(:id) { should == @ducks[:wingy].id }
258
+
259
+ }
260
+
261
+ context {
262
+
263
+ subject { Duck.rank(:row).collect {|duck| duck.id } }
264
+
265
+ it { subject[0..-2].should == @ordered }
266
+
267
+ }
268
+
269
+ end
270
+
271
+ end
272
+
127
273
  end
@@ -0,0 +1,117 @@
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
+ end
41
+
42
+ describe "a rearrangement" do
43
+
44
+ describe "with max value" do
45
+
46
+ before {
47
+ @first = Duck.first
48
+ @second = Duck.offset(1).first
49
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_in([@first.id, @second.id])).collect {|d| d.id }
50
+ @first.update_attribute :row, RankedModel::MAX_RANK_VALUE
51
+ @second.update_attribute :row, RankedModel::MAX_RANK_VALUE
52
+ }
53
+
54
+ context {
55
+
56
+ subject { Duck.rank(:row).collect {|d| d.id } }
57
+
58
+ it { should == (@ordered[0..-2] + [@first.id, @second.id, @ordered[-1]]) }
59
+
60
+ }
61
+
62
+ end
63
+
64
+ describe "with min value" do
65
+
66
+ before {
67
+ @first = Duck.first
68
+ @second = Duck.offset(1).first
69
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_in([@first.id, @second.id])).collect {|d| d.id }
70
+ @first.update_attribute :row, RankedModel::MIN_RANK_VALUE
71
+ @second.update_attribute :row, RankedModel::MIN_RANK_VALUE
72
+ }
73
+
74
+ context {
75
+
76
+ subject { Duck.rank(:row).collect {|d| d.id } }
77
+
78
+ it { should == ([@second.id, @first.id] + @ordered) }
79
+
80
+ }
81
+
82
+ end
83
+
84
+ describe "with no more gaps" do
85
+
86
+ before {
87
+ @first = Duck.first
88
+ @second = Duck.where(:row => RankedModel::MAX_RANK_VALUE).first || Duck.offset(1).first
89
+ @third = Duck.offset(2).first
90
+ @fourth = Duck.offset(4).first
91
+ @lower = Duck.rank(:row).
92
+ where(Duck.arel_table[:id].not_in([@first.id, @second.id, @third.id, @fourth.id])).
93
+ where(Duck.arel_table[:row].lt(RankedModel::MAX_RANK_VALUE / 2)).
94
+ collect {|d| d.id }
95
+ @upper = Duck.rank(:row).
96
+ where(Duck.arel_table[:id].not_in([@first.id, @second.id, @third.id, @fourth.id])).
97
+ where(Duck.arel_table[:row].gteq(RankedModel::MAX_RANK_VALUE / 2)).
98
+ collect {|d| d.id }
99
+ @first.update_attribute :row, RankedModel::MIN_RANK_VALUE
100
+ @second.update_attribute :row, RankedModel::MAX_RANK_VALUE
101
+ @third.update_attribute :row, (RankedModel::MAX_RANK_VALUE / 2)
102
+ @fourth.update_attribute :row, @third.row
103
+ }
104
+
105
+ context {
106
+
107
+ subject { Duck.rank(:row).collect {|d| d.id } }
108
+
109
+ it { should == ([@first.id] + @lower + [@fourth.id, @third.id] + @upper + [@second.id]) }
110
+
111
+ }
112
+
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,25 @@
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
@@ -18,6 +18,18 @@ ActiveRecord::Schema.define :version => 0 do
18
18
  t.integer :age
19
19
  t.string :pond
20
20
  end
21
+
22
+ create_table :wrong_scope_ducks, :force => true do |t|
23
+ t.string :name
24
+ t.integer :size
25
+ t.string :pond
26
+ end
27
+
28
+ create_table :wrong_field_ducks, :force => true do |t|
29
+ t.string :name
30
+ t.integer :age
31
+ t.string :pond
32
+ end
21
33
  end
22
34
 
23
35
  class Duck < ActiveRecord::Base
@@ -30,3 +42,19 @@ class Duck < ActiveRecord::Base
30
42
  scope :in_shin_pond, where(:pond => 'Shin')
31
43
 
32
44
  end
45
+
46
+ # Negative examples
47
+
48
+ class WrongScopeDuck < ActiveRecord::Base
49
+
50
+ include RankedModel
51
+ ranks :size, :scope => :non_existant_scope
52
+
53
+ end
54
+
55
+ class WrongFieldDuck < ActiveRecord::Base
56
+
57
+ include RankedModel
58
+ ranks :age, :with_same => :non_existant_field
59
+
60
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ranked-model
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matthew Beale
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-08 00:00:00 -05:00
18
+ date: 2011-04-14 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -104,7 +104,7 @@ dependencies:
104
104
  version: "0"
105
105
  type: :development
106
106
  version_requirements: *id006
107
- description: ranked-model is a modern row sorting library built for Rails 3. It uses ARel aggressivly and is better optimized than most other libraries.
107
+ description: ranked-model is a modern row sorting library built for Rails 3. It uses ARel aggressively and is better optimized than most other libraries.
108
108
  email:
109
109
  - matt.beale@madhatted.com
110
110
  executables: []
@@ -117,7 +117,6 @@ files:
117
117
  - .gitignore
118
118
  - .rspec
119
119
  - Gemfile
120
- - Gemfile.lock
121
120
  - LICENSE
122
121
  - Rakefile
123
122
  - Readme.mkd
@@ -128,6 +127,8 @@ files:
128
127
  - rails/init.rb
129
128
  - ranked-model.gemspec
130
129
  - spec/duck-model/duck_spec.rb
130
+ - spec/duck-model/lots_of_ducks_spec.rb
131
+ - spec/duck-model/wrong_ducks_spec.rb
131
132
  - spec/ranked-model/ranker_spec.rb
132
133
  - spec/ranked-model/version_spec.rb
133
134
  - spec/spec_helper.rb
@@ -170,6 +171,8 @@ specification_version: 3
170
171
  summary: An acts_as_sortable replacement built for Rails 3
171
172
  test_files:
172
173
  - spec/duck-model/duck_spec.rb
174
+ - spec/duck-model/lots_of_ducks_spec.rb
175
+ - spec/duck-model/wrong_ducks_spec.rb
173
176
  - spec/ranked-model/ranker_spec.rb
174
177
  - spec/ranked-model/version_spec.rb
175
178
  - spec/spec_helper.rb
@@ -1,81 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- ranked-model (0.0.1)
5
- activerecord (>= 3.0.3)
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- abstract (1.0.0)
11
- actionpack (3.0.3)
12
- activemodel (= 3.0.3)
13
- activesupport (= 3.0.3)
14
- builder (~> 2.1.2)
15
- erubis (~> 2.6.6)
16
- i18n (~> 0.4)
17
- rack (~> 1.2.1)
18
- rack-mount (~> 0.6.13)
19
- rack-test (~> 0.5.6)
20
- tzinfo (~> 0.3.23)
21
- activemodel (3.0.3)
22
- activesupport (= 3.0.3)
23
- builder (~> 2.1.2)
24
- i18n (~> 0.4)
25
- activerecord (3.0.3)
26
- activemodel (= 3.0.3)
27
- activesupport (= 3.0.3)
28
- arel (~> 2.0.2)
29
- tzinfo (~> 0.3.23)
30
- activesupport (3.0.3)
31
- arel (2.0.7)
32
- builder (2.1.2)
33
- diff-lcs (1.1.2)
34
- erubis (2.6.6)
35
- abstract (>= 1.0.0)
36
- genspec (0.1.1)
37
- rspec
38
- sc-core-ext (>= 1.2.0)
39
- i18n (0.5.0)
40
- mocha (0.9.10)
41
- rake
42
- rack (1.2.1)
43
- rack-mount (0.6.13)
44
- rack (>= 1.0.0)
45
- rack-test (0.5.7)
46
- rack (>= 1.0)
47
- railties (3.0.3)
48
- actionpack (= 3.0.3)
49
- activesupport (= 3.0.3)
50
- rake (>= 0.8.7)
51
- thor (~> 0.14.4)
52
- rake (0.8.7)
53
- rspec (2.4.0)
54
- rspec-core (~> 2.4.0)
55
- rspec-expectations (~> 2.4.0)
56
- rspec-mocks (~> 2.4.0)
57
- rspec-core (2.4.0)
58
- rspec-expectations (2.4.0)
59
- diff-lcs (~> 1.1.2)
60
- rspec-mocks (2.4.0)
61
- rspec-rails (2.4.1)
62
- actionpack (~> 3.0)
63
- activesupport (~> 3.0)
64
- railties (~> 3.0)
65
- rspec (~> 2.4.0)
66
- sc-core-ext (1.2.1)
67
- activesupport (>= 2.3.5)
68
- sqlite3 (1.3.3)
69
- thor (0.14.6)
70
- tzinfo (0.3.24)
71
-
72
- PLATFORMS
73
- ruby
74
-
75
- DEPENDENCIES
76
- genspec
77
- mocha
78
- ranked-model!
79
- rspec
80
- rspec-rails
81
- sqlite3