sorted 0.3.8 → 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -1,5 +1,3 @@
1
1
  README.rdoc
2
2
  lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
3
  LICENSE
data/README.rdoc CHANGED
@@ -1,87 +1,109 @@
1
1
  = sorted
2
2
 
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.
3
+ Sorted is a simple object that will take an sql order string and a url
4
+ sort string to let you sort large datasets over many pages (using
5
+ {will_paginate}[http://github.com/mislav/will_paginatea] or
6
+ {kaminari}[https://github.com/amatsuda/kaminari]) without loosing state.
4
7
 
5
- Using Rails master it will create a sorted scope and a two view helpers
6
-
7
- == Example
8
+ If you would like to see it in action clone the
9
+ {example app}[https://github.com/mynameisrufus/sorted_app]
8
10
 
9
11
  === Gemfile
10
12
 
11
- gem sorted
13
+ gem 'sorted' '~> 0.3.9'
12
14
 
13
15
  === View
14
16
 
15
17
  link_to_sorted "Email", :email
16
18
 
17
- This will make a url like this:
19
+ This will generate a link with a url like this:
18
20
 
19
21
  http://myapp/users?sort=email_asc
20
22
 
21
- or on the next page load when you then sort by something else....
23
+ and on the next page load when you then sort by something else....
22
24
 
23
25
  http://myapp/users?sort=name_asc!email_asc
24
26
 
25
27
  === Model
26
28
 
27
- This will initially sort by email ascending:
29
+ This will initially sort by email ascending with the optional second
30
+ default order argument:
31
+
32
+ @users = User.sort(params[:sort], "email ASC").page(params[:page])
33
+
34
+ ==== Joins and includes
35
+
36
+ If you want to sort by a belongs to relationship add the table name to
37
+ the order argument. For example assuming a user belongs to a company:
38
+
39
+ @users = User.joins(:company).sort(params[:sort], "companies.name ASC").page(params[:page])
40
+
41
+ When generating links using the +link_to_sorted+ method you should
42
+ specify the table for every attribute you use as well otherwise you
43
+ will probably get an ambiguous column name error.
44
+
45
+ <th class="ui-state-default"><%= link_to_sorted "Company name", 'companies.name' %></th>
46
+ <th class="ui-state-default"><%= link_to_sorted "Users name", 'users.name' %></th>
47
+
48
+ ==== Typecasting and DB functions
28
49
 
29
- @users = User.sorted(:order => "email ASC", :sort => params[:sort]).paginate(:page => params[:page])
50
+ Do your type casting and function calls in your select statement with an
51
+ alias, for example:
30
52
 
31
- Or you can just clone the example app https://github.com/mynameisrufus/sorted_app.
53
+ # controller
54
+ @users = User.select("income::BigInt as bg_income").sort(params[:sort], "bg_income ASC")
55
+
56
+ # view
57
+ <th class="ui-state-default"><%= link_to_sorted "Income", 'bg_income' %></th>
32
58
 
33
- If you want to sort by a belongs_to relationship, just provide sort order as "RELATIONS.COLUMN ASC|DESC" where
34
- RELATIONS is the name of the relationship table and COLUMN is an attribute in that table. For example,
35
- assuming the User model belongs_to a :company.
59
+ or using a DB function:
36
60
 
37
- @users = User.sorted(:order => "companies.name asc", :sort => params[:sort]).paginate(:page => params[:page])
61
+ # controller
62
+ @users = User.select("inet_aton(`ip_address`) AS in_ip").sort(params[:sort], "in_ip ASC")
63
+
64
+ # view
65
+ <th class="ui-state-default"><%= link_to_sorted "IP address", 'in_ip' %></th>
38
66
 
39
67
  == Presentation
40
68
 
41
- You might want to roll your own link_to_sorted method to use jQuery ui css classes for example, all you need is the sorted object.
69
+ You might want to roll your own +link_to_sorted+ method to use jQuery ui
70
+ css classes for example, all you need is the sorted object.
42
71
 
43
72
  def link_to_sorted(name, order)
44
- sorter = sorted(order)
45
- css_class = case sorter.to_css
46
- when "sorted asc"
47
- "ui-icon ui-icon-triangle-1-n"
48
- when "sorted desc"
49
- "ui-icon ui-icon-triangle-1-s"
50
- when "sorted"
51
- "ui-icon ui-icon-carat-2-n-s"
52
- end
73
+ dup_params = (request.get? && !params.nil?) ? params.dup : nil
74
+ sorter = ActionView::Base::SortedViewHelper.new(order, dup_params)
75
+ css_class = case sorter.css
76
+ when "sorted asc"
77
+ "ui-icon ui-icon-triangle-1-n"
78
+ when "sorted desc"
79
+ "ui-icon ui-icon-triangle-1-s"
80
+ when "sorted"
81
+ "ui-icon ui-icon-carat-2-n-s"
82
+ end
53
83
  link_to(content_tag(:span, nil, {:class => css_class}) + name.to_s, sorter.params)
54
84
  end
55
85
 
56
- Tables are best displayed with alternating shades for each row, so put this in your helper:
57
-
58
- def odd_even
59
- @odd_even ||= 0
60
- @odd_even += 1
61
- (@odd_even % 2) == 1 ? 'odd' : 'even'
62
- end
63
-
64
- Then in your table do something like this:
86
+ Tables are best displayed with alternating shades for each row, so add
87
+ an alternating class to you table rows using the rails +cycle+ method:
65
88
 
66
- <tr class="<%= odd_even %>">
89
+ <tr class="<%= cycle 'odd', 'even' %>">
67
90
 
68
91
  == Rails 2.3.x
69
92
 
70
- This gem works with rails 2.3.x but you will have to roll your own scope and view helper.
93
+ Sorted works with rails 2.3.x but you will have to roll your own scope
94
+ and view helper.
71
95
 
72
96
  Here is the named scope for your model(s):
73
97
 
74
- named_scope :sorted, lambda { |params|
75
- { :order => Sorted::Sorter.new(params[:order], {:sort => params[:sort]}).to_sql }
98
+ named_scope :sort, lambda { |sort, order|
99
+ { :order => Sorted::Parser.new(sort, order).to_sql }
76
100
  }
77
101
 
78
- and the the application helper methods:
102
+ and the application helper method:
79
103
 
80
- def sorted(order)
81
- Sorted::Sorter.new(order, (request.get? && !params.nil?) ? params.dup : nil).toggle
82
- end
83
-
84
- def link_to_sorted(name, order)
85
- sorter = sorted(order)
86
- link_to(name, sorter.params, {:class => sorter.to_css})
104
+ def link_to_sorted(name, order, options = {})
105
+ dup_params = (request.get? && !params.nil?) ? params.dup : nil
106
+ sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new(order, dup_params)
107
+ options[:class] = [options[:class], sorter.css].join(' ').strip
108
+ link_to(name.to_s, sorter.params, options)
87
109
  end
data/lib/sorted.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'sorted/sorter'
2
- require 'sorted/toggler'
1
+ require 'sorted/parser'
3
2
 
4
3
  module Sorted
5
4
  end
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+ require 'sorted'
3
+
4
+ module Sorted
5
+ module Orms
6
+ module ActiveRecord
7
+ def self.enable!
8
+ ::ActiveRecord::Base.class_eval do
9
+ def self.sort(sort, order = nil)
10
+ sorter = ::Sorted::Parser.new(sort, order)
11
+ order sorter.to_sql
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,79 @@
1
+ require 'sorted/toggler'
2
+
3
+ module Sorted
4
+ # Takes a sort query string and an SQL order string and parses the
5
+ # values to produce key value pairs.
6
+ #
7
+ # Example:
8
+ # Sorted::Parser.new('phone_desc', 'name ASC').to_s #-> "phone_desc!name_asc"
9
+ class Parser
10
+ attr_reader :sort, :order, :sorts, :orders
11
+
12
+ # Regex to make sure we only get valid names and not injected code.
13
+ SORTED_QUERY_REGEX = /([a-zA-Z0-9._]+)_(asc|desc)$/
14
+ SQL_REGEX = /(([a-z0-9._]+)\s([asc|desc]+)|[a-z0-9._]+)/i
15
+
16
+ def initialize(sort, order = nil)
17
+ @sort = sort
18
+ @order = order
19
+ @sorts = parse_sort
20
+ @orders = parse_order
21
+ end
22
+
23
+ def parse_sort
24
+ sort.to_s.split(/!/).map do |sort_string|
25
+ if m = sort_string.match(SORTED_QUERY_REGEX)
26
+ [m[1], m[2].downcase]
27
+ end
28
+ end.compact
29
+ end
30
+
31
+ def parse_order
32
+ order.to_s.split(/,/).map do |order_string|
33
+ if m = order_string.match(SQL_REGEX)
34
+ [(m[2].nil? ? m[1] : m[2]),(m[3].nil? ? "asc" : m[3].downcase)]
35
+ end
36
+ end.compact
37
+ end
38
+
39
+ def to_hash
40
+ array.inject({}){|h,a| h.merge(Hash[a[0],a[1]])}
41
+ end
42
+
43
+ def to_sql
44
+ array.map{|a| "#{a[0]} #{a[1].upcase}" }.join(', ')
45
+ end
46
+
47
+ def to_s
48
+ array.map{|a| a.join('_') }.join('!')
49
+ end
50
+
51
+ def to_a
52
+ array
53
+ end
54
+
55
+ def toggle
56
+ @array = Toggler.new(sorts, orders).to_a
57
+ self
58
+ end
59
+
60
+ def reset
61
+ @array = default
62
+ self
63
+ end
64
+
65
+ private
66
+
67
+ def array
68
+ @array ||= default
69
+ end
70
+
71
+ def default
72
+ sorts_new = sorts.dup
73
+ orders.each do |o|
74
+ sorts_new << o unless sorts_new.flatten.include?(o[0])
75
+ end
76
+ sorts_new
77
+ end
78
+ end
79
+ end
@@ -4,8 +4,8 @@ module Sorted
4
4
  class Railtie < Rails::Railtie
5
5
  initializer "sorted.active_record" do |app|
6
6
  if defined? ::ActiveRecord
7
- require 'sorted/finders/active_record'
8
- Sorted::Finders::ActiveRecord.enable!
7
+ require 'sorted/orms/active_record'
8
+ Sorted::Orms::ActiveRecord.enable!
9
9
  end
10
10
  end
11
11
 
@@ -1,6 +1,13 @@
1
1
  module Sorted
2
+ # Takes a parsed arrays of sorts and orders, it then will reorder the
3
+ # pairs and flip the assendance of the first sort pair.
4
+ #
5
+ # Example:
6
+ # sorts = [['name', 'asc'], ['phone', 'desc']]
7
+ # oredrs = [['name', 'asc']]
8
+ # Sorted::Toggler.new(sorts, orders).to_a #-> [['name', 'desc'], ['phone', 'desc']]
2
9
  class Toggler
3
- def initialize(orders, sorts)
10
+ def initialize(sorts, orders)
4
11
  @array = []
5
12
  @sorts = sorts
6
13
  @orders = orders
@@ -1,3 +1,3 @@
1
1
  module Example
2
- VERSION = "0.3.8"
2
+ VERSION = "0.3.9"
3
3
  end
@@ -4,13 +4,28 @@ require 'sorted'
4
4
  module Sorted
5
5
  module ViewHelpers
6
6
  module ActionView
7
- def sorted(order)
8
- ::Sorted::Sorter.new(order, (request.get? && !params.nil?) ? params.dup : nil).toggle
7
+ class SortedViewHelper
8
+ attr_reader :params
9
+
10
+ def initialize(order, params)
11
+ sort = params.delete :sort
12
+ @params = params
13
+ @parser = ::Sorted::Parser.new(sort, order).toggle
14
+ @params[:sort] = @parser.to_s
15
+ end
16
+
17
+ def css
18
+ if @parser.sorts.flatten.include? @parser.orders[0][0]
19
+ "sorted #{@parser.sorts.assoc(@parser.orders[0][0]).last}"
20
+ else
21
+ "sorted"
22
+ end
23
+ end
9
24
  end
10
25
 
11
26
  def link_to_sorted(name, order, options = {})
12
- sorter = sorted(order)
13
- options[:class] = [options[:class], sorter.to_css].join(' ').strip
27
+ sorter = SortedViewHelper.new(order, ((request.get? && !params.nil?) ? params.dup : nil))
28
+ options[:class] = [options[:class], sorter.css].join(' ').strip
14
29
  link_to(name.to_s, sorter.params, options)
15
30
  end
16
31
  end
data/sorted.gemspec CHANGED
@@ -15,10 +15,9 @@ Gem::Specification.new do |s|
15
15
  s.rubyforge_project = "sorted"
16
16
 
17
17
  s.add_development_dependency "bundler", ">= 1.0.0"
18
- s.add_development_dependency "rails", ">= 3.0.0"
18
+ s.add_development_dependency "rails", ">= 3.1.2"
19
19
  s.add_development_dependency "rspec", ">= 2.0.0"
20
20
 
21
-
22
21
  s.files = `git ls-files`.split("\n")
23
22
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
24
23
  s.require_path = 'lib'
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sorted::Orms::ActiveRecord do
4
+ before(:each) do
5
+ Sorted::Orms::ActiveRecord.enable!
6
+ end
7
+
8
+ it "should integrate with ActiveRecord::Base" do
9
+ ActiveRecord::Base.should respond_to(:sort)
10
+ end
11
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sorted::Parser, "params parsing" do
4
+ it "should not raise if pased nil arguments" do
5
+ lambda { Sorted::Parser.new(nil, nil).toggle }.should_not raise_error
6
+ end
7
+
8
+ it "should return a nice array from the order sql" do
9
+ sort = nil
10
+ order = "email ASC, phone ASC, name DESC"
11
+ result = [["email", "asc"], ["phone", "asc"], ["name", "desc"]]
12
+
13
+ sorter = Sorted::Parser.new(sort, order)
14
+ sorter.orders.should eq result
15
+ end
16
+
17
+ it "should return a nice array from the sort params" do
18
+ sort = "email_desc!name_desc"
19
+ order = nil
20
+ result = [["email", "desc"], ["name", "desc"]]
21
+
22
+ sorter = Sorted::Parser.new(sort, order)
23
+ sorter.sorts.should eq result
24
+ end
25
+
26
+ it "should combine sort and order params with sort params being of higer importance" do
27
+ sort = "email_desc!name_desc"
28
+ order = "email ASC, phone ASC, name DESC"
29
+ result = [["email", "desc"], ["name", "desc"], ["phone", "asc"]]
30
+
31
+ sorter = Sorted::Parser.new(sort, order)
32
+ sorter.to_a.should eq result
33
+ end
34
+
35
+ it "should allow numbers, underscores and full stops in sort params" do
36
+ sort = "assessmentsTable.name_desc!users_300.name_5_desc"
37
+ order = nil
38
+ result = [["assessmentsTable.name", "desc"], ["users_300.name_5", "desc"]]
39
+
40
+ sorter = Sorted::Parser.new(sort, order)
41
+ sorter.sorts.should eq result
42
+ end
43
+
44
+ it "should allow numbers, underscores and full stops in order params" do
45
+ sort = nil
46
+ order = "assessmentsTable.name ASC, users_300.name_5 ASC"
47
+ result = [["assessmentsTable.name", "asc"], ["users_300.name_5", "asc"]]
48
+
49
+ sorter = Sorted::Parser.new(sort, order)
50
+ sorter.orders.should eq result
51
+ end
52
+
53
+ it "should default to asc if sort params order is ommited" do
54
+ sort = nil
55
+ order = :email
56
+ result = [["email", "asc"]]
57
+
58
+ sorter = Sorted::Parser.new(sort, order)
59
+ sorter.orders.should eq result
60
+ end
61
+ end
62
+
63
+ describe Sorted::Parser, "return types" do
64
+ it "should return an sql sort string" do
65
+ sort = "email_desc!name_desc"
66
+ order = "email ASC, phone ASC, name DESC"
67
+ result = "email DESC, name DESC, phone ASC"
68
+
69
+ sorter = Sorted::Parser.new(sort, order)
70
+ sorter.to_sql.should eq result
71
+ end
72
+
73
+ it "should return an hash" do
74
+ sort = "email_desc!name_desc"
75
+ order = "email ASC, phone ASC, name DESC"
76
+ result = {"email" => "desc", "name" => "desc", "phone" => "asc"}
77
+
78
+ sorter = Sorted::Parser.new(sort, order)
79
+ sorter.to_hash.should eq result
80
+ end
81
+
82
+ it "should return an the encoded sort string" do
83
+ sort = "email_desc!name_desc"
84
+ order = "email ASC, phone ASC, name DESC"
85
+ result = "email_desc!name_desc!phone_asc"
86
+
87
+ sorter = Sorted::Parser.new(sort, order)
88
+ sorter.to_s.should eq result
89
+ end
90
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sorted::Toggler do
4
+ it "should bring phone to first order importance but not toggle ascendance" do
5
+ orders = [["email", "asc"], ["phone", "asc"]]
6
+ sorts = [["phone", "asc"]]
7
+ result = [["phone", "asc"], ["email", "asc"]]
8
+
9
+ toggler = Sorted::Toggler.new(sorts, orders)
10
+ toggler.to_a.should eq result
11
+ end
12
+
13
+ it "should toggle ascendance of email" do
14
+ orders = [["email", "desc"]]
15
+ sorts = [["email", "asc"]]
16
+ result = [["email", "desc"]]
17
+
18
+ toggler = Sorted::Toggler.new(sorts, orders)
19
+ toggler.to_a.should eq result
20
+ end
21
+
22
+ it "should return both order params un-toggled with no sort param" do
23
+ orders = [["email", "asc"], ["phone", "asc"]]
24
+ sorts = []
25
+ result = [["email", "asc"], ["phone", "asc"]]
26
+
27
+ toggler = Sorted::Toggler.new(sorts, orders)
28
+ toggler.to_a.should eq result
29
+ end
30
+
31
+ it "should toggle the email ascendance" do
32
+ orders = [["email", "asc"]]
33
+ sorts = [["email", "asc"], ["phone", "asc"]]
34
+ result = [["email", "desc"], ["phone", "asc"]]
35
+
36
+ toggler = Sorted::Toggler.new(sorts, orders)
37
+ toggler.to_a.should eq result
38
+ end
39
+
40
+ it "should toggle the email ascendance" do
41
+ orders = [["email", "desc"]]
42
+ sorts = [["email", "asc"], ["phone", "asc"]]
43
+ result = [["email", "desc"], ["phone", "asc"]]
44
+
45
+ toggler = Sorted::Toggler.new(sorts, orders)
46
+ toggler.to_a.should eq result
47
+ end
48
+
49
+ it "should toggle two 1..n sort values" do
50
+ orders = [["email", "asc"], ["phone", "asc"]]
51
+ sorts = [["email", "asc"], ["phone", "asc"]]
52
+ result = [["email", "desc"], ["phone", "desc"]]
53
+
54
+ toggler = Sorted::Toggler.new(sorts, orders)
55
+ toggler.to_a.should eq result
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sorted::ViewHelpers::ActionView do
4
+ it "should integrate with ActiveRecord::Base" do
5
+ ActionView::Base.send(:include, Sorted::ViewHelpers::ActionView)
6
+ ActionView::Base.new.should respond_to(:link_to_sorted)
7
+ end
8
+ end
9
+
10
+ describe Sorted::ViewHelpers::ActionView::SortedViewHelper do
11
+ it "should return the default sort order and preserve the existing params" do
12
+ order = :email
13
+ params = { :page => 10 }
14
+ result = { :page => 10, :sort => "email_asc" }
15
+
16
+ sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
17
+ sorter.params.should eq result
18
+ end
19
+
20
+ it "should only return the sorted css class if email has not yet been sorted" do
21
+ order = :email
22
+ params = {}
23
+ result = "sorted"
24
+
25
+ sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
26
+ sorter.css.should eq result
27
+ end
28
+
29
+ it "should only return the sorted css class if email has not yet been sorted" do
30
+ order = :email
31
+ params = { :sort => "email_asc" }
32
+ result = "sorted asc"
33
+
34
+ sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
35
+ sorter.css.should eq result
36
+ end
37
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'sorted/finders/active_record'
3
+ require 'sorted'
4
+ require 'sorted/orms/active_record'
4
5
  require 'sorted/view_helpers/action_view'
5
6
  require 'rspec'
6
- require 'rspec/autorun'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorted
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-04 00:00:00.000000000Z
12
+ date: 2011-11-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
- requirement: &70138519878420 !ruby/object:Gem::Requirement
16
+ requirement: &70134746957060 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,21 +21,21 @@ dependencies:
21
21
  version: 1.0.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70138519878420
24
+ version_requirements: *70134746957060
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rails
27
- requirement: &70138519876540 !ruby/object:Gem::Requirement
27
+ requirement: &70134746956600 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
31
31
  - !ruby/object:Gem::Version
32
- version: 3.0.0
32
+ version: 3.1.2
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70138519876540
35
+ version_requirements: *70134746956600
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70138519874500 !ruby/object:Gem::Requirement
38
+ requirement: &70134746956100 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 2.0.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70138519874500
46
+ version_requirements: *70134746956100
47
47
  description: lets you sort large data sets using view helpers and a scope
48
48
  email:
49
49
  - rufuspost@gmail.com
@@ -53,23 +53,23 @@ extra_rdoc_files: []
53
53
  files:
54
54
  - .document
55
55
  - .gitignore
56
- - .watchr
57
56
  - Gemfile
58
57
  - LICENSE
59
58
  - README.rdoc
60
59
  - Rakefile
61
60
  - lib/sorted.rb
62
- - lib/sorted/finders/active_record.rb
61
+ - lib/sorted/orms/active_record.rb
62
+ - lib/sorted/parser.rb
63
63
  - lib/sorted/railtie.rb
64
- - lib/sorted/sorter.rb
65
64
  - lib/sorted/toggler.rb
66
65
  - lib/sorted/version.rb
67
66
  - lib/sorted/view_helpers/action_view.rb
68
67
  - sorted.gemspec
69
- - spec/sorted_spec.rb
70
- - spec/sorter_spec.rb
68
+ - spec/sorted/orms/active_record_spec.rb
69
+ - spec/sorted/parser_spec.rb
70
+ - spec/sorted/toggler_spec.rb
71
+ - spec/sorted/view_helpers/action_view_spec.rb
71
72
  - spec/spec_helper.rb
72
- - spec/toggler_spec.rb
73
73
  homepage: http://rubygems.org/gems/sorted
74
74
  licenses: []
75
75
  post_install_message:
@@ -84,7 +84,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
84
  version: '0'
85
85
  segments:
86
86
  - 0
87
- hash: 1254694282220959241
87
+ hash: -354722037037653326
88
88
  required_rubygems_version: !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
@@ -93,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  version: 1.3.6
94
94
  requirements: []
95
95
  rubyforge_project: sorted
96
- rubygems_version: 1.8.10
96
+ rubygems_version: 1.8.11
97
97
  signing_key:
98
98
  specification_version: 3
99
99
  summary: sort data with a database
data/.watchr DELETED
@@ -1,24 +0,0 @@
1
- module WatchrActions
2
- def self.run_spec(file)
3
- unless File.exist?(file)
4
- puts "#{file} does not exist"
5
- return
6
- end
7
-
8
- puts "Running #{file}"
9
- system "rspec #{file}"
10
- end
11
- end
12
-
13
- watch(/^lib\/(?:sorted\/)?(.*).rb/) do |m|
14
- WatchrActions.run_spec("spec/#{m[1]}_spec.rb")
15
- end
16
-
17
- watch(/^spec\/.*_spec.rb/) do |m|
18
- WatchrActions.run_spec(m[0])
19
- end
20
-
21
- watch(/^lib\/sorted\/finders\/active_record.rb/) do |m|
22
- WatchrActions.run_spec('spec/sorted_spec.rb')
23
- end
24
-
@@ -1,41 +0,0 @@
1
- require 'active_record'
2
- require 'sorted'
3
-
4
- module Sorted
5
- module Finders
6
- module ActiveRecord
7
- def self.enable!
8
- ::ActiveRecord::Base.class_eval do
9
- # Define a symbolic sort column. This allows mapping a simple column name
10
- # to a more complex sql order clause.
11
- # class Model < ActiveRecord::Base
12
- # symbolic_sort :ip_address, 'inet_aton(`ip_address`)'
13
- # end
14
- def self.symbolic_sort(key, sql)
15
- symbolic_sorts[key] = sql
16
- end
17
-
18
- def self.sorted(params)
19
- sorter = ::Sorted::Sorter.new(params[:order], :sort => params[:sort], :symbolic_sorts => symbolic_sorts)
20
- # Check if we parsed some relationship includes and apply them
21
- # before applying the order
22
- relation = self
23
- if sorter.includes.size > 0
24
- # Remove self.name from includes
25
- my_name = self.name.to_sym
26
- real_includes = sorter.includes.delete_if{|include_name| include_name == my_name}
27
- relation = includes(real_includes) if real_includes.size > 0
28
- end
29
- relation.order(sorter.to_sql)
30
- end
31
-
32
- private
33
-
34
- def self.symbolic_sorts
35
- @symbolic_sorts ||= {}
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end
data/lib/sorted/sorter.rb DELETED
@@ -1,122 +0,0 @@
1
- module Sorted
2
- class Sorter
3
- attr_reader :includes
4
- def initialize(order, params = nil)
5
- @includes = []
6
- @symbolic_sorts = {}
7
-
8
- if order.is_a?(String) || order.is_a?(Symbol)
9
- parse_order(order)
10
- end
11
- if params.is_a?(Hash)
12
- @params = params
13
- if @params[:sort].is_a?(String)
14
- parse_sort @params[:sort]
15
- end
16
- @symbolic_sorts = @params[:symbolic_sorts] || {}
17
- end
18
- end
19
-
20
- def parse_sort(sort_string)
21
- sort_string.split(/!/).each do |sort|
22
- if parsed = parse_query(sort)
23
- sorts << parsed
24
- end
25
- end
26
- end
27
-
28
- def parse_order(order_string_or_symbol)
29
- order_string_or_symbol.to_s.split(/,/).each do |order|
30
- if parsed = parse_sql(order)
31
- orders << parsed
32
- end
33
- end
34
- end
35
-
36
- def parse_query(sort)
37
- if m = sort.match(/([a-zA-Z0-9._]+)_(asc|desc)$/)
38
- parse_include(m[1])
39
- [m[1],m[2]]
40
- end
41
- end
42
-
43
- def parse_sql(order)
44
- if m = order.match(/(([a-zA-Z._:][a-zA-Z._:0-9]*)\s([asc|ASC|desc|DESC]+)|[a-zA-Z._:][a-zA-Z._:0-9]*)/)
45
- sort_column = (m[2].nil? ? m[1] : m[2])
46
- parse_include(sort_column)
47
- [sort_column,(m[3].nil? ? "asc" : m[3].downcase)]
48
- end
49
- end
50
-
51
- def parse_include(order)
52
- if match_data = /^([^\.]+)\..+/.match(order)
53
- include_name = match_data[1].singularize.to_sym
54
- @includes << include_name unless @includes.include?(include_name)
55
- end
56
- end
57
-
58
- def toggle
59
- @array = Toggler.new(orders, sorts).to_a
60
- self
61
- end
62
-
63
- def reset
64
- @array = default
65
- self
66
- end
67
-
68
- def to_hash
69
- array.inject({}){|h,a| h.merge(Hash[a[0],a[1]])}
70
- end
71
-
72
- def to_sql
73
- array.map do |a|
74
- column = @symbolic_sorts[a[0].to_sym] || a[0]
75
- "#{column} #{a[1].upcase}"
76
- end.join(', ')
77
- end
78
-
79
- def to_s
80
- array.map{|a| a.join('_')}.join('!')
81
- end
82
-
83
- def to_a
84
- array
85
- end
86
-
87
- def to_css
88
- if sorts.flatten.include?(orders[0][0])
89
- "sorted #{sorts.assoc(orders[0][0]).last}"
90
- else
91
- "sorted"
92
- end
93
- end
94
-
95
- def params
96
- @params ||= {}
97
- @params[:sort] = to_s
98
- @params
99
- end
100
-
101
- def orders
102
- @orders ||= []
103
- end
104
-
105
- def sorts
106
- @sorts ||= []
107
- end
108
-
109
- private
110
- def default
111
- sorts_new = sorts.dup
112
- orders.each do |order|
113
- sorts_new << order unless sorts_new.flatten.include?(order[0])
114
- end
115
- sorts_new
116
- end
117
-
118
- def array
119
- @array ||= default
120
- end
121
- end
122
- end
data/spec/sorted_spec.rb DELETED
@@ -1,122 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'action_controller'
3
- require 'sorted'
4
-
5
- describe Sorted::Finders::ActiveRecord do
6
- before(:each) do
7
- Sorted::Finders::ActiveRecord.enable!
8
- end
9
-
10
- it "should integrate with ActiveRecord::Base" do
11
- ActiveRecord::Base.should respond_to(:sorted)
12
- end
13
-
14
- it "should define a symbolic_sorts method" do
15
- ActiveRecord::Base.should respond_to(:symbolic_sort)
16
- end
17
-
18
- it "should define symbolic_sorts" do
19
- a = Class.new(ActiveRecord::Base)
20
- b = Class.new(ActiveRecord::Base)
21
-
22
- a.symbolic_sort(:foo, 'foo')
23
- b.symbolic_sort(:bar, 'bar')
24
-
25
- a.instance_variable_get(:@symbolic_sorts).should == {:foo => 'foo'}
26
- b.instance_variable_get(:@symbolic_sorts).should == {:bar => 'bar'}
27
- end
28
-
29
- it "should add orders to the relation" do
30
- a = Class.new(ActiveRecord::Base)
31
- relation = a.sorted(:order => nil, :sort => 'a_asc')
32
- relation.order_values.should == ["a ASC"]
33
- end
34
-
35
- it "should add orders to the relation using symbolic_sorts" do
36
- a = Class.new(ActiveRecord::Base)
37
- a.symbolic_sort(:ip, 'inet_aton(`ip`)')
38
- relation = a.sorted(:order => nil, :sort => 'ip_asc')
39
- relation.order_values.should == ["inet_aton(`ip`) ASC"]
40
- end
41
- end
42
-
43
- describe Sorted::ViewHelpers::ActionView do
44
- before(:each) do
45
- class TestController
46
- def params
47
- @params ||= {}
48
- end
49
-
50
- def params=(params)
51
- @params = params
52
- end
53
-
54
- def request
55
- Request.new
56
- end
57
-
58
- def _prefixes
59
- end
60
- end
61
- class Request
62
- def get?; true end
63
- end
64
- @controller = TestController.new
65
- ActionView::Base.send(:include, Sorted::ViewHelpers::ActionView)
66
- end
67
-
68
- it "should integrate with ActionView::Base" do
69
- ActionView::Base.new.should respond_to(:sorted)
70
- end
71
-
72
- it "should integrate with ActionView::Base" do
73
- ActionView::Base.new.should respond_to(:link_to_sorted)
74
- end
75
-
76
- it "should not change the direction of name using view helper" do
77
- @controller.params = {:sort => "name_desc!email_asc"}
78
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
79
- sorter.toggle.to_a.should == [["email", "asc"], ["name", "desc"]]
80
- end
81
-
82
- it "should reverse email direction using view helper" do
83
- @controller.params = {:sort => "email_asc!name_desc"}
84
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
85
- sorter.toggle.to_a.should == [["email", "desc"], ["name", "desc"]]
86
- end
87
-
88
- it "should reverse email direction using view helper" do
89
- @controller.params = {:sort => "email_desc!name_desc!phone_desc"}
90
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
91
- sorter.toggle.params.should == {:sort => "email_asc!name_desc!phone_desc"}
92
- @controller.params = sorter.params
93
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
94
- sorter.toggle.params.should == {:sort => "email_desc!name_desc!phone_desc"}
95
- end
96
-
97
- it "should not reverse order when made first again" do
98
- @controller.params = {:sort => "phone_desc!name_desc!email_desc"}
99
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
100
- sorter.toggle.params.should == {:sort => "email_desc!phone_desc!name_desc"}
101
- sorter = ActionView::Base.new([], {}, @controller).sorted(:name)
102
- sorter.toggle.params.should == {:sort => "name_desc!phone_desc!email_desc"}
103
- end
104
-
105
- it "should order correctly" do
106
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
107
- sorter.toggle.params.should == {:sort => "email_asc"}
108
- @controller.params = sorter.params
109
- sorter = ActionView::Base.new([], {}, @controller).sorted(:name)
110
- sorter.toggle.params.should == {:sort => "name_asc!email_asc"}
111
- end
112
-
113
- it "should return a hash of options for url builder with sorted query string" do
114
- @controller.params = {:sort => "email_asc!name_desc", :page => 2}
115
- ActionView::Base.new([], {}, @controller).sorted(:email).params.should == {:sort => "email_desc!name_desc", :page => 2}
116
- end
117
-
118
- it "should not die if params nil" do
119
- sorter = ActionView::Base.new([], {}, @controller).sorted(:email)
120
- sorter.params.should == {:sort => "email_asc"}
121
- end
122
- end
data/spec/sorter_spec.rb DELETED
@@ -1,65 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'action_controller'
3
- require 'sorted'
4
-
5
- describe Sorted::Sorter, "parse methods" do
6
- it "should not die if pased dumb things" do
7
- lambda { Sorted::Sorter.new(false, Time.now).should_not }.should_not raise_error
8
- end
9
-
10
- it "should return a nice array from the order sql" do
11
- sorter = Sorted::Sorter.new("email ASC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
12
- sorter.orders.should == [["email", "asc"], ["phone", "asc"], ["name", "desc"]]
13
- end
14
-
15
- it "should return a nice array from the sort params" do
16
- sorter = Sorted::Sorter.new("email ASC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
17
- sorter.sorts.should == [["email", "desc"], ["name", "desc"]]
18
- end
19
-
20
- it "should allow numbers, underscores, full stops and colons in" do
21
- sorter = Sorted::Sorter.new('users.email ASC, users.phone_number DESC, assessments.name ASC, assessments.number_as_string::BigInt, assessments.column_with_number_123', {:sort => "users.email_desc!users.first_name_desc"})
22
- sorter.to_sql.should == "users.email DESC, users.first_name DESC, users.phone_number DESC, assessments.name ASC, assessments.number_as_string::BigInt ASC, assessments.column_with_number_123 ASC"
23
- sorter.includes.size.should == 2
24
- end
25
- end
26
-
27
- describe Sorted::Sorter, "logic:" do
28
- it "should not toggle the sort order and include any sql orders not in sort params" do
29
- sorter = Sorted::Sorter.new("email ASC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
30
- sorter.to_a.should == [["email", "desc"], ["name", "desc"], ["phone", "asc"]]
31
- sorter.includes.size.should == 0
32
- end
33
-
34
- it "should return an sql sort string" do
35
- Sorted::Sorter.new(:email).to_sql.should == "email ASC"
36
- Sorted::Sorter.new(:email, {:sort => "name_desc!email_desc"}).to_sql.should == "name DESC, email DESC"
37
- end
38
-
39
- it "should handle a large initial order string" do
40
- sorter = Sorted::Sorter.new('email ASC, name DESC, phone ASC', {:sort => "email_desc!name_desc"})
41
- sorter.to_sql.should == "email DESC, name DESC, phone ASC"
42
- end
43
-
44
- it "should handle a large initial order string" do
45
- sorter = Sorted::Sorter.new('email ASC, phone DESC, name ASC', {:sort => "email_desc!name_desc"})
46
- sorter.to_sql.should == "email DESC, name DESC, phone DESC"
47
- end
48
-
49
- it "should substitute symbolic sorts" do
50
- sorter = Sorted::Sorter.new('ip_address', :symbolic_sorts => {:ip_address => 'inet_aton(`ip_address`)'})
51
- sorter.to_sql.should == "inet_aton(`ip_address`) ASC"
52
- end
53
- end
54
-
55
- describe Sorted::Sorter, "to_css" do
56
- it "should return css base class for to_css if not in sort params" do
57
- sorter = Sorted::Sorter.new(:email)
58
- sorter.to_css.should == "sorted"
59
- end
60
-
61
- it "should return css class for to_css" do
62
- sorter = Sorted::Sorter.new(:email, {:sort => "email_desc"})
63
- sorter.to_css.should == "sorted desc"
64
- end
65
- end
data/spec/toggler_spec.rb DELETED
@@ -1,39 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'action_controller'
3
- require 'sorted'
4
-
5
- describe Sorted::Toggler, "toggle sorts" do
6
- it "should should fail becasue the sort order is incorect" do
7
- sorter = Sorted::Sorter.new(:jsci_complete, {:sort => "parent_id_desc!non_vocational_complete_desc!jsci_complete_desc"})
8
- sorter.toggle
9
- sorter.to_s.should_not == "parent_id_desc!non_vocational_complete_desc!jsci_complete_desc"
10
- end
11
-
12
- it "should toggle two order params at once" do
13
- first = Sorted::Sorter.new("email ASC, phone ASC")
14
- first.toggle.to_a.should == [["email", "asc"],["phone", "asc"]]
15
- second = Sorted::Sorter.new(:name, {:sort => first.to_s})
16
- second.toggle.to_a.should == [["name", "asc"], ["email", "asc"],["phone", "asc"]]
17
- third = Sorted::Sorter.new("email ASC, phone ASC", {:sort => second.to_s})
18
- third.toggle.to_a.should == [["email", "asc"],["phone", "asc"], ["name", "asc"]]
19
- end
20
- end
21
-
22
- describe Sorted::Toggler, "add remaining orders" do
23
- it "should toggle the sort order and include any sql orders not in sort params" do
24
- sorter = Sorted::Sorter.new("email DESC, phone ASC, name DESC", {:sort => "email_desc!name_desc"})
25
- sorter.toggle.to_a.should == [["email", "desc"], ["name", "desc"], ["phone", "asc"]]
26
- end
27
-
28
- it "should toggle the sort order and include any sql orders not in sort params" do
29
- sorter = Sorted::Sorter.new("email DESC, phone ASC, name DESC", {:sort => "mobile_asc!email_desc!phone_asc!name_desc"})
30
- sorter.toggle.to_a.should == [["email", "desc"], ["phone", "asc"], ["name", "desc"], ["mobile", "asc"]]
31
- end
32
- end
33
-
34
- describe Sorted::Toggler, "add remaining sorts" do
35
- it "should toggle the sort order and include any sort orders not in order params" do
36
- sorter = Sorted::Sorter.new("email DESC", {:sort => "email_desc!name_desc"})
37
- sorter.toggle.to_a.should == [["email", "asc"], ["name", "desc"]]
38
- end
39
- end