sort_this 1.0.0

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