sorted 0.2.2 → 0.3.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/README.rdoc +32 -13
- data/Rakefile +1 -3
- data/VERSION +1 -1
- data/lib/sorted.rb +110 -0
- data/lib/sorted/action_view.rb +7 -53
- data/lib/sorted/active_record.rb +2 -8
- data/sorted.gemspec +3 -9
- data/spec/sorted_spec.rb +81 -12
- metadata +7 -36
data/README.rdoc
CHANGED
@@ -1,27 +1,46 @@
|
|
1
1
|
= sorted
|
2
2
|
|
3
|
-
|
3
|
+
Sorted is a simple object that will take an sql order string and a url sort string to let you sort large datasets over many pages (using {will_paginate}[http://github.com/mislav/will_paginate]) without loosing state.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
== Requirements
|
8
|
-
|
9
|
-
activerecord >= 3.0.0
|
5
|
+
Using Rails master it will create a `sorted` scope and a two view helpers
|
10
6
|
|
11
7
|
== Example
|
12
8
|
|
13
|
-
The view:
|
9
|
+
=== The view:
|
14
10
|
|
15
11
|
link_to_sorted "Email", :email
|
16
12
|
|
17
|
-
|
13
|
+
This will make a url like this:
|
14
|
+
|
15
|
+
http://myapp/users?sort=email_asc
|
18
16
|
|
19
|
-
|
17
|
+
Or on the next page load when you then sort by something else....
|
20
18
|
|
21
|
-
|
19
|
+
http://myapp/users?sort=name_asc!email_asc
|
20
|
+
|
21
|
+
=== The model:
|
22
|
+
|
23
|
+
This will initially sort by email ascending:
|
24
|
+
|
25
|
+
@users = User.sorted(:order => "email ASC", :sort => params[:sort]).paginate(:page => params[:page])
|
26
|
+
|
27
|
+
== Rails 2.3.x
|
28
|
+
|
29
|
+
This gem works with rails 2.3.x but you will have to roll your own scope and view helper
|
30
|
+
|
31
|
+
Here is the named scope:
|
32
|
+
|
33
|
+
named_scope :sorted, lambda { |params|
|
34
|
+
{ :order => Sorted::Sorter.new(params[:order], {:sort => params[:sort]}).to_sql }
|
35
|
+
}
|
22
36
|
|
23
|
-
|
37
|
+
And this in your application helper:
|
24
38
|
|
25
|
-
|
39
|
+
def sorted(order)
|
40
|
+
Sorted::Sorter.new(order, (request.get? && !params.nil?) ? params.dup : nil).toggle
|
41
|
+
end
|
26
42
|
|
27
|
-
|
43
|
+
def link_to_sorted(name, order)
|
44
|
+
sorter = sorted(order)
|
45
|
+
link_to(name, sorter.params, {:class => sorter.css_class})
|
46
|
+
end
|
data/Rakefile
CHANGED
@@ -6,12 +6,10 @@ begin
|
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "sorted"
|
8
8
|
gem.summary = %Q{sort a table}
|
9
|
-
gem.description = %Q{lets you sort
|
9
|
+
gem.description = %Q{lets you sort large data sets using view helpers and a scope}
|
10
10
|
gem.email = "rufuspost@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/mynameisrufus/sorted"
|
12
12
|
gem.authors = ["Rufus Post"]
|
13
|
-
gem.add_dependency "activerecord", ">= 3.0.0"
|
14
|
-
gem.add_development_dependency "rspec", ">= 1.2.9"
|
15
13
|
end
|
16
14
|
Jeweler::GemcutterTasks.new
|
17
15
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/sorted.rb
CHANGED
@@ -1,4 +1,114 @@
|
|
1
1
|
module Sorted
|
2
|
+
class Sorter
|
3
|
+
def initialize(*args)
|
4
|
+
parse_order(args[0])
|
5
|
+
unless args[1].nil?
|
6
|
+
@params = args[1]
|
7
|
+
unless @params[:sort].nil?
|
8
|
+
parse_sort(@params[:sort])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_sort(sort)
|
15
|
+
sort.split(/!/).each do |s|
|
16
|
+
if ps = parse_query(s)
|
17
|
+
sort_queue << ps
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_order(order)
|
23
|
+
order.to_s.split(/,/).each do |o|
|
24
|
+
if po = parse_sql(o)
|
25
|
+
order_queue << po
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_query(sort)
|
31
|
+
if m = sort.match(/(\w+)_(asc|desc)/)
|
32
|
+
[m[1],m[2]]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_sql(sql)
|
37
|
+
if m = sql.match(/(([a-zA-Z._]+)\s([asc|ASC|desc|DESC]+)|[a-zA-Z._]+)/)
|
38
|
+
[(m[2].nil? ? m[1] : m[2]),(m[3].nil? ? "asc" : m[3].downcase)]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def toggle
|
43
|
+
@_array = []
|
44
|
+
order_queue.select do |os|
|
45
|
+
sort_queue.flatten.include?(os[0])
|
46
|
+
end.each do |os|
|
47
|
+
@_array << [os[0], (case sort_queue.assoc(os[0])[1]; when "asc"; "desc"; when "desc"; "asc" end)]
|
48
|
+
end
|
49
|
+
order_queue.select do |o|
|
50
|
+
!@_array.flatten.include?(o[0])
|
51
|
+
end.each do |o|
|
52
|
+
@_array << [o[0], o[1]]
|
53
|
+
end
|
54
|
+
sort_queue.select do |s|
|
55
|
+
!@_array.flatten.include?(s[0])
|
56
|
+
end.each do |s|
|
57
|
+
@_array << [s[0], s[1]]
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def un_toggle
|
63
|
+
@_array = default
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_hash
|
68
|
+
_array.inject({}){|h,a| h.merge(Hash[a[0],a[1]]) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_sql
|
72
|
+
_array.map{|a| "#{a[0]} #{a[1].upcase}"}.join(', ')
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
_array.map{|a| a.join('_')}.join('!')
|
77
|
+
end
|
78
|
+
|
79
|
+
def css_class
|
80
|
+
"sorted-#{_array[0][1]}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def params
|
84
|
+
@params ||= {}
|
85
|
+
@params[:sort] = to_s unless _array.empty?
|
86
|
+
@params
|
87
|
+
end
|
88
|
+
|
89
|
+
def order_queue
|
90
|
+
@order_queue ||= []
|
91
|
+
end
|
92
|
+
|
93
|
+
def sort_queue
|
94
|
+
@sort_queue ||= []
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def default
|
99
|
+
_default = sort_queue.dup
|
100
|
+
order_queue.each do |o|
|
101
|
+
unless _default.flatten.include?(o[0])
|
102
|
+
_default << [o[0], o[1]]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
_default
|
106
|
+
end
|
107
|
+
|
108
|
+
def _array
|
109
|
+
@_array ||= default
|
110
|
+
end
|
111
|
+
end
|
2
112
|
end
|
3
113
|
|
4
114
|
if defined?(::Rails::Railtie)
|
data/lib/sorted/action_view.rb
CHANGED
@@ -1,62 +1,16 @@
|
|
1
1
|
require 'action_view'
|
2
|
+
require 'sorted'
|
2
3
|
|
3
4
|
module Sorted
|
4
5
|
module ActionView
|
5
|
-
def
|
6
|
-
|
7
|
-
get_params[:order] = sorted_order(get_params[:order], attribute)
|
8
|
-
get_params
|
6
|
+
def sorted(order)
|
7
|
+
::Sorted::Sorter.new(order, (request.get? && !params.nil?) ? params.dup : nil).toggle
|
9
8
|
end
|
10
9
|
|
11
|
-
def link_to_sorted(name,
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
elsif options[:class].nil?
|
16
|
-
options.merge(:class => sorted_css_class)
|
17
|
-
else
|
18
|
-
options[:class] = "#{options[:class]} #{sorted_css_class}"
|
19
|
-
end
|
20
|
-
link_to(name, @sorted_params.merge(:order => sorted_to_string), options)
|
21
|
-
end
|
22
|
-
|
23
|
-
protected
|
24
|
-
|
25
|
-
def sorted_css_class
|
26
|
-
"sorted-#{@sorted_params[:order].first[1]}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def sorted_order(sort_string, attribute, order_rest = {})
|
30
|
-
order_first = {attribute => 'asc'}
|
31
|
-
if sort_string.class == String
|
32
|
-
sort_string.split(/!/).each do |order|
|
33
|
-
order.match(/(\w+)_(asc|desc)/) do |match_data|
|
34
|
-
if match_data[1] == attribute.to_s
|
35
|
-
order_first = {attribute => sorted_sql_reverse(match_data[2])}
|
36
|
-
else
|
37
|
-
order_rest = order_rest.merge(match_data[1] => match_data[2])
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
order_first.merge(order_rest).symbolize_keys
|
43
|
-
end
|
44
|
-
|
45
|
-
def sorted_to_string
|
46
|
-
@sorted_params[:order].map do |order|
|
47
|
-
order.join('_')
|
48
|
-
end.join('!')
|
49
|
-
end
|
50
|
-
|
51
|
-
def sorted_sql_reverse(order)
|
52
|
-
case order
|
53
|
-
when 'asc'
|
54
|
-
'desc'
|
55
|
-
when 'desc'
|
56
|
-
'asc'
|
57
|
-
else
|
58
|
-
'asc'
|
59
|
-
end
|
10
|
+
def link_to_sorted(name, order, options = {})
|
11
|
+
sorter = sorted(order)
|
12
|
+
options[:class] = options[:class].nil? ? sorter.css_class : "#{options[:class]} #{sorter.css_class}"
|
13
|
+
link_to(name, sorter.params, options)
|
60
14
|
end
|
61
15
|
end
|
62
16
|
end
|
data/lib/sorted/active_record.rb
CHANGED
@@ -1,17 +1,11 @@
|
|
1
1
|
require 'active_record'
|
2
|
+
require 'sorted'
|
2
3
|
|
3
4
|
module Sorted
|
4
5
|
module ActiveRecord
|
5
6
|
def self.enable!
|
6
7
|
::ActiveRecord::Base.class_eval do
|
7
|
-
scope :sorted, lambda{|params|
|
8
|
-
return if params.nil?
|
9
|
-
order = []
|
10
|
-
params.split(/!/).each do |param|
|
11
|
-
order << param.gsub(/_asc/, ' ASC').gsub(/_desc/, ' DESC')
|
12
|
-
end
|
13
|
-
{:order => order.join(', ')}
|
14
|
-
}
|
8
|
+
scope :sorted, lambda { |params| Hash[:order, ::Sorted::Sorter.new(params[:order], {:sort => params[:sort]}).to_sql] }
|
15
9
|
end
|
16
10
|
end
|
17
11
|
end
|
data/sorted.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{sorted}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Rufus Post"]
|
12
|
-
s.date = %q{2010-07-
|
13
|
-
s.description = %q{lets you sort
|
12
|
+
s.date = %q{2010-07-23}
|
13
|
+
s.description = %q{lets you sort large data sets using view helpers and a scope}
|
14
14
|
s.email = %q{rufuspost@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
@@ -46,15 +46,9 @@ Gem::Specification.new do |s|
|
|
46
46
|
s.specification_version = 3
|
47
47
|
|
48
48
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
-
s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0"])
|
50
|
-
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
51
49
|
else
|
52
|
-
s.add_dependency(%q<activerecord>, [">= 3.0.0"])
|
53
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
54
50
|
end
|
55
51
|
else
|
56
|
-
s.add_dependency(%q<activerecord>, [">= 3.0.0"])
|
57
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
58
52
|
end
|
59
53
|
end
|
60
54
|
|
data/spec/sorted_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require 'action_controller'
|
3
|
+
require 'sorted'
|
3
4
|
|
4
5
|
describe Sorted::ActiveRecord do
|
5
6
|
before(:each) do
|
@@ -34,25 +35,93 @@ describe Sorted::ActionView do
|
|
34
35
|
end
|
35
36
|
|
36
37
|
it "should integrate with ActionView::Base" do
|
37
|
-
ActionView::Base.new.should respond_to(:
|
38
|
+
ActionView::Base.new.should respond_to(:sorted)
|
38
39
|
end
|
39
40
|
|
40
|
-
it "should return a hash
|
41
|
-
|
42
|
-
|
41
|
+
it "should return a nice hash from the order sql" do
|
42
|
+
sorter = Sorted::Sorter.new("email ASC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
|
43
|
+
sorter.order_queue.should == [["email", "asc"], ["phone", "asc"], ["name", "desc"]]
|
43
44
|
end
|
44
45
|
|
45
|
-
it "should return a hash
|
46
|
-
|
47
|
-
|
46
|
+
it "should return a nice hash from the sort params" do
|
47
|
+
sorter = Sorted::Sorter.new("email ASC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
|
48
|
+
sorter.sort_queue.should == [["email", "desc"], ["name", "desc"]]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should not toggle the sort order and include any sql orders not in sort params" do
|
52
|
+
sorter = Sorted::Sorter.new("email DESC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
|
53
|
+
sorter.to_hash.should == {"email" => "desc", "name" => "desc", "phone" => "asc"}
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should toggle the sort order and include any sql orders not in sort params" do
|
57
|
+
sorter = Sorted::Sorter.new("email DESC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
|
58
|
+
sorter.toggle.to_hash.should == {"email" => "asc", "name" => "asc", "phone" => "asc"}
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not change the direction of name using view helper" do
|
62
|
+
@controller.params = {:sort => "name_desc!email_asc"}
|
63
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
64
|
+
sorter.toggle.to_hash.should == {"email" => "desc", "name" => "desc"}
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should reverse email direction using view helper" do
|
68
|
+
@controller.params = {:sort => "email_asc!name_desc"}
|
69
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
70
|
+
sorter.toggle.to_hash.should == {"email" => "desc", "name" => "desc"}
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should reverse email direction using view helper" do
|
74
|
+
@controller.params = {:sort => "phone_desc!name_desc!email_desc"}
|
75
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
76
|
+
sorter.toggle.params.should == {:sort => "email_asc!phone_desc!name_desc"}
|
77
|
+
@controller.params = sorter.params
|
78
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
79
|
+
sorter.toggle.params.should == {:sort => "email_desc!phone_desc!name_desc"}
|
80
|
+
@controller.params = sorter.params
|
81
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:phone)
|
82
|
+
sorter.toggle.params.should == {:sort => "phone_asc!email_desc!name_desc"}
|
83
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:name)
|
84
|
+
sorter.toggle.params.should == {:sort => "name_asc!email_desc!phone_desc"}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should should fail becasue the sort order is incorect" do
|
88
|
+
sorter = Sorted::Sorter.new(:jsci_complete, {:sort => "parent_id_desc!non_vocational_complete_desc!jsci_complete_desc"})
|
89
|
+
sorter.toggle
|
90
|
+
sorter.to_s.should_not == "parent_id_desc!non_vocational_complete_desc!jsci_complete_desc"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return a hash of options for url builder with sorted query string" do
|
94
|
+
@controller.params = {:sort => "email_asc!name_desc", :page => 2}
|
95
|
+
ActionView::Base.new([], {}, @controller).sorted(:email).params.should == {:sort => "email_desc!name_desc", :page => 2}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should return an sql sort string" do
|
99
|
+
Sorted::Sorter.new(:email).to_sql.should == "email ASC"
|
100
|
+
Sorted::Sorter.new(:email, {:sort => "name_desc!email_desc"}).to_sql.should == "name DESC, email DESC"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should handle a large initial order string" do
|
104
|
+
sorter = Sorted::Sorter.new('email ASC, name DESC, phone ASC', {:sort => "email_desc!name_desc"})
|
105
|
+
sorter.to_sql.should == "email DESC, name DESC, phone ASC"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should handle a large initial order string" do
|
109
|
+
sorter = Sorted::Sorter.new('email ASC, phone DESC, name ASC', {:sort => "email_desc!name_desc"})
|
110
|
+
sorter.to_sql.should == "email DESC, name DESC, phone DESC"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not die if params nil" do
|
114
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
115
|
+
sorter.params.should == {:sort => "email_asc"}
|
48
116
|
end
|
49
117
|
|
50
|
-
it "should
|
51
|
-
|
52
|
-
|
118
|
+
it "should have some roll your own methods" do
|
119
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
120
|
+
sorter.to_s.should == "email_asc"
|
53
121
|
end
|
54
122
|
|
55
|
-
it "should
|
56
|
-
ActionView::Base.new([], {}, @controller).
|
123
|
+
it "should return css class" do
|
124
|
+
sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
|
125
|
+
sorter.css_class.should == "sorted-asc"
|
57
126
|
end
|
58
127
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Rufus Post
|
@@ -14,40 +14,11 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-07-
|
17
|
+
date: 2010-07-23 00:00:00 +10:00
|
18
18
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
21
|
-
|
22
|
-
prerelease: false
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
-
none: false
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
segments:
|
29
|
-
- 3
|
30
|
-
- 0
|
31
|
-
- 0
|
32
|
-
version: 3.0.0
|
33
|
-
type: :runtime
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: rspec
|
37
|
-
prerelease: false
|
38
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ">="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
segments:
|
44
|
-
- 1
|
45
|
-
- 2
|
46
|
-
- 9
|
47
|
-
version: 1.2.9
|
48
|
-
type: :development
|
49
|
-
version_requirements: *id002
|
50
|
-
description: lets you sort tables (or somthing else) using a view helper and a custom scope
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: lets you sort large data sets using view helpers and a scope
|
51
22
|
email: rufuspost@gmail.com
|
52
23
|
executables: []
|
53
24
|
|