ranked-model 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ranked-model.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,81 @@
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
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Iridesco LLC (support@harvestapp.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ task :default => :spec
data/Readme.mkd ADDED
@@ -0,0 +1,98 @@
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.
2
+
3
+ Installation
4
+ ------------
5
+
6
+ To install ranked-model, just add it to your `Gemfile`:
7
+
8
+ gem 'ranked-model'
9
+
10
+ # Or pin ranked-model to git
11
+ # gem 'ranked-model',
12
+ # :git => 'git@github.com:harvesthq/ranked-model.git'
13
+
14
+ Then use `bundle install` to update your `Gemfile.lock`.
15
+
16
+ Simple Use
17
+ ----------
18
+
19
+ Use of ranked-model is straight ahead. Get some ducks:
20
+
21
+ class Duck < ActiveRecord::Base
22
+ end
23
+
24
+ Put your ducks in a row:
25
+
26
+ class Duck < ActiveRecord::Base
27
+
28
+ include RankedModel
29
+ ranks :row_order
30
+
31
+ end
32
+
33
+ This simple example assumes an integer column called `row_order`. To order Ducks by this order:
34
+
35
+ Duck.rank(:row_order).all
36
+
37
+ The ranking integers stored in the `raw_order` column will be big and spaced apart. When you
38
+ implement a sorting UI, just update the resource with the position instead:
39
+
40
+ @duck.update_attribute :row_order_position, 0 # or 1, 2, 37. :first and :last are also valid
41
+
42
+ So using a normal json controller where `@duck.attributes = params[:duck]; @duck.save`, JS can
43
+ look pretty elegant:
44
+
45
+ $.ajax({
46
+ type: 'PUT',
47
+ url: '/ducks',
48
+ dataType: 'json',
49
+ data: { duck: { row_order_position: 0 } }, // or whatever your new position is
50
+ });
51
+
52
+ Complex Use
53
+ -----------
54
+
55
+ The `ranks` method takes serveral arguments:
56
+
57
+ class Duck < ActiveRecord::Base
58
+
59
+ include RankedModel
60
+
61
+ ranks :row_order, # Name this ranker, used with rank()
62
+ :column => :sort_order # Override the default column, which defaults to the name
63
+
64
+ belongs_to :pond
65
+ ranks :swimming_order,
66
+ :with_same => :pond_id # Ducks belong_to Ponds, make the ranker scoped to one pond
67
+
68
+ scope :walking, where(:walking => true )
69
+ ranks :walking_order,
70
+ :scope => :walking # Narrow this ranker to a scope
71
+
72
+ end
73
+
74
+ When you make a query, add the rank:
75
+
76
+ Duck.rank(:row_order)
77
+
78
+ Pond.first.ducks.rank(:swimming_order)
79
+
80
+ Duck.walking.rank(:walking)
81
+
82
+ Internals
83
+ ---------
84
+
85
+ This libarary is written using ARel from the ground-up. This leaves the code much cleaner
86
+ than many implementations. ranked-model is also optimized to write to the database as little
87
+ as possible: ranks are stored as a number between 0 and 65534 (just below MEDIUMINT in MySQL).
88
+ When an item is given a new position, it assigns itself a rank number between two neighbors.
89
+ This allows several movements of items before no digits are available between to neighbors. When
90
+ this occurs, ranked-model will rebalance the distribution of rank numbers across all memebers
91
+ of the ranked group.
92
+
93
+ Contributing
94
+ ------------
95
+
96
+ Fork, clone, write a test, write some code, commit, push, send a pull request. Github FTW!
97
+
98
+ This project was open-sourced by [Harvest](http://getharvest.com/). [We're hiring!](http://www.getharvest.com/careers)
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__)+'/ranked-model/ranker'
2
+ require File.dirname(__FILE__)+'/ranked-model/railtie' if defined?(Rails::Railtie)
3
+
4
+ module RankedModel
5
+
6
+ # Signed MEDIUMINT in MySQL
7
+ #
8
+ MAX_RANK_VALUE = 65534
9
+
10
+ def self.included base
11
+
12
+ base.class_eval do
13
+ cattr_accessor :rankers
14
+
15
+ extend RankedModel::ClassMethods
16
+
17
+ before_save :handle_ranking
18
+
19
+ scope :rank, lambda { |name|
20
+ order arel_table[ ranker(name.to_sym).column ]
21
+ }
22
+ end
23
+
24
+ end
25
+
26
+ private
27
+
28
+ def handle_ranking
29
+ self.class.rankers.each do |ranker|
30
+ ranker.with(self).handle_ranking
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+
36
+ def ranker name
37
+ rankers.find do |ranker|
38
+ ranker.name == name
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def ranks *args
45
+ self.rankers ||= []
46
+ ranker = RankedModel::Ranker.new(*args)
47
+ self.rankers << ranker
48
+ attr_accessor "#{ranker.name}_position"
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,14 @@
1
+ require 'ranked-model'
2
+ require 'rails'
3
+
4
+ module RankedModel
5
+
6
+ class Railtie < Rails::Railtie
7
+
8
+ initializer "ranked-model.initialize" do |app|
9
+
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,153 @@
1
+ module RankedModel
2
+
3
+ class Ranker
4
+ attr_accessor :name, :column, :scope, :with_same
5
+
6
+ def initialize name, options={}
7
+ self.name = name.to_sym
8
+ self.column = options[:column] || name
9
+
10
+ [ :scope, :with_same ].each do |key|
11
+ self.send "#{key}=", options[key]
12
+ end
13
+ end
14
+
15
+ def with instance
16
+ Mapper.new self, instance
17
+ end
18
+
19
+ class Mapper
20
+ attr_accessor :ranker, :instance
21
+
22
+ def initialize ranker, instance
23
+ self.ranker = ranker
24
+ self.instance = instance
25
+ end
26
+
27
+ def handle_ranking
28
+ update_index_from_position
29
+ assure_unique_position
30
+ end
31
+
32
+ def update_rank! value
33
+ # Bypass callbacks
34
+ #
35
+ instance.class.where(:id => instance.id).update_all ["#{ranker.column} = ?", value]
36
+ end
37
+
38
+ def position
39
+ instance.send "#{ranker.name}_position"
40
+ end
41
+
42
+ def rank
43
+ instance.send "#{ranker.column}"
44
+ end
45
+
46
+ private
47
+
48
+ def position_at value
49
+ instance.send "#{ranker.name}_position=", value
50
+ update_index_from_position
51
+ end
52
+
53
+ def rank_at value
54
+ instance.send "#{ranker.column}=", value
55
+ end
56
+
57
+ def rank_changed?
58
+ instance.send "#{ranker.column}_changed?"
59
+ end
60
+
61
+ def new_record?
62
+ instance.new_record?
63
+ end
64
+
65
+ def update_index_from_position
66
+ case position
67
+ when :first
68
+ if !current_order.empty? && current_order.first.rank
69
+ rank_at( current_order.first.rank / 2 )
70
+ else
71
+ rank_at 0
72
+ end
73
+ when :last
74
+ if !current_order.empty? && current_order.last.rank
75
+ rank_at( ( RankedModel::MAX_RANK_VALUE + current_order.last.rank ) / 2 )
76
+ else
77
+ rank_at RankedModel::MAX_RANK_VALUE
78
+ end
79
+ when String
80
+ position_at position.to_i
81
+ when 0
82
+ position_at :first
83
+ when Integer
84
+ if current_order[position]
85
+ rank_at( ( current_order[position-1].rank + current_order[position].rank ) / 2 )
86
+ else
87
+ position_at :last
88
+ end
89
+ end
90
+ end
91
+
92
+ def assure_unique_position
93
+ if ( new_record? || rank_changed? )
94
+ unless rank
95
+ rank_at RankedModel::MAX_RANK_VALUE
96
+ end
97
+
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
103
+ end
104
+ end
105
+ end
106
+
107
+ def rebalance_ranks
108
+ total = current_order.size + 2
109
+ has_set_self = false
110
+ total.times do |index|
111
+ next if index == 0 || index == total
112
+ rank_value = RankedModel::MAX_RANK_VALUE / total * index
113
+ index = index - 1
114
+ if has_set_self
115
+ index = index - 1
116
+ else
117
+ if !current_order[index] ||
118
+ ( !current_order[index].rank.nil? &&
119
+ current_order[index].rank >= rank )
120
+ rank_at rank_value
121
+ has_set_self = true
122
+ next
123
+ end
124
+ end
125
+ current_order[index].update_rank! rank_value
126
+ end
127
+ end
128
+
129
+ def current_order
130
+ @current_order ||= begin
131
+ finder = instance.class
132
+ if ranker.scope
133
+ finder = finder.send ranker.scope
134
+ end
135
+ if ranker.with_same
136
+ finder = finder.where \
137
+ instance.class.arel_table[ranker.with_same].eq(instance.attributes["#{ranker.with_same}"])
138
+ end
139
+ if !new_record?
140
+ finder = finder.where \
141
+ instance.class.arel_table[:id].not_eq(instance.id)
142
+ end
143
+ finder.order(ranker.column).select([:id, ranker.column]).collect { |ordered_instance|
144
+ RankedModel::Ranker::Mapper.new ranker, ordered_instance
145
+ }
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+
153
+ end
@@ -0,0 +1,3 @@
1
+ module RankedModel
2
+ VERSION = "0.0.1"
3
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'ranked-model/railtie'
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ranked-model/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ranked-model"
7
+ s.version = RankedModel::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Matthew Beale"]
10
+ s.email = ["matt.beale@madhatted.com"]
11
+ s.homepage = "https://github.com/harvesthq/ranked-model"
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.}
14
+
15
+ s.add_dependency "activerecord", ">= 3.0.3"
16
+ s.add_development_dependency "rspec"
17
+ s.add_development_dependency "rspec-rails"
18
+ s.add_development_dependency "sqlite3"
19
+ s.add_development_dependency "genspec"
20
+ s.add_development_dependency "mocha"
21
+
22
+ # s.rubyforge_project = "ranked-model"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe Duck do
4
+
5
+ before {
6
+ @ducks = {
7
+ :quacky => Duck.create(
8
+ :name => 'Quacky',
9
+ :pond => 'Shin' ),
10
+ :feathers => Duck.create(
11
+ :name => 'Feathers',
12
+ :pond => 'Shin' ),
13
+ :wingy => Duck.create(
14
+ :name => 'Wingy',
15
+ :pond => 'Shin' ),
16
+ :webby => Duck.create(
17
+ :name => 'Webby',
18
+ :pond => 'Boyden' ),
19
+ :waddly => Duck.create(
20
+ :name => 'Waddly',
21
+ :pond => 'Meddybemps' ),
22
+ :beaky => Duck.create(
23
+ :name => 'Beaky',
24
+ :pond => 'Great Moose' )
25
+ }
26
+ @ducks.each { |name, duck|
27
+ duck.reload
28
+ duck.row_position = 0
29
+ duck.size_position = 0
30
+ duck.age_position = 0
31
+ duck.save!
32
+ }
33
+ @ducks.each {|name, duck| duck.reload }
34
+ }
35
+
36
+ describe "sorting by size on in_shin_pond" do
37
+
38
+ before {
39
+ @ducks[:quacky].update_attribute :size_position, 0
40
+ @ducks[:wingy].update_attribute :size_position, 2
41
+ }
42
+
43
+ subject { Duck.in_shin_pond.rank(:size).all }
44
+
45
+ its(:size) { should == 3 }
46
+
47
+ its(:first) { should == @ducks[:quacky] }
48
+
49
+ its(:last) { should == @ducks[:wingy] }
50
+
51
+ end
52
+
53
+ describe "sorting by age on Shin pond" do
54
+
55
+ before {
56
+ @ducks[:feathers].update_attribute :age_position, 0
57
+ @ducks[:wingy].update_attribute :age_position, 0
58
+ }
59
+
60
+ subject { Duck.where(:pond => 'Shin').rank(:age).all }
61
+
62
+ its(:size) { should == 3 }
63
+
64
+ its(:first) { should == @ducks[:wingy] }
65
+
66
+ its(:last) { should == @ducks[:quacky] }
67
+
68
+ end
69
+
70
+ describe "sorting by row" do
71
+
72
+ before {
73
+ @ducks[:beaky].update_attribute :row_position, 0
74
+ @ducks[:webby].update_attribute :row_position, 2
75
+ @ducks[:waddly].update_attribute :row_position, 2
76
+ @ducks[:wingy].update_attribute :row_position, 6
77
+ }
78
+
79
+ subject { Duck.rank(:row).all }
80
+
81
+ its(:size) { should == 6 }
82
+
83
+ its(:first) { should == @ducks[:beaky] }
84
+
85
+ its(:last) { should == @ducks[:wingy] }
86
+
87
+ end
88
+
89
+ describe "mixed sorting by" do
90
+
91
+ before {
92
+ @ducks[:quacky].update_attribute :size_position, 0
93
+ @ducks[:beaky].update_attribute :row_position, 0
94
+ @ducks[:webby].update_attribute :row_position, 2
95
+ @ducks[:wingy].update_attribute :size_position, 1
96
+ @ducks[:waddly].update_attribute :row_position, 2
97
+ @ducks[:wingy].update_attribute :row_position, 6
98
+ @ducks[:webby].update_attribute :row_position, 6
99
+ }
100
+
101
+ describe "row" do
102
+
103
+ subject { Duck.rank(:row).all }
104
+
105
+ its(:size) { should == 6 }
106
+
107
+ its(:first) { should == @ducks[:beaky] }
108
+
109
+ its(:last) { should == @ducks[:webby] }
110
+
111
+ end
112
+
113
+ describe "row" do
114
+
115
+ subject { Duck.in_shin_pond.rank(:size).all }
116
+
117
+ its(:size) { should == 3 }
118
+
119
+ its(:first) { should == @ducks[:quacky] }
120
+
121
+ its(:last) { should == @ducks[:feathers] }
122
+
123
+ end
124
+
125
+ end
126
+
127
+ end
@@ -0,0 +1,18 @@
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
+ }
12
+
13
+ its(:name) { should == :overview }
14
+ its(:column) { should == :a_sorting_column }
15
+ its(:scope) { should == :a_scope }
16
+ its(:with_same) { should == :a_column }
17
+
18
+ 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,15 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'ranked-model' # and any other gems you need
5
+
6
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each {|f| require f}
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :mocha
10
+ config.use_transactional_fixtures = true
11
+ end
12
+
13
+ RSpec::Matchers.define :define_constant do |expected|
14
+ match { |actual| actual.const_defined?(expected) }
15
+ end
@@ -0,0 +1,32 @@
1
+ require 'active_record'
2
+ require 'sqlite3'
3
+ require 'logger'
4
+ require 'rspec/rails/adapters'
5
+ require 'rspec/rails/fixture_support'
6
+
7
+ ROOT = File.join(File.dirname(__FILE__), '..')
8
+
9
+ ActiveRecord::Base.logger = Logger.new('tmp/ar_debug.log')
10
+ ActiveRecord::Base.configurations = YAML::load(IO.read('spec/support/database.yml'))
11
+ ActiveRecord::Base.establish_connection('development')
12
+
13
+ ActiveRecord::Schema.define :version => 0 do
14
+ create_table :ducks, :force => true do |t|
15
+ t.string :name
16
+ t.integer :row
17
+ t.integer :size
18
+ t.integer :age
19
+ t.string :pond
20
+ end
21
+ end
22
+
23
+ class Duck < ActiveRecord::Base
24
+
25
+ include RankedModel
26
+ ranks :row
27
+ ranks :size, :scope => :in_shin_pond
28
+ ranks :age, :with_same => :pond
29
+
30
+ scope :in_shin_pond, where(:pond => 'Shin')
31
+
32
+ end
@@ -0,0 +1,5 @@
1
+ development:
2
+ adapter: sqlite3
3
+ database: tmp/data.sqlite3
4
+ pool: 5
5
+ timeout: 5000
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ranked-model
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Matthew Beale
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-08 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 1
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 3
34
+ version: 3.0.3
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: rspec-rails
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: sqlite3
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: genspec
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id005
93
+ - !ruby/object:Gem::Dependency
94
+ name: mocha
95
+ prerelease: false
96
+ requirement: &id006 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ type: :development
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.
108
+ email:
109
+ - matt.beale@madhatted.com
110
+ executables: []
111
+
112
+ extensions: []
113
+
114
+ extra_rdoc_files: []
115
+
116
+ files:
117
+ - .gitignore
118
+ - .rspec
119
+ - Gemfile
120
+ - Gemfile.lock
121
+ - LICENSE
122
+ - Rakefile
123
+ - Readme.mkd
124
+ - lib/ranked-model.rb
125
+ - lib/ranked-model/railtie.rb
126
+ - lib/ranked-model/ranker.rb
127
+ - lib/ranked-model/version.rb
128
+ - rails/init.rb
129
+ - ranked-model.gemspec
130
+ - spec/duck-model/duck_spec.rb
131
+ - spec/ranked-model/ranker_spec.rb
132
+ - spec/ranked-model/version_spec.rb
133
+ - spec/spec_helper.rb
134
+ - spec/support/active_record.rb
135
+ - spec/support/database.yml
136
+ - tmp/.gitignore
137
+ has_rdoc: true
138
+ homepage: https://github.com/harvesthq/ranked-model
139
+ licenses: []
140
+
141
+ post_install_message:
142
+ rdoc_options: []
143
+
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ hash: 3
152
+ segments:
153
+ - 0
154
+ version: "0"
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ requirements: []
165
+
166
+ rubyforge_project:
167
+ rubygems_version: 1.5.0
168
+ signing_key:
169
+ specification_version: 3
170
+ summary: An acts_as_sortable replacement built for Rails 3
171
+ test_files:
172
+ - spec/duck-model/duck_spec.rb
173
+ - spec/ranked-model/ranker_spec.rb
174
+ - spec/ranked-model/version_spec.rb
175
+ - spec/spec_helper.rb
176
+ - spec/support/active_record.rb
177
+ - spec/support/database.yml