sort_this 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-1.9.3-p327@sort_this"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.17.3 (stable)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
+ \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
+ else
29
+ # If the environment file has not yet been created, use the RVM CLI to select.
30
+ rvm --create "$environment_id" || {
31
+ echo "Failed to create RVM environment '${environment_id}'."
32
+ return 1
33
+ }
34
+ fi
35
+
36
+ # If you use bundler, this might be useful to you:
37
+ # if [[ -s Gemfile ]] && {
38
+ # ! builtin command -v bundle >/dev/null ||
39
+ # builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
40
+ # }
41
+ # then
42
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
43
+ # gem install bundler
44
+ # fi
45
+ # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
46
+ # then
47
+ # bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
48
+ # fi
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## v1.0.0
2
+
3
+ * Initial Release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sortable.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Scott Pullen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # SortThis
2
+
3
+ SortThis provides a way to sort.
4
+
5
+ The controller/view code is based off of railscasts [episode 228 - Sortable Table Columns](http://railscasts.com/episodes/228-sortable-table-columns).
6
+
7
+ Here is an [example app](https://github.com/spullen/sort_example) that demonstrates the usage.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'sort_this', :git => 'git://github.com/spullen/sort_this.git'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ ## Usage
20
+
21
+ ### Model
22
+
23
+ Add a call to the `sortable` method with a hash of sorts
24
+
25
+ class YourModel < ActiveRecord::Base
26
+
27
+ sort_this :sort_name => {:column_name => :name, :default => 'ASC', :joins => :association, :clause => "some.clause"}
28
+
29
+ end
30
+
31
+ The options are
32
+
33
+ sort_name: Is the name of the sort, this in most cases will be the same as the column_name, but can be anything
34
+
35
+ Sort Options:
36
+
37
+ column_name: (Required) The name of the column to sort on.
38
+ default: (Optional) Defines a default sort if provided. The valid options are 'ASC' or 'DESC'.
39
+ joins: (Optional) Defines an association to join on, this should be provided if the column is in another table.
40
+ clause: (Optional) Overrides the clause used for the sort.
41
+
42
+ Then to sort
43
+
44
+ YourModel.sort("sort_name", "asc|desc") => sorted list of YourModel objects
45
+
46
+ ### Controller
47
+
48
+ In you controller define a default sort
49
+
50
+ class SomeController < ApplicationController
51
+ sortable "sort_name"
52
+
53
+ def index
54
+ SomeModel.sort(sort_column, sort_direction)
55
+ end
56
+ end
57
+
58
+ ### In Views
59
+
60
+ sortable(sort_name, title = nil, html_options = {})
61
+
62
+ <%= sortable("sort_name") %>
63
+
64
+ or custom title
65
+
66
+ <%= sortable("sort_name", "Sort Different Name") %>
67
+
68
+ ## TODO:
69
+
70
+ - Add error handling to active record sortable
71
+ - Add the ability to customize the sort and direction parameters
72
+ - Define scopes for each sort defined (individual sort scopes)
73
+
74
+ - Testing on different databases (should probably hit postgresql and mysql unless the way SQLite handles it in the same way)
75
+
76
+ ## Contributing
77
+
78
+ 1. Fork it
79
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
80
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
81
+ 4. Push to the branch (`git push origin my-new-feature`)
82
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,31 @@
1
+ require 'action_controller'
2
+
3
+ module SortThis
4
+ module ActionController
5
+ def self.included(base)
6
+ base.class_eval do
7
+ extend ClassMethods
8
+ class_attribute :default_sort_column
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def sortable(sort_column)
15
+ self.default_sort_column = sort_column
16
+ end
17
+
18
+ end
19
+
20
+ protected
21
+
22
+ def sort_column
23
+ params[:sort].blank? ? self.default_sort_column : params[:sort]
24
+ end
25
+
26
+ def sort_direction
27
+ %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,100 @@
1
+ require 'memoist'
2
+ require 'active_record'
3
+
4
+ module SortThis
5
+ module ActiveRecord
6
+ def self.included(base)
7
+ base.class_eval do
8
+ extend ClassMethods
9
+
10
+ class << self; extend Memoist; self; end.memoize :sort
11
+
12
+
13
+ class_attribute :sort_columns, :default_sort_columns
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ #
20
+ # Input is a hash of sort_name => sort_options pairs
21
+ #
22
+ # :column_name => (required) Column name to sort on
23
+ # :default => (optional) 'ASC'|'DESC'
24
+ # :joins => (optional) Association to join on. Note: must be an association of the model being sorted.
25
+ # Prefixes the column_name with the table name to prevent collisions
26
+ # :clause => (optional) Override the clause of the sort
27
+ #
28
+ # ex.
29
+ # products have many quotes
30
+ # vendors have many quotes
31
+ #
32
+ # sorting on the quotes table
33
+ #
34
+ # :sort_name1 => { :column_name => :quantity }
35
+ # :sort_name2 => { :column_name => :price, :default => 'DESC' }
36
+ # :sort_name3 => { :column_name => :name, :joins => :product }
37
+ #
38
+ def sort_this(sorts = {})
39
+ self.sort_columns = {}
40
+ self.default_sort_columns = {}
41
+
42
+ empty_sort_options = { :column_name => nil, :default => nil, :joins => nil, :clause => nil }
43
+
44
+ sorts.each do |sort_name, sort_options|
45
+ sort_options = empty_sort_options.merge(sort_options)
46
+
47
+ self.sort_columns[sort_name] = sort_options
48
+
49
+ column = sort_options[:column_name] # TODO: raise exception if this is nil
50
+ table = (sort_options[:joins].blank?) ? table_name : sort_options[:joins].to_s.pluralize
51
+ clause = (sort_options[:clause].blank?) ? "#{table}.#{column}" : sort_options[:clause]
52
+
53
+ self.sort_columns[sort_name][:clause] = clause if sort_options[:clause].blank?
54
+
55
+ unless sort_options[:default].blank?
56
+ # TODO: raise exception if the default is not ASC|DESC
57
+ default_sort_direction = sort_options[:default].upcase
58
+ self.default_sort_columns[sort_name] = "#{clause} #{default_sort_direction}"
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ def sort(sort_column = nil, sort_direction = DEFAULT_SORT_DIRECTION)
65
+ query = scoped
66
+
67
+ # sanitize the sort column and direction
68
+ sort_column = sort_column.to_s.downcase.to_sym
69
+ sort_direction = sort_direction.upcase
70
+ sort_direction = (VALID_SORT_DIRECTIONS.include?(sort_direction) ? sort_direction : DEFAULT_SORT_DIRECTION)
71
+
72
+ table_joins = []
73
+ order_clauses = []
74
+
75
+ if !sort_column.blank? && sort_columns.include?(sort_column)
76
+ join = sort_columns[sort_column][:joins]
77
+ table_joins << join unless join.nil?
78
+ order_clauses << "#{sort_columns[sort_column][:clause]} #{sort_direction}"
79
+ end
80
+
81
+ default_sorts = default_sort_columns.clone
82
+ default_sorts.delete(sort_column) unless sort_column.blank? # remove the sort column's default if it exists
83
+
84
+ default_sorts.each do |column_name, sort_clause|
85
+ join = sort_columns[column_name][:joins]
86
+ tables_joins << join unless join.nil?
87
+ order_clauses << sort_clause
88
+ end
89
+
90
+ table_joins.uniq!
91
+ query = query.joins(table_joins) unless table_joins.empty?
92
+
93
+ query = query.order(order_clauses.join(', '))
94
+
95
+ query
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,18 @@
1
+ module SortThis
2
+ class Railtie < Rails::Railtie
3
+ initializer 'sortable' do |app|
4
+ ActiveSupport.on_load :active_record do
5
+ include SortThis::ActiveRecord
6
+ end
7
+
8
+ ActiveSupport.on_load :action_controller do
9
+ include SortThis::ActionController
10
+ helper_method :sort_column, :sort_direction
11
+ end
12
+
13
+ ActiveSupport.on_load :action_view do
14
+ include SortThis::ViewHelpers::ActionView
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module SortThis
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'action_view'
2
+
3
+ module SortThis
4
+ module ViewHelpers
5
+ module ActionView
6
+
7
+ def sortable(sort_name, title = nil, html_options = {})
8
+ title ||= sort_name.titleize
9
+ css_class = (sort_name == sort_column) ? "sortable-current sortable-#{sort_direction}" : nil
10
+ direction = (sort_name == sort_column && sort_direction == "asc") ? "desc" : "asc"
11
+ link_to title, params.merge(:sort => sort_name, :direction => direction), {:class => css_class}.merge(html_options)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
data/lib/sort_this.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'sort_this/version'
2
+ require 'sort_this/active_record'
3
+ require 'sort_this/action_controller'
4
+ require 'sort_this/view_helpers/action_view'
5
+ require 'sort_this/railtie' if defined?(Rails)
6
+
7
+ module SortThis
8
+ SORT_ASC = 'ASC'
9
+ SORT_DESC = 'DESC'
10
+ DEFAULT_SORT_DIRECTION = SORT_ASC
11
+ VALID_SORT_DIRECTIONS = [SORT_ASC, SORT_DESC]
12
+ end
data/sort_this.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sort_this/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "sort_this"
8
+ gem.version = SortThis::VERSION
9
+ gem.authors = ["Scott Pullen"]
10
+ gem.email = ["s.pullen05@gmail.com"]
11
+ gem.description = %q{SortThis}
12
+ gem.summary = %q{SortThis}
13
+ gem.homepage = "https://github.com/spullen/sort_this"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'rails', '>= 3.0'
21
+ gem.add_dependency 'memoist', '0.2.0'
22
+
23
+ gem.add_development_dependency 'debugger', '1.2.2'
24
+ gem.add_development_dependency 'rspec', '2.12.0'
25
+ gem.add_development_dependency 'sqlite3', '1.3.6'
26
+ gem.add_development_dependency 'factory_girl', '4.1.0'
27
+ gem.add_development_dependency 'database_cleaner', '0.9.1'
28
+ gem.add_development_dependency 'capybara', '2.0.1'
29
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ class TestController < ActionController::Base
4
+ include SortThis::ActionController
5
+ sortable "price"
6
+
7
+ def index
8
+ render :text => 'OK'
9
+ end
10
+ end
11
+
12
+ # get access to the protected methods
13
+ TestController.send(:public, *TestController.protected_instance_methods)
14
+
15
+ describe SortThis::ActionController do
16
+
17
+ it 'should respond to sortable' do
18
+ TestController.should respond_to(:sortable)
19
+ end
20
+
21
+ it 'should set the default sort column' do
22
+ TestController.default_sort_column.should == "price"
23
+ end
24
+
25
+ describe 'instance methods' do
26
+ subject { @controller }
27
+
28
+ before(:each) do
29
+ @controller = TestController.new
30
+ end
31
+
32
+ it 'should respond to sort_column' do
33
+ @controller.should respond_to(:sort_column)
34
+ end
35
+
36
+ it 'should respond to sort_direction' do
37
+ @controller.should respond_to(:sort_direction)
38
+ end
39
+
40
+ describe '#sort_column' do
41
+ context 'given that the sort param is not set' do
42
+ it 'should return the default sort column' do
43
+ @controller.stub!(:params).and_return({})
44
+ @controller.sort_column.should == "price"
45
+ end
46
+ end
47
+
48
+ context 'given that the sort param is set' do
49
+ it 'should return the sort provided by the params' do
50
+ sort = "quantity"
51
+ @controller.stub!(:params).and_return({:sort => sort})
52
+ @controller.sort_column.should == sort
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#sort_direction' do
58
+ context 'given that the direction param is not set' do
59
+ it 'should return the default sort direction' do
60
+ @controller.stub!(:params).and_return({})
61
+ @controller.sort_direction.should == "asc"
62
+ end
63
+ end
64
+
65
+ context 'given that the direction param is set' do
66
+ it 'should return the sort direction provided by the params' do
67
+ @controller.stub!(:params).and_return({:direction => "desc"})
68
+ @controller.sort_direction.should == "desc"
69
+ end
70
+
71
+ context 'when the param is invalid' do
72
+ it 'should return the default sort direction' do
73
+ @controller.stub!(:params).and_return({})
74
+ @controller.sort_direction.should == "asc"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,219 @@
1
+ require 'spec_helper'
2
+
3
+ describe SortThis::ActiveRecord do
4
+
5
+ before(:all) do
6
+ setup_db
7
+ DatabaseCleaner.strategy = :truncation
8
+ end
9
+
10
+ after(:all) do
11
+ teardown_db
12
+ end
13
+
14
+ before(:each) do
15
+ DatabaseCleaner.start
16
+ end
17
+
18
+ after(:each) do
19
+ DatabaseCleaner.clean
20
+ end
21
+
22
+ it 'should have a sort_this class method' do
23
+ Quote.should respond_to(:sort_this)
24
+ end
25
+
26
+ it 'should have a sort class method' do
27
+ Quote.should respond_to(:sort)
28
+ end
29
+
30
+ describe '.sort_this' do
31
+
32
+ shared_examples_for 'sort_columns_defined' do
33
+ it 'should set the sort_columns options for the specified sort name' do
34
+ Quote.sort_columns.should have_key(sort_name)
35
+ end
36
+
37
+ describe 'sort_columns for the specified sort name' do
38
+ it 'should properly set the column_name option' do
39
+ Quote.sort_columns[sort_name][:column_name].should == column_name_option
40
+ end
41
+
42
+ it 'should properly set the default option' do
43
+ Quote.sort_columns[sort_name][:default].should == default_option
44
+ end
45
+
46
+ it 'should properly set the joins option' do
47
+ Quote.sort_columns[sort_name][:joins].should == joins_option
48
+ end
49
+
50
+ it 'should properly set the clause option' do
51
+ Quote.sort_columns[sort_name][:clause].should == clause_option
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'given no parameters' do
57
+ before(:each) do
58
+ Quote.sort_this()
59
+ end
60
+
61
+ it 'should set sort_columns to an empty hash' do
62
+ Quote.sort_columns.should == {}
63
+ end
64
+
65
+ it 'should set default_sort_columns to an empty hash' do
66
+ Quote.default_sort_columns.should == {}
67
+ end
68
+ end
69
+
70
+ context 'given a sort column with just the required sort options' do
71
+ let!(:sort_name) { :price }
72
+ let!(:column_name_option) { :price }
73
+ let!(:default_option) { nil }
74
+ let!(:joins_option) { nil }
75
+ let!(:clause_option) { "quotes.price" }
76
+
77
+ before(:each) do
78
+ Quote.sort_this sort_name => {:column_name => column_name_option}
79
+ end
80
+
81
+ it_should_behave_like 'sort_columns_defined'
82
+
83
+ it 'should set default_sort_columns to an empty hash' do
84
+ Quote.default_sort_columns.should == {}
85
+ end
86
+ end
87
+
88
+ context 'given a sort column with a default specified in the sort options' do
89
+ let!(:sort_name) { :price }
90
+ let!(:column_name_option) { :price }
91
+ let!(:default_option) { 'DESC' }
92
+ let!(:joins_option) { nil }
93
+ let!(:clause_option) { "quotes.price" }
94
+
95
+ before(:each) do
96
+ Quote.sort_this sort_name => {:column_name => column_name_option, :default => default_option}
97
+ end
98
+
99
+ it_should_behave_like 'sort_columns_defined'
100
+
101
+ it 'should set the default_sort_columns options for the specified sort name' do
102
+ Quote.default_sort_columns.should have_key(sort_name)
103
+ end
104
+
105
+ it 'should set the clause of the default_sort_columns for the specified sort name' do
106
+ Quote.default_sort_columns[sort_name].should == "#{clause_option} #{default_option}"
107
+ end
108
+ end
109
+
110
+ context 'given a sort column with a joins specified in the sort options' do
111
+ let!(:sort_name) { :product_name }
112
+ let!(:column_name_option) { :name }
113
+ let!(:default_option) { nil }
114
+ let!(:joins_option) { :product }
115
+ let!(:clause_option) { "products.name" }
116
+
117
+ before(:each) do
118
+ Quote.sort_this sort_name => {:column_name => column_name_option, :joins => joins_option}
119
+ end
120
+
121
+ it_should_behave_like 'sort_columns_defined'
122
+
123
+ it 'should set default_sort_columns to an empty hash' do
124
+ Quote.default_sort_columns.should == {}
125
+ end
126
+ end
127
+
128
+ context 'given a sort column with a custom clause specified in the sort options' do
129
+ let!(:sort_name) { :price }
130
+ let!(:column_name_option) { :price }
131
+ let!(:default_option) { nil }
132
+ let!(:joins_option) { nil }
133
+ let!(:clause_option) { "custom.joins_clause" }
134
+
135
+ before(:each) do
136
+ Quote.sort_this sort_name => {:column_name => column_name_option, :clause => clause_option}
137
+ end
138
+
139
+ it_should_behave_like 'sort_columns_defined'
140
+
141
+ it 'should set default_sort_columns to an empty hash' do
142
+ Quote.default_sort_columns.should == {}
143
+ end
144
+ end
145
+
146
+ context 'given a sort column with a custom clause and default specified in the sort options' do
147
+ let!(:sort_name) { :quantity }
148
+ let!(:column_name_option) { :quantity }
149
+ let!(:default_option) { 'DESC' }
150
+ let!(:joins_option) { nil }
151
+ let!(:clause_option) { "custom.joins_clause" }
152
+
153
+ before(:each) do
154
+ Quote.sort_this sort_name => {:column_name => column_name_option, :default => default_option, :clause => clause_option}
155
+ end
156
+
157
+ it_should_behave_like 'sort_columns_defined'
158
+
159
+ it 'should set the default_sort_columns options for the specified sort name' do
160
+ Quote.default_sort_columns.should have_key(sort_name)
161
+ end
162
+
163
+ it 'should set the clause of the default_sort_columns for the specified sort name' do
164
+ Quote.default_sort_columns[sort_name].should == "#{clause_option} #{default_option}"
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '.sort' do
170
+ let!(:product1) { create(:product, :name => 'D Name') }
171
+ let!(:product2) { create(:product, :name => 'B Name') }
172
+ let!(:product3) { create(:product, :name => 'A Name') }
173
+ let!(:product4) { create(:product, :name => 'C Name') }
174
+
175
+ let!(:vendor1) { create(:vendor, :name => 'D Name') }
176
+ let!(:vendor2) { create(:vendor, :name => 'B Name') }
177
+ let!(:vendor3) { create(:vendor, :name => 'A Name') }
178
+ let!(:vendor4) { create(:vendor, :name => 'C Name') }
179
+
180
+ let!(:quote1) { create(:quote, :product => product1, :vendor => vendor4, :price => 10.55, :quantity => 4) }
181
+ let!(:quote2) { create(:quote, :product => product3, :vendor => vendor2, :price => 9.31, :quantity => 2) }
182
+ let!(:quote3) { create(:quote, :product => product2, :vendor => vendor1, :price => 8.25, :quantity => 10) }
183
+ let!(:quote4) { create(:quote, :product => product4, :vendor => vendor3, :price => 15.12, :quantity => 8) }
184
+
185
+ before(:each) do
186
+ Quote.sort_this :price => {:column_name => :price, :default => 'ASC'},
187
+ :quantity => {:column_name => :quantity},
188
+ :product_name => {:column_name => :name, :joins => :product},
189
+ :vendor_name => {:column_name => :name, :joins => :vendor}
190
+ end
191
+
192
+ context 'default parameters' do
193
+ it 'should sort by quote price ascending' do
194
+ Quote.sort.should == [quote3, quote2, quote1, quote4]
195
+ end
196
+ end
197
+
198
+ context 'given a sort column' do
199
+ context 'without specifying an order' do
200
+ it 'should sort by the specified column in ascending order' do
201
+ Quote.sort(:quantity).should == [quote2, quote1, quote4, quote3]
202
+ end
203
+ end
204
+
205
+ context 'specifying an order' do
206
+ it 'should sort by the specified column in descending order' do
207
+ Quote.sort(:quantity, 'DESC').should == [quote3, quote4, quote1, quote2]
208
+ end
209
+ end
210
+ end
211
+
212
+ context 'given a sort column from another table' do
213
+ it 'should sort by the specified column in descending order' do
214
+ Quote.sort(:product_name, 'DESC').should == [quote1, quote4, quote3, quote2]
215
+ end
216
+ end
217
+ end
218
+
219
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,18 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :product do
4
+ sequence(:name) { |n| "ProductName#{n}" }
5
+ end
6
+
7
+ factory :vendor do
8
+ sequence(:name) { |n| "VendorName#{n}" }
9
+ end
10
+
11
+ factory :quote do
12
+ product
13
+ vendor
14
+ price 10.99
15
+ quantity 10
16
+ end
17
+
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'debugger'
2
+ require 'sqlite3'
3
+ require 'active_record'
4
+ require 'factory_girl'
5
+ require 'database_cleaner'
6
+ require 'capybara'
7
+ require 'rspec'
8
+
9
+ require 'sort_this'
10
+
11
+ Dir[File.expand_path("support/**/*.rb", File.dirname(__FILE__))].each {|f| require f}
12
+
13
+ FactoryGirl.find_definitions
14
+
15
+ RSpec.configure do |config|
16
+ config.include FactoryGirl::Syntax::Methods
17
+ end
File without changes
@@ -0,0 +1,50 @@
1
+ def setup_db
2
+ puts "\nSetting up the database...\n"
3
+
4
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
5
+
6
+ ActiveRecord::Schema.define(:version => 1) do
7
+ create_table("products") do |t|
8
+ t.string "name"
9
+ end
10
+
11
+ create_table("quotes") do |t|
12
+ t.integer "product_id"
13
+ t.integer "vendor_id"
14
+ t.decimal "price", :precision => 10, :scale => 2, :default => 0.0
15
+ t.integer "quantity", :default => 0
16
+ end
17
+
18
+ create_table("vendors") do |t|
19
+ t.string "name"
20
+ end
21
+ end
22
+ end
23
+
24
+ def teardown_db
25
+ puts "\nTearing down the database...\n"
26
+
27
+ ActiveRecord::Base.connection.tables.each do |table|
28
+ ActiveRecord::Base.connection.drop_table(table)
29
+ end
30
+ end
31
+
32
+ class Product < ActiveRecord::Base
33
+ has_many :quotes
34
+ end
35
+
36
+ class Vendor < ActiveRecord::Base
37
+ has_many :quotes
38
+ end
39
+
40
+ class Quote < ActiveRecord::Base
41
+ include SortThis::ActiveRecord
42
+
43
+ belongs_to :product
44
+ belongs_to :vendor
45
+
46
+ #sortable :price => {:column_name => :price, :default => 'ASC'},
47
+ # :quantity => {:column_name => :quantity},
48
+ # :product_name => {:column_name => :name, :joins => :product},
49
+ # :vendor_name => {:column_name => :name, :joins => :vendor}
50
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ # there's probably a better way to test this
4
+ # check out https://github.com/mislav/will_paginate/blob/master/spec/view_helpers/action_view_spec.rb#L28
5
+
6
+ class TestActionView < ActionView::Base
7
+ include SortThis::ViewHelpers::ActionView
8
+ end
9
+
10
+ describe SortThis::ViewHelpers::ActionView do
11
+
12
+ let!(:params_base) { {:controller => 'quotes', :action => 'index'} }
13
+
14
+ before(:each) do
15
+ @helper = TestActionView.new
16
+ @helper.stub!(:url_for).and_return('/quotes/index')
17
+ @helper.stub!(:params).and_return(params_base)
18
+ @helper.stub!(:sort_column).and_return("price")
19
+ @helper.stub!(:sort_direction).and_return("asc")
20
+ end
21
+
22
+ describe '#sortable' do
23
+ context 'given the column is the only specified parameter' do
24
+ before(:each) do
25
+ @node = Capybara::Node::Simple.new(@helper.sortable("test"))
26
+ end
27
+
28
+ it 'should set the title to be the capitalized version of the column name' do
29
+ @node.should have_content("Test")
30
+ end
31
+ end
32
+
33
+ context 'given the sort column is the current column being sorted' do
34
+ before(:each) do
35
+ @node = Capybara::Node::Simple.new(@helper.sortable("price"))
36
+ end
37
+
38
+ it 'should set the sortable-current class' do
39
+ @node.should have_css('a.sortable-current')
40
+ end
41
+
42
+ it 'should set the sortable-<direction>' do
43
+ @node.should have_css('a.sortable-asc')
44
+ end
45
+ end
46
+
47
+ context 'given the sort column is not the current column being sorted' do
48
+ before(:each) do
49
+ @node = Capybara::Node::Simple.new(@helper.sortable("other_column"))
50
+ end
51
+
52
+ it 'should set the sortable-current class' do
53
+ @node.should_not have_css('a.sortable-current')
54
+ end
55
+
56
+ it 'should set the sortable-<direction>' do
57
+ @node.should_not have_css('a.sortable-asc')
58
+ end
59
+ end
60
+
61
+ context 'given the title is specified' do
62
+ before(:each) do
63
+ @node = Capybara::Node::Simple.new(@helper.sortable("column", "Custom Title"))
64
+ end
65
+
66
+ it 'should set the sortable-current class' do
67
+ @node.should have_content("Custom Title")
68
+ end
69
+ end
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sort_this
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Pullen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: memoist
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.2.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.2.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: debugger
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.2
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.2
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 2.12.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 2.12.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: sqlite3
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: 1.3.6
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: 1.3.6
94
+ - !ruby/object:Gem::Dependency
95
+ name: factory_girl
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 4.1.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 4.1.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: database_cleaner
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 0.9.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - '='
124
+ - !ruby/object:Gem::Version
125
+ version: 0.9.1
126
+ - !ruby/object:Gem::Dependency
127
+ name: capybara
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - '='
132
+ - !ruby/object:Gem::Version
133
+ version: 2.0.1
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - '='
140
+ - !ruby/object:Gem::Version
141
+ version: 2.0.1
142
+ description: SortThis
143
+ email:
144
+ - s.pullen05@gmail.com
145
+ executables: []
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - .gitignore
150
+ - .rspec
151
+ - .rvmrc
152
+ - CHANGELOG.md
153
+ - Gemfile
154
+ - LICENSE.txt
155
+ - README.md
156
+ - Rakefile
157
+ - lib/sort_this.rb
158
+ - lib/sort_this/action_controller.rb
159
+ - lib/sort_this/active_record.rb
160
+ - lib/sort_this/railtie.rb
161
+ - lib/sort_this/version.rb
162
+ - lib/sort_this/view_helpers/action_view.rb
163
+ - sort_this.gemspec
164
+ - spec/action_controller_spec.rb
165
+ - spec/active_record_spec.rb
166
+ - spec/factories.rb
167
+ - spec/spec_helper.rb
168
+ - spec/support/.gitkeep
169
+ - spec/support/models.rb
170
+ - spec/view_helpers/action_view_spec.rb
171
+ homepage: https://github.com/spullen/sort_this
172
+ licenses: []
173
+ post_install_message:
174
+ rdoc_options: []
175
+ require_paths:
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ none: false
179
+ requirements:
180
+ - - ! '>='
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ segments:
184
+ - 0
185
+ hash: 2103404262135696406
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ none: false
188
+ requirements:
189
+ - - ! '>='
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ segments:
193
+ - 0
194
+ hash: 2103404262135696406
195
+ requirements: []
196
+ rubyforge_project:
197
+ rubygems_version: 1.8.24
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: SortThis
201
+ test_files:
202
+ - spec/action_controller_spec.rb
203
+ - spec/active_record_spec.rb
204
+ - spec/factories.rb
205
+ - spec/spec_helper.rb
206
+ - spec/support/.gitkeep
207
+ - spec/support/models.rb
208
+ - spec/view_helpers/action_view_spec.rb