ranked-model 0.0.1 → 0.0.2

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