Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Updates. Better docs & support for Rails 3.

  • Loading branch information...
commit 86eb6064fdfa3ae1b737335519970534d02e980a 1 parent b3c6806
Alex Fortuna authored July 29, 2011
13  .gitignore
... ...
@@ -1,16 +1,9 @@
1  
-# General Ruby.
2  
-.old*/
  1
+# General Ruby, sorted by first letter.
  2
+.old*
3 3
 *-old*
4  
-*-old*/
5  
-/*.patch
6  
-.proto*
7  
-.proto*/
8  
-*.rbx
9 4
 .ref*
10  
-.ref*/
11 5
 
12 6
 # Project-specific.
13  
-/dev/
14 7
 /doc/
15 8
 /pkg/
16  
-/README.html
  9
+/.rvmrc
2  MIT-LICENSE
... ...
@@ -1,4 +1,4 @@
1  
-Copyright (c) 2010 Alex Fortuna
  1
+Copyright (c) 2010-2011 Alex Fortuna
2 2
 
3 3
 Permission is hereby granted, free of charge, to any person obtaining
4 4
 a copy of this software and associated documentation files (the
140  README.html
... ...
@@ -0,0 +1,140 @@
  1
+<head>
  2
+  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
  3
+  <link href="dev/github.css" rel="stylesheet" type="text/css" />
  4
+</head>
  5
+
  6
+<h1 id="sortable-table-columns">Sortable Table Columns</h1>
  7
+
  8
+<h2 id="introduction">Introduction</h2>
  9
+
  10
+<p>A simple yet flexible Rails gem/plugin to quickly add sortable table columns to your controller and views.</p>
  11
+
  12
+<h2 id="setup-rails-3">Setup (Rails 3)</h2>
  13
+
  14
+<p>In your app’s <code>Gemfile</code>, add:</p>
  15
+
  16
+<pre><code>gem "handles_sortable_columns"
  17
+</code></pre>
  18
+
  19
+<p>To install the gem with RDoc/ri documentation, do a:</p>
  20
+
  21
+<pre><code>$ gem install handles_sortable_columns
  22
+</code></pre>
  23
+
  24
+<p>Otherwise, do a <code>bundle install</code>.</p>
  25
+
  26
+<h2 id="setup-rails-2">Setup (Rails 2)</h2>
  27
+
  28
+<p>In your app’s <code>config/environment.rb</code> add:</p>
  29
+
  30
+<pre><code>config.gem "handles_sortable_columns"
  31
+</code></pre>
  32
+
  33
+<p>To install the gem, do a:</p>
  34
+
  35
+<pre><code>$ gem sources --add http://rubygems.org
  36
+$ gem install handles_sortable_columns
  37
+</code></pre>
  38
+
  39
+<p>, or use <code>rake gems:install</code>.</p>
  40
+
  41
+<h2 id="basic-usage">Basic Usage</h2>
  42
+
  43
+<p>Activate the feature in your controller class:</p>
  44
+
  45
+<pre><code>class MyController &lt; ApplicationController
  46
+  handles_sortable_columns
  47
+  ...
  48
+</code></pre>
  49
+
  50
+<p>In a view, mark up sortable columns by using the <tt>sortable_column</tt> helper:</p>
  51
+
  52
+<pre><code>&lt;%= sortable_column "Product" %&gt;
  53
+&lt;%= sortable_column "Price" %&gt;
  54
+</code></pre>
  55
+
  56
+<p>In controller action, fetch and use the order clause according to current state of sortable columns:</p>
  57
+
  58
+<pre><code>def index
  59
+  order = sortable_column_order
  60
+  @records = Article.order(order)           # Rails 3.
  61
+  @records = Article.all(:order =&gt; order)   # Rails 2.
  62
+end
  63
+</code></pre>
  64
+
  65
+<p>That’s it for basic usage. Production usage may require passing additional parameters to listed methods.</p>
  66
+
  67
+<h2 id="production-usage">Production Usage</h2>
  68
+
  69
+<p>Please take time to read the gem’s full <a href="http://rdoc.info/projects/dadooda/handles_sortable_columns">RDoc documentation</a>. This README has a limited coverage.</p>
  70
+
  71
+<h3 id="configuration">Configuration</h3>
  72
+
  73
+<p>Change names of GET parameters used for sorting and pagination:</p>
  74
+
  75
+<pre><code>class MyController &lt; ApplicationController
  76
+  handles_sortable_columns do |conf|
  77
+    conf.sort_param = "s"
  78
+    conf.page_param = "p"
  79
+  end
  80
+  ...
  81
+</code></pre>
  82
+
  83
+<p>Change CSS class of all sortable column <code>&lt;a&gt;</code> tags:</p>
  84
+
  85
+<pre><code>handles_sortable_columns do |conf|
  86
+  conf.class = "SortableLink"
  87
+  conf.indicator_class = {:asc =&gt; "AscSortableLink", :desc =&gt; "DescSortableLink"}
  88
+end
  89
+</code></pre>
  90
+
  91
+<p>Change how text-based sort indicator looks like:</p>
  92
+
  93
+<pre><code>handles_sortable_columns do |conf|
  94
+  conf.indicator_text = {:asc =&gt; "[asc]", :desc =&gt; "[desc]"}
  95
+end
  96
+</code></pre>
  97
+
  98
+<p>Disable text-based sort indicator completely:</p>
  99
+
  100
+<pre><code>handles_sortable_columns do |conf|
  101
+  conf.indicator_text = {}
  102
+end
  103
+</code></pre>
  104
+
  105
+<h3 id="helper-options">Helper Options</h3>
  106
+
  107
+<p>Explicitly specify column name:</p>
  108
+
  109
+<pre><code>&lt;%= sortable_column "Highest Price", :column =&gt; "max_price" %&gt;
  110
+</code></pre>
  111
+
  112
+<p>Specify CSS class for this particular link:</p>
  113
+
  114
+<pre><code>&lt;%= sortable_column "Name", :class =&gt; "SortableLink" %&gt;
  115
+</code></pre>
  116
+
  117
+<p>Specify sort direction on first click:</p>
  118
+
  119
+<pre><code>&lt;%= sortable_column "Created At", :direction =&gt; :asc %&gt;
  120
+</code></pre>
  121
+
  122
+<h3 id="fetching-sort-order">Fetching Sort Order</h3>
  123
+
  124
+<p>To fetch sort order <strong>securely</strong>, with <strong>column name validation</strong>, <strong>default values</strong> and <strong>multiple sort criteria</strong>, use the block form of <code>sortable_column_order</code>:</p>
  125
+
  126
+<pre><code>order = sortable_column_order do |column, direction|
  127
+  case column
  128
+  when "name"
  129
+    "#{column} #{direction}"
  130
+  when "created_at", "updated_at"
  131
+    "#{column} #{direction}, name ASC"
  132
+  else
  133
+    "name ASC"
  134
+  end
  135
+end
  136
+</code></pre>
  137
+
  138
+<h2 id="feedback">Feedback</h2>
  139
+
  140
+<p>Send bug reports, suggestions and criticisms through <a href="http://github.com/dadooda/handles_sortable_columns">project’s page on GitHub</a>.</p>
33  README.md
Source Rendered
@@ -9,16 +9,34 @@ Introduction
9 9
 A simple yet flexible Rails gem/plugin to quickly add sortable table columns to your controller and views.
10 10
 
11 11
 
12  
-Setup
13  
------
  12
+Setup (Rails 3)
  13
+---------------
  14
+
  15
+In your app's `Gemfile`, add:
  16
+
  17
+    gem "handles_sortable_columns"
  18
+
  19
+To install the gem with RDoc/ri documentation, do a:
14 20
 
15  
-    $ gem sources --add http://rubygems.org
16 21
     $ gem install handles_sortable_columns
17 22
 
18  
-In your app's `config/environment.rb` do a:
  23
+Otherwise, do a `bundle install`.
  24
+
  25
+
  26
+Setup (Rails 2)
  27
+---------------
  28
+
  29
+In your app's `config/environment.rb` add:
19 30
 
20 31
     config.gem "handles_sortable_columns"
21 32
 
  33
+To install the gem, do a:
  34
+
  35
+    $ gem sources --add http://rubygems.org
  36
+    $ gem install handles_sortable_columns
  37
+
  38
+, or use `rake gems:install`.
  39
+
22 40
 
23 41
 Basic Usage
24 42
 -----------
@@ -27,7 +45,7 @@ Activate the feature in your controller class:
27 45
 
28 46
     class MyController < ApplicationController
29 47
       handles_sortable_columns
30  
-    ...
  48
+      ...
31 49
 
32 50
 In a view, mark up sortable columns by using the <tt>sortable_column</tt> helper:
33 51
 
@@ -38,7 +56,8 @@ In controller action, fetch and use the order clause according to current state
38 56
 
39 57
     def index
40 58
       order = sortable_column_order
41  
-      @records = Article.all(:order => order)
  59
+      @records = Article.order(order)           # Rails 3.
  60
+      @records = Article.all(:order => order)   # Rails 2.
42 61
     end
43 62
 
44 63
 That's it for basic usage. Production usage may require passing additional parameters to listed methods.
@@ -59,7 +78,7 @@ Change names of GET parameters used for sorting and pagination:
59 78
         conf.sort_param = "s"
60 79
         conf.page_param = "p"
61 80
       end
62  
-    ...
  81
+      ...
63 82
 
64 83
 Change CSS class of all sortable column `<a>` tags:
65 84
 
25  Rakefile
... ...
@@ -1,4 +1,5 @@
1 1
 require "rake/rdoctask"
  2
+require "yaml"
2 3
 
3 4
 GEM_NAME = "handles_sortable_columns"
4 5
 
@@ -14,10 +15,9 @@ begin
14 15
     gem.files = FileList[
15 16
       "[A-Z]*",
16 17
       "*.gemspec",
17  
-      "lib/**/*.rb",
18 18
       "init.rb",
19  
-    ] - ["README.html"]
20  
-    gem.extra_rdoc_files = ["README.md"]
  19
+      "lib/**/*.rb",
  20
+    ]
21 21
   end
22 22
 rescue LoadError
23 23
   STDERR.puts "This gem requires Jeweler to be built"
@@ -28,11 +28,20 @@ task :rebuild => [:gemspec, :build]
28 28
 
29 29
 desc "Push (publish) gem to RubyGems (aka Gemcutter)"
30 30
 task :push => :rebuild do
31  
-  # Yet found no way to ask Jeweler forge a complete version string for us.
32  
-  vh = YAML.load(File.read("VERSION.yml"))
33  
-  version = [vh[:major], vh[:minor], vh[:patch]].join(".")
34  
-  pkgfile = File.join("pkg", [GEM_NAME, "-", version, ".gem"].to_s)
35  
-  system("gem", "push", pkgfile)
  31
+  # NOTE: Yet found no way to ask Jeweler forge a complete version string for us.
  32
+  h = YAML.load_file("VERSION.yml")
  33
+  version = [h[:major], h[:minor], h[:patch], h[:build]].compact.join(".")
  34
+  pkgfile = File.join("pkg", "#{GEM_NAME}-#{version}.gem")
  35
+  Kernel.system("gem", "push", pkgfile)
  36
+end
  37
+
  38
+desc "Generate RDoc documentation"
  39
+Rake::RDocTask.new(:rdoc) do |rdoc|
  40
+  rdoc.rdoc_dir = "doc"
  41
+  rdoc.title    = "Handles::SortableColumns"
  42
+  #rdoc.options << "--line-numbers"
  43
+  #rdoc.options << "--inline-source"
  44
+  rdoc.rdoc_files.include("lib/**/*.rb")
36 45
 end
37 46
 
38 47
 desc "Compile README preview"
3  VERSION.yml
... ...
@@ -1,4 +1,5 @@
1 1
 --- 
2 2
 :major: 0
3 3
 :minor: 1
4  
-:patch: 2
  4
+:patch: 3
  5
+#:build: pre1
41  dev/github.css
... ...
@@ -0,0 +1,41 @@
  1
+
  2
+body{font:13.34px helvetica,arial,freesans,clean,sans-serif;}
  3
+body{width:920px;margin:0 auto;padding:0 15px;text-align:left;}
  4
+
  5
+/* Это неважно работает, с виду погано. */
  6
+xbody{background-color:#f8f8f8;padding:.7em;}
  7
+
  8
+/*
  9
+#readme div.plain,#readme div.wikistyle{background-color:#f8f8f8;padding:.7em;}
  10
+.site{width:920px;margin:0 auto;padding:0 15px;text-align:left;}
  11
+#readme{font:13.34px helvetica,arial,freesans,clean,sans-serif;}
  12
+#readme.announce{margin:1em 0;}
  13
+#readme span.name{font-size:140%;padding:.8em 0;}
  14
+#readme div.plain,#readme div.wikistyle{background-color:#f8f8f8;padding:.7em;}
  15
+#readme.announce div.plain,#readme.announce div.wikistyle{border:1px solid #e9e9e9;}
  16
+#readme.blob div.plain,#readme.blob div.wikistyle{border-top:none;}
  17
+#readme div.plain pre{font-family:'Bitstream Vera Sans Mono','Courier',monospace;font-size:85%;color:#444;}
  18
+*/
  19
+
  20
+h1,h2,h3,h4,h5,h6{border:0!important;}
  21
+h1{font-size:170%!important;border-top:4px solid #aaa!important;padding-top:.5em!important;margin-top:1.5em!important;}
  22
+h1:first-child{margin-top:0!important;padding-top:.25em!important;border-top:none!important;}
  23
+h2{font-size:150%!important;margin-top:1.5em!important;border-top:4px solid #e0e0e0!important;padding-top:.5em!important;}
  24
+h3{margin-top:1em!important;}
  25
+p{margin:1em 0!important;line-height:1.5em!important;}
  26
+ul{margin:1em 0 1em 2em!important;}
  27
+ol{margin:1em 0 1em 2em!important;}
  28
+ul ul,ul ol,ol ol,ol ul{margin-top:0!important;margin-bottom:0!important;}
  29
+blockquote{margin:1em 0!important;border-left:5px solid #ddd!important;padding-left:.6em!important;color:#555!important;}
  30
+dt{font-weight:bold!important;margin-left:1em!important;}
  31
+dd{margin-left:2em!important;margin-bottom:1em!important;}
  32
+table{margin:1em 0!important;}
  33
+table th{border-bottom:1px solid #bbb!important;padding:.2em 1em!important;}
  34
+table td{border-bottom:1px solid #ddd!important;padding:.2em 1em!important;}
  35
+pre{margin:1em 0!important;font-size:90%!important;background-color:#f8f8ff!important;border:1px solid #dedede!important;padding:.5em!important;line-height:1.5em!important;color:#444!important;overflow:auto!important;}
  36
+pre code{padding:0!important;font-size:100%!important;background-color:#f8f8ff!important;border:none!important;}
  37
+code{font-size:90%!important;background-color:#f8f8ff!important;color:#444!important;padding:0 .2em!important;border:1px solid #dedede!important;}
  38
+pre.console{margin:1em 0!important;font-size:90%!important;background-color:black!important;padding:.5em!important;line-height:1.5em!important;color:white!important;}
  39
+pre.console code{padding:0!important;font-size:100%!important;background-color:black!important;border:none!important;color:white!important;}
  40
+pre.console span{color:#888!important;}
  41
+pre.console span.command{color:yellow!important;}
4  dev/head.html
... ...
@@ -0,0 +1,4 @@
  1
+<head>
  2
+  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
  3
+  <link href="dev/github.css" rel="stylesheet" type="text/css" />
  4
+</head>
34  handles_sortable_columns.gemspec
... ...
@@ -1,42 +1,39 @@
1 1
 # Generated by jeweler
2  
-# DO NOT EDIT THIS FILE
3  
-# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
  2
+# DO NOT EDIT THIS FILE DIRECTLY
  3
+# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4 4
 # -*- encoding: utf-8 -*-
5 5
 
6 6
 Gem::Specification.new do |s|
7 7
   s.name = %q{handles_sortable_columns}
8  
-  s.version = "0.1.2"
  8
+  s.version = "0.1.3"
9 9
 
10 10
   s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 11
   s.authors = ["Alex Fortuna"]
12  
-  s.date = %q{2010-09-08}
  12
+  s.date = %q{2011-07-29}
13 13
   s.description = %q{Sortable Table Columns}
14 14
   s.email = %q{alex.r@askit.org}
15 15
   s.extra_rdoc_files = [
  16
+    "README.html",
16 17
     "README.md"
17 18
   ]
18 19
   s.files = [
19 20
     "MIT-LICENSE",
20  
-     "README.md",
21  
-     "Rakefile",
22  
-     "VERSION.yml",
23  
-     "handles_sortable_columns.gemspec",
24  
-     "init.rb",
25  
-     "lib/action_controller/base/handles_sortable_columns.rb",
26  
-     "lib/handles/sortable_columns.rb",
27  
-     "lib/handles_sortable_columns.rb"
  21
+    "README.html",
  22
+    "README.md",
  23
+    "Rakefile",
  24
+    "VERSION.yml",
  25
+    "handles_sortable_columns.gemspec",
  26
+    "init.rb",
  27
+    "lib/action_controller/base/handles_sortable_columns.rb",
  28
+    "lib/handles/sortable_columns.rb",
  29
+    "lib/handles_sortable_columns.rb"
28 30
   ]
29 31
   s.homepage = %q{http://github.com/dadooda/handles_sortable_columns}
30  
-  s.rdoc_options = ["--charset=UTF-8"]
31 32
   s.require_paths = ["lib"]
32  
-  s.rubygems_version = %q{1.3.7}
  33
+  s.rubygems_version = %q{1.6.2}
33 34
   s.summary = %q{Sortable Table Columns}
34  
-  s.test_files = [
35  
-    "spec/handles_sortable_columns_spec.rb"
36  
-  ]
37 35
 
38 36
   if s.respond_to? :specification_version then
39  
-    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40 37
     s.specification_version = 3
41 38
 
42 39
     if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -45,3 +42,4 @@ Gem::Specification.new do |s|
45 42
   else
46 43
   end
47 44
 end
  45
+
6  lib/action_controller/base/handles_sortable_columns.rb
... ...
@@ -1,3 +1,5 @@
1  
-class ActionController::Base    #:nodoc:
2  
-  include ::Handles::SortableColumns
  1
+module ActionController   #:nodoc:
  2
+  class Base    #:nodoc:
  3
+    include ::Handles::SortableColumns
  4
+  end
3 5
 end
231  lib/handles/sortable_columns.rb
@@ -9,7 +9,7 @@ module Handles  #:nodoc:
9 9
   #
10 10
   #   class MyController < ApplicationController
11 11
   #     handles_sortable_columns
12  
-  #   ...
  12
+  #     ...
13 13
   # 
14 14
   # In a view, mark up sortable columns by using the <tt>sortable_column</tt> helper:
15 15
   #
@@ -20,30 +20,30 @@ module Handles  #:nodoc:
20 20
   #
21 21
   #   def index
22 22
   #     order = sortable_column_order
23  
-  #     @records = Article.all(:order => order)
  23
+  #     @records = Article.order(order)           # Rails 3.
  24
+  #     @records = Article.all(:order => order)   # Rails 2.
24 25
   #   end
25 26
   #
26 27
   # That's it for basic usage. Production usage may require passing additional parameters to listed methods.
27 28
   #
28 29
   # See also:
29  
-  # * <tt>MetaClassMethods#handles_sortable_columns</tt>
30  
-  # * <tt>HelperMethods#sortable_column</tt>
31  
-  # * <tt>InstanceMethods#sortable_column_order</tt>
  30
+  # * MetaClassMethods#handles_sortable_columns
  31
+  # * InstanceMethods#sortable_column_order
32 32
   module SortableColumns
33  
-    def self.included(owner)
  33
+    def self.included(owner)    #:nodoc:
34 34
       owner.extend MetaClassMethods
35 35
     end
36 36
 
37  
-    # Sortable columns configuration object. Passed to block when you do a:
  37
+    # Sortable columns configuration object. Passed to the block when you do a:
38 38
     #
39  
-    #   handles_sortable_column do |conf|
  39
+    #   handles_sortable_columns do |conf|
40 40
     #     ...
41 41
     #   end
42 42
     class Config
43 43
       # CSS class for link (regardless of sorted state). Default:
44 44
       #
45  
-      #   nil
46  
-      attr_accessor :class
  45
+      #   SortableColumnLink
  46
+      attr_accessor :link_class
47 47
 
48 48
       # GET parameter name for page number. Default:
49 49
       #
@@ -67,30 +67,33 @@ class Config
67 67
 
68 68
       def initialize(attrs = {})
69 69
         defaults = {
70  
-          :page_param         => "page",
71  
-          :sort_param         => "sort",
72  
-          :indicator_text     => {:asc => "&nbsp;&darr;&nbsp;", :desc => "&nbsp;&uarr;&nbsp;"},
73  
-          :indicator_class    => {:asc => "SortedAsc", :desc => "SortedDesc"},
  70
+          :link_class => "SortableColumnLink",
  71
+          :indicator_class => {:asc => "SortedAsc", :desc => "SortedDesc"},
  72
+          :indicator_text => {:asc => "&nbsp;&darr;&nbsp;", :desc => "&nbsp;&uarr;&nbsp;"},
  73
+          :page_param => "page",
  74
+          :sort_param => "sort",
74 75
         }
75 76
 
76 77
         defaults.merge(attrs).each {|k, v| send("#{k}=", v)}
77 78
       end
78 79
 
  80
+      # Bracket access for convenience.
79 81
       def [](key)
80 82
         send(key)
81 83
       end
82 84
 
  85
+      # Bracket access for convenience.
83 86
       def []=(key, value)
84 87
         send("#{key}=", value)
85 88
       end
86 89
     end # Config
87 90
 
88 91
     module MetaClassMethods
89  
-      # Activate and optionally configure the sortable columns.
  92
+      # Activate and optionally configure the sortable columns feature in your controller.
90 93
       #
91 94
       #   class MyController < ApplicationController
92 95
       #     handles_sortable_columns
93  
-      #   end
  96
+      #     ...
94 97
       #
95 98
       # With configuration:
96 99
       #
@@ -101,42 +104,43 @@ module MetaClassMethods
101 104
       #       conf.indicator_text = {}
102 105
       #       ...
103 106
       #     end
104  
-      #   end
  107
+      #     ...
  108
+      #
  109
+      # With filter options:
  110
+      #
  111
+      #   class MyController < ApplicationController
  112
+      #     handles_sortable_columns(:only => [:index]) do |conf|
  113
+      #       ...
  114
+      #     end
  115
+      #     ...
105 116
       #
106  
-      # <tt>conf</tt> is a <tt>Config</tt> object.
107  
-      def handles_sortable_columns(&block)
  117
+      # NOTE: <tt>conf</tt> is a Config object.
  118
+      def handles_sortable_columns(fopts = {}, &block)
108 119
         # Multiple activation protection.
109 120
         if not self < InstanceMethods
110  
-          extend ClassMethods
111 121
           include InstanceMethods
112  
-          helper HelperMethods
  122
+          helper_method :sortable_column
113 123
         end
114 124
 
115  
-        # Configuration is processed at every activation.
116  
-        yield(sortable_columns_config) if block
  125
+        # Process configuration at every activation.
  126
+        before_filter(fopts) do |ac|
  127
+          ac.instance_eval do
  128
+            # NOTE: Can't `yield`, we're in a block already.
  129
+            block.call(sortable_columns_config) if block
  130
+          end
  131
+        end
117 132
       end
118 133
     end # MetaClassMethods
119 134
 
120  
-    module ClassMethods
121  
-      # Internal/advanced use only. Access/initialize the sortable columns config.
122  
-      def sortable_columns_config
123  
-        # NOTE: This is controller's class instance variable.
124  
-        @sortable_columns_config ||= ::Handles::SortableColumns::Config.new
125  
-      end
126  
-
127  
-      # Internal/advanced use only. Convert title to sortable column name.
128  
-      #
129  
-      #   sortable_column_name_from_title("ProductName")  # => "product_name"
130  
-      def sortable_column_name_from_title(title)
131  
-        title.gsub(/(\s)(\S)/) {$2.upcase}.underscore
132  
-      end
  135
+    module InstanceMethods
  136
+      private
133 137
 
134 138
       # Internal/advanced use only. Parse sortable column sort param into a Hash with predefined keys.
135 139
       #
136 140
       #   parse_sortable_column_sort_param("name")    # => {:column => "name", :direction => :asc}
137 141
       #   parse_sortable_column_sort_param("-name")   # => {:column => "name", :direction => :desc}
138 142
       #   parse_sortable_column_sort_param("")        # => {:column => nil, :direction => nil}
139  
-      def parse_sortable_column_sort_param(sort)
  143
+      def parse_sortable_column_sort_param(sort)    #:nodoc:
140 144
         out = {:column => nil, :direction => nil}
141 145
         if sort.to_s.strip.match /\A((?:-|))([^-]+)\z/
142 146
           out[:direction] = $1.empty?? :asc : :desc
@@ -144,96 +148,45 @@ def parse_sortable_column_sort_param(sort)
144 148
         end
145 149
         out
146 150
       end
147  
-    end # ClassMethods
148  
-
149  
-    module InstanceMethods
150  
-      protected
151  
-
152  
-      # Compile SQL order clause according to current state of sortable columns.
153  
-      #
154  
-      # Basic (kickstart) usage:
155  
-      #
156  
-      #   order = sortable_column_order
157  
-      #
158  
-      # <b>WARNING!</b> Basic usage is <b>not recommended</b> for production since it is potentially
159  
-      # vulnerable to SQL injection!
160  
-      #
161  
-      # Production usage with multiple sort criteria, column name validation and defaults:
162  
-      #
163  
-      #   order = sortable_column_order do |column, direction|
164  
-      #     case column
165  
-      #     when "name"
166  
-      #       "#{column} #{direction}"
167  
-      #     when "created_at", "updated_at"
168  
-      #       "#{column} #{direction}, name ASC"
169  
-      #     else
170  
-      #       "name ASC"
171  
-      #     end
172  
-      #   end
173  
-      #
174  
-      # Apply order:
175  
-      #
176  
-      #   @records = Article.all(:order => order)   # Rails 2.x.
177  
-      #   @records = Article.order(order)           # Rails 3.
178  
-      def sortable_column_order(&block)
179  
-        conf = {}
180  
-        conf[k = :sort_param] = self.class.sortable_columns_config[k]
181  
-
182  
-        # Parse sort param.
183  
-        pp = self.class.parse_sortable_column_sort_param(params[conf[:sort_param]])
184  
-
185  
-        order = if block
186  
-          yield(pp[:column], pp[:direction])
187  
-        else
188  
-          # No block -- do a straight mapping.
189  
-          if pp[:column]
190  
-            [pp[:column], pp[:direction]].join(" ")
191  
-          end
192  
-        end
193 151
 
194  
-        # Can be nil.
195  
-        order
196  
-      end
197  
-    end # InstanceMethods
198  
-
199  
-    module HelperMethods
200 152
       # Render a sortable column link.
201 153
       #
202 154
       # Options:
203  
-      # * <tt>:column</tt> -- Column name. E.g. <tt>"created_at"</tt>.
  155
+      #
  156
+      # * <tt>:column</tt> -- Column name. E.g. <tt>created_at</tt>.
204 157
       # * <tt>:direction</tt> -- Sort direction on first click. <tt>:asc</tt> or <tt>:desc</tt>. Default is <tt>:asc</tt>.
205  
-      # * <tt>:class</tt> -- CSS class for link (regardless of sorted state).
206  
-      # * <tt>:style</tt> -- CSS style for link (regardless of sorted state).
  158
+      # * <tt>:link_class</tt> -- CSS class for link, regardless of sorted state.
  159
+      # * <tt>:link_style</tt> -- CSS style for link, regardless of sorted state.
207 160
       #
208 161
       # Examples:
209 162
       #
210 163
       #   <%= sortable_column "Product" %>
211  
-      #   <%= sortable_column "Highest Price", :column_name => "max_price" %>
212  
-      #   <%= sortable_column "Name", :class => "SortableLink" %>
  164
+      #   <%= sortable_column "Highest Price", :column => "max_price" %>
  165
+      #   <%= sortable_column "Name", :link_class => "SortableLink" %>
213 166
       #   <%= sortable_column "Created At", :direction => :asc %>
214  
-      def sortable_column(title, options = {})
  167
+      def sortable_column(title, options = {})    #:doc:
215 168
         options = options.dup
216 169
         o = {}
217 170
         conf = {}
218  
-        conf[k = :sort_param] = controller.class.sortable_columns_config[k]
219  
-        conf[k = :page_param] = controller.class.sortable_columns_config[k]
220  
-        conf[k = :indicator_text] = controller.class.sortable_columns_config[k]
221  
-        conf[k = :indicator_class] = controller.class.sortable_columns_config[k]
  171
+        conf[k = :sort_param] = sortable_columns_config[k]
  172
+        conf[k = :page_param] = sortable_columns_config[k]
  173
+        conf[k = :indicator_text] = sortable_columns_config[k]
  174
+        conf[k = :indicator_class] = sortable_columns_config[k]
222 175
 
223 176
         #HELP sortable_column
224  
-        o[k = :column] = options.delete(k) || controller.class.sortable_column_name_from_title(title)
  177
+        o[k = :column] = options.delete(k) || sortable_column_title_to_name(title)
225 178
         o[k = :direction] = options.delete(k).to_s.downcase =~ /\Adesc\z/ ? :desc : :asc
226  
-        o[k = :class] = options.delete(k) || controller.class.sortable_columns_config[k]
227  
-        o[k = :style] = options.delete(k)
  179
+        o[k = :link_class] = options.delete(k) || sortable_columns_config[k]
  180
+        o[k = :link_style] = options.delete(k)
228 181
         #HELP /sortable_column
229 182
 
230 183
         raise "Unknown option(s): #{options.inspect}" if not options.empty?
231 184
 
232 185
         # Parse sort param.
233  
-        pp = controller.class.parse_sortable_column_sort_param(params[conf[:sort_param]])
  186
+        pp = parse_sortable_column_sort_param(params[conf[:sort_param]])
234 187
 
235 188
         css_class = []
236  
-        if (s = o[:class]).present?
  189
+        if (s = o[:link_class]).present?
237 190
           css_class << s
238 191
         end
239 192
 
@@ -247,11 +200,14 @@ def sortable_column(title, options = {})
247 200
 
248 201
         html_options = {}
249 202
         html_options[:class] = css_class.join(" ") if css_class.present?
250  
-        html_options[:style] = o[:style] if o[:style].present?
  203
+        html_options[:style] = o[:link_style] if o[:link_style].present?
  204
+
  205
+        # Rails 3 / Rails 2 fork.
  206
+        tpl = respond_to?(:view_context) ? view_context : @template
251 207
 
252 208
         # Already sorted?
253 209
         if pp[:column] == o[:column].to_s
254  
-          pcs << link_to(title, params.merge({conf[:sort_param] => [("-" if pp[:direction] == :asc), o[:column]].join, conf[:page_param] => 1}), html_options)       # Opposite sort order when clicked.
  210
+          pcs << tpl.link_to(title, params.merge({conf[:sort_param] => [("-" if pp[:direction] == :asc), o[:column]].join, conf[:page_param] => 1}), html_options)       # Opposite sort order when clicked.
255 211
 
256 212
           # Append indicator, if configured.
257 213
           if (s = conf[:indicator_text][pp[:direction]]).present?
@@ -259,12 +215,71 @@ def sortable_column(title, options = {})
259 215
           end
260 216
         else
261 217
           # Not sorted.
262  
-          pcs << link_to(title, params.merge({conf[:sort_param] => [("-" if o[:direction] != :asc), o[:column]].join, conf[:page_param] => 1}), html_options)
  218
+          pcs << tpl.link_to(title, params.merge({conf[:sort_param] => [("-" if o[:direction] != :asc), o[:column]].join, conf[:page_param] => 1}), html_options)
263 219
         end
264 220
 
265 221
         # For Rails 3 provide #html_safe.
266 222
         (v = pcs.join).respond_to?(:html_safe) ? v.html_safe : v
267 223
       end
268  
-    end # HelperMethods
  224
+
  225
+      # Compile SQL order clause according to current state of sortable columns.
  226
+      #
  227
+      # Basic (kickstart) usage:
  228
+      #
  229
+      #   order = sortable_column_order
  230
+      #
  231
+      # <b>WARNING:</b> Basic usage is <b>not recommended</b> for production since it is potentially
  232
+      # vulnerable to SQL injection!
  233
+      #
  234
+      # Production usage with multiple sort criteria, column name validation and defaults:
  235
+      #
  236
+      #   order = sortable_column_order do |column, direction|
  237
+      #     case column
  238
+      #     when "name"
  239
+      #       "#{column} #{direction}"
  240
+      #     when "created_at", "updated_at"
  241
+      #       "#{column} #{direction}, name ASC"
  242
+      #     else
  243
+      #       "name ASC"
  244
+      #     end
  245
+      #   end
  246
+      #
  247
+      # Apply order:
  248
+      #
  249
+      #   @records = Article.order(order)           # Rails 3.
  250
+      #   @records = Article.all(:order => order)   # Rails 2.
  251
+      def sortable_column_order(&block)
  252
+        conf = {}
  253
+        conf[k = :sort_param] = sortable_columns_config[k]
  254
+
  255
+        # Parse sort param.
  256
+        pp = parse_sortable_column_sort_param(params[conf[:sort_param]])
  257
+
  258
+        order = if block
  259
+          column, direction = pp[:column], pp[:direction]
  260
+          yield(column, direction)    # NOTE: Makes RDoc/ri look a little smarter.
  261
+        else
  262
+          # No block -- do a straight mapping.
  263
+          if pp[:column]
  264
+            [pp[:column], pp[:direction]].join(" ")
  265
+          end
  266
+        end
  267
+
  268
+        # Can be nil.
  269
+        order
  270
+      end
  271
+
  272
+      # Internal use only. Convert title to sortable column name.
  273
+      #
  274
+      #   sortable_column_title_to_name("ProductName")  # => "product_name"
  275
+      def sortable_column_title_to_name(title)    #:nodoc:
  276
+        title.gsub(/(\s)(\S)/) {$2.upcase}.underscore
  277
+      end
  278
+
  279
+      # Internal use only. Access/initialize feature's config.
  280
+      def sortable_columns_config   #:nodoc:
  281
+        @sortable_columns_config ||= ::Handles::SortableColumns::Config.new
  282
+      end
  283
+    end # InstanceMethods
269 284
   end # SortableColumns
270 285
 end # Handles
42  spec/handles_sortable_columns_spec.rb
... ...
@@ -1,42 +0,0 @@
1  
-describe "ClassMethods" do
2  
-  module WrapSortableColumnsClassMethods
3  
-    class MyController < ActionController::Base
4  
-      handles_sortable_columns
5  
-    end
6  
-  end
7  
-
8  
-  describe "#sortable_column_name_from_title" do
9  
-    it "generally works" do
10  
-      tests = [
11  
-        ["Product", "product"],
12  
-        ["product", "product"],
13  
-        ["created_at", "created_at"],
14  
-        ["created at", "created_at"],
15  
-        ["CreatedAt", "created_at"],
16  
-        ["Created At", "created_at"],
17  
-      ]
18  
-
19  
-      tests.each do |input, expected|
20  
-        WrapSortableColumnsClassMethods::MyController.sortable_column_name_from_title(input).should == expected
21  
-      end
22  
-    end
23  
-  end # #sortable_column_name_from_title
24  
-
25  
-  describe "#parse_sortable_column_sort_param" do
26  
-    it "generally works" do
27  
-      tests = [
28  
-        ["name", {:column => "name", :direction => :asc}],
29  
-        ["-name", {:column => "name", :direction => :desc}],
30  
-        [" -name ", {:column => "name", :direction => :desc}],
31  
-        ["", {:column => nil, :direction => nil}],
32  
-        ["-", {:column => nil, :direction => nil}],
33  
-        ["- name", {:column => "name", :direction => :desc}],
34  
-        ["--kaka", {:column => nil, :direction => nil}],
35  
-      ]
36  
-
37  
-      tests.each do |input, expected|
38  
-        WrapSortableColumnsClassMethods::MyController.parse_sortable_column_sort_param(input).should == expected
39  
-      end
40  
-    end
41  
-  end # #parse_sortable_column_sort_param
42  
-end # ClassMethods

0 notes on commit 86eb606

Please sign in to comment.
Something went wrong with that request. Please try again.