sorted 0.3.8 → 0.3.9

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