sorting_table_for 0.2.0 → 0.2.1

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/CHANGELOG.mdown CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### Version 0.2.1 _(July 20, 2011)_
4
+
5
+ * Fix bug with sorting_table was not loading in ActiveRecord
6
+
3
7
  ### Version 0.2.0 _(November 14, 2010)_
4
8
 
5
9
  * Add footers and footer
data/init.rb CHANGED
@@ -1,7 +1,3 @@
1
1
  # encoding: utf-8
2
2
 
3
- # Require Helper
4
3
  require 'sorting_table_for'
5
-
6
- # Require Scoping helper
7
- require 'model/sorting_table_model_scope'
@@ -1,6 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'sorting_table_for/model_scope'
3
4
  require 'sorting_table_for/table_builder'
5
+ require 'sorting_table_for/format_line'
6
+ require 'sorting_table_for/format_cell'
4
7
  require 'sorting_table_for/i18n'
5
8
  require 'sorting_table_for/tools'
6
9
 
@@ -0,0 +1,189 @@
1
+ module SortingTableFor
2
+ class FormatCell < FormatLine
3
+
4
+ def initialize(object, args, type = nil, block = nil)
5
+ @object, @type, @block = object, type, block
6
+ if args.is_a? Array
7
+ @options, @html_options = get_cell_and_html_options( args.extract_options! )
8
+ @ask = args.first
9
+ if @ask.nil? and @options.has_key?(:action)
10
+ @type = :action
11
+ @ask = @options[:action]
12
+ end
13
+ else
14
+ @ask = args
15
+ end
16
+ set_default_options
17
+ can_sort_column?
18
+ end
19
+
20
+ # Return a td with the formated value or action for columns
21
+ def render_cell_tbody
22
+ if @type == :action
23
+ cell_value = action_link_to(@ask)
24
+ elsif @ask
25
+ cell_value = (@ask.is_a?(Symbol)) ? format_cell_value(@object[@ask], @ask) : format_cell_value(@ask)
26
+ else
27
+ cell_value = @block
28
+ end
29
+ cell_value = action_link_to(@options[:action], cell_value) if @type != :action and @options.has_key?(:action)
30
+ content_tag(:td, cell_value, @html_options)
31
+ end
32
+
33
+ # Return a td with the formated value or action for headers
34
+ def render_cell_thead
35
+ if @ask
36
+ cell_value = (@ask.is_a?(Symbol)) ? I18n.t(@ask, {}, :header) : @ask
37
+ else
38
+ cell_value = @block
39
+ end
40
+ if @can_sort
41
+ sort_on = @options[:sort_as] || @ask
42
+ @html_options.merge!(:class => "#{@html_options[:class]} #{sorting_html_class(sort_on)}".strip)
43
+ content_tag(:th, sort_link_to(cell_value, sort_on), @html_options)
44
+ else
45
+ content_tag(:th, cell_value, @html_options)
46
+ end
47
+ end
48
+
49
+ def render_cell_tfoot
50
+ if @ask
51
+ cell_value = (@ask.is_a?(Symbol)) ? I18n.t(@ask, {}, :footer) : @ask
52
+ else
53
+ cell_value = @block
54
+ end
55
+ cell_value = action_link_to(@options[:action], cell_value) if @type != :action and @options.has_key?(:action)
56
+ content_tag(:td, cell_value, @html_options)
57
+ end
58
+
59
+ private
60
+
61
+ # Return options and html options for a cell
62
+ def get_cell_and_html_options(options)
63
+ return options, options.delete(:html) || {}
64
+ end
65
+
66
+ # set to true if column is sortable
67
+ def can_sort_column?
68
+ @can_sort = true if @options[:sort] and model_have_column?(@options[:sort_as] || @ask)
69
+ end
70
+
71
+ # Set default options for cell
72
+ # Set an empty hash if no html options
73
+ # Set an empty hash if no options
74
+ # Set sort to true if no options sort
75
+ def set_default_options
76
+ @html_options = {} unless defined? @html_options
77
+ @options = {} unless defined? @options
78
+ @html_options = format_options_to_cell(@html_options, @options)
79
+ @options[:sort] = @@options[:sort] if !@options.has_key? :sort
80
+ end
81
+
82
+ # Create the link for actions
83
+ # Set the I18n translation or the given block for the link's name
84
+ def action_link_to(action, block = nil)
85
+ object_or_array = @@object_or_array.clone
86
+ object_or_array.push @object
87
+ return case action.to_sym
88
+ when :delete
89
+ create_link_to(block || I18n.t(:delete), object_or_array, @@options[:link_remote], :delete, I18n.t(:confirm_delete))
90
+ when :show
91
+ create_link_to(block || I18n.t(:show), object_or_array, @@options[:link_remote])
92
+ else
93
+ object_or_array.insert(0, action)
94
+ create_link_to(block || I18n.t(@ask), object_or_array, @@options[:link_remote])
95
+ end
96
+ end
97
+
98
+ # Create sorting link
99
+ def sort_link_to(name, sort_on)
100
+ create_link_to(name, sort_url(sort_on), @@options[:sort_remote])
101
+ end
102
+
103
+ # Create the link based on object
104
+ # Set an ajax link if option link_remote is set to true
105
+ # Compatible with rails 2 and 3.
106
+ def create_link_to(block, url, remote, method = nil, confirm = nil)
107
+ if remote and Tools::rails3?
108
+ return link_to(block, url, :method => method, :confirm => confirm, :remote => true)
109
+ elsif remote
110
+ method = :get if method.nil?
111
+ return link_to_remote(block, { :url => url, :method => method, :confirm => confirm })
112
+ end
113
+ link_to(block, url, :method => method, :confirm => confirm)
114
+ end
115
+
116
+ # Return a string with html class of sorting for headers
117
+ # The html class is based on option: SortingTableFor::TableBuilder.html_sorting_class
118
+ def sorting_html_class(sort_on)
119
+ return TableBuilder.html_sorting_class.first if current_sorting(sort_on).nil?
120
+ return TableBuilder.html_sorting_class.second if current_sorting(sort_on) == :asc
121
+ TableBuilder.html_sorting_class.third
122
+ end
123
+
124
+ # Return an url for sorting
125
+ # Add the param sorting_table[name]=direction to the url
126
+ # Add the default direction: :asc
127
+ def sort_url(sort_on)
128
+ url_params = @@params.clone
129
+ if url_params.has_key? TableBuilder.params_sort_table
130
+ if url_params[TableBuilder.params_sort_table].has_key? sort_on
131
+ url_params[TableBuilder.params_sort_table][sort_on] = inverse_sorting(sort_on)
132
+ return url_for(url_params)
133
+ end
134
+ url_params[TableBuilder.params_sort_table].delete sort_on
135
+ end
136
+ url_params[TableBuilder.params_sort_table] = { sort_on => :asc }
137
+ url_for(url_params)
138
+ end
139
+
140
+ # Return a symbol of the current sorting (:asc, :desc, nil)
141
+ def current_sorting(sort_on)
142
+ if @@params.has_key? TableBuilder.params_sort_table and @@params[TableBuilder.params_sort_table].has_key? sort_on
143
+ return @@params[TableBuilder.params_sort_table][sort_on].to_sym
144
+ end
145
+ nil
146
+ end
147
+
148
+ # Return a symbol, the inverse of the current sorting
149
+ def inverse_sorting(sort_on)
150
+ return :asc if current_sorting(sort_on).nil?
151
+ return :desc if current_sorting(sort_on) == :asc
152
+ :asc
153
+ end
154
+
155
+ # Return the formated cell's value
156
+ def format_cell_value(value, attribute = nil)
157
+ unless (ret_value = format_cell_value_as_ask(value)).nil?
158
+ return ret_value
159
+ end
160
+ format_cell_value_as_type(value, attribute)
161
+ end
162
+
163
+ # Format the value if option :as is set
164
+ def format_cell_value_as_ask(value)
165
+ return nil if !@options or @options.empty? or !@options.has_key?(:as)
166
+ return case @options[:as]
167
+ when :date then ::I18n.l(value.to_date, :format => @options[:format] || TableBuilder.i18n_default_format_date)
168
+ when :time then ::I18n.l(value.to_datetime, :format => @options[:format] || TableBuilder.i18n_default_format_date)
169
+ when :currency then number_to_currency(value)
170
+ else nil
171
+ end
172
+ end
173
+
174
+ # Format the value based on value's type
175
+ def format_cell_value_as_type(value, attribute)
176
+ if value.is_a?(Time) or value.is_a?(Date)
177
+ return ::I18n.l(value, :format => @options[:format] || TableBuilder.i18n_default_format_date)
178
+ elsif TableBuilder.currency_columns.include?(attribute)
179
+ return number_to_currency(value)
180
+ elsif value.is_a?(TrueClass)
181
+ return TableBuilder.default_boolean.first
182
+ elsif value.is_a?(FalseClass)
183
+ return TableBuilder.default_boolean.second
184
+ end
185
+ value
186
+ end
187
+
188
+ end
189
+ end
@@ -0,0 +1,110 @@
1
+ module SortingTableFor
2
+ class FormatLine < TableBuilder
3
+
4
+ def initialize(args, column_options = {}, html_options = {}, object = nil, type = nil)
5
+ @args, @column_options, @html_options, @object, @type = args, column_options, html_options, object, type
6
+ @cells = []
7
+ if object
8
+ @attributes = (args.empty?) ? (get_columns - TableBuilder.reserved_columns) : @args
9
+ create_cells
10
+ end
11
+ end
12
+
13
+ # Create a new cell with the class FormatCell
14
+ # Add the object in @cells
15
+ def add_cell(object, args, type = nil, block = nil)
16
+ @cells << FormatCell.new(object, args, type, block)
17
+ end
18
+
19
+ # Return a tr line based on the type (:thead, :tbody or :tfoot)
20
+ def render_line
21
+ if @type == :thead
22
+ return content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_thead }.join), @html_options)
23
+ elsif @type == :tfoot
24
+ return content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_tfoot }.join), @html_options)
25
+ else
26
+ content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_tbody }.join), @html_options.merge(:class => "#{@html_options[:class]} #{@@template.cycle(:odd, :even)}".strip))
27
+ end
28
+ end
29
+
30
+ # Return the number of cells in line
31
+ def total_cells
32
+ @cells.size
33
+ end
34
+
35
+ protected
36
+
37
+ # Return each column in the model's database table
38
+ def content_columns
39
+ model_name(@object).constantize.content_columns.collect { |c| c.name.to_sym }.compact rescue []
40
+ end
41
+
42
+ # Return true if the column is in the model's database table
43
+ def model_have_column?(column)
44
+ model_name(@object).constantize.content_columns.each do |model_column|
45
+ return true if model_column.name == column.to_s
46
+ end
47
+ false
48
+ end
49
+
50
+ # Options only for cells
51
+ def only_cell_option?(key)
52
+ [:colspan].include? key
53
+ end
54
+
55
+ # Format ask to send options to cell
56
+ def format_options_to_cell(ask, options = @column_options)
57
+ options.each do |key, value|
58
+ if only_cell_option?(key)
59
+ if ask.is_a? Hash
60
+ ask.merge!(key => value)
61
+ else
62
+ ask = [ask] unless ask.is_a? Array
63
+ (ask.last.is_a? Hash and ask.last.has_key? :html) ? ask.last[:html].merge!(key => value) : ask << { :html => { key => value }}
64
+ end
65
+ end
66
+ end
67
+ ask
68
+ end
69
+
70
+ private
71
+
72
+ # Call after headers or columns with no attributes (table.headers)
73
+ # Create all the cells based on each column in the model's database table
74
+ # Create cell's actions based on option default_actions or on actions given (:actions => [:edit])
75
+ def create_cells
76
+ @attributes.each { |ask| add_cell(@object, format_options_to_cell(ask)) }
77
+ if @args.empty?
78
+ TableBuilder.default_actions.each { |action| add_cell(@object, action, :action) }
79
+ else
80
+ get_column_actions.each { |action| add_cell(@object, action, :action) }
81
+ end
82
+ end
83
+
84
+ # Return an Array of all actions given to headers or columns (:actions => [:edit, :delete])
85
+ def get_column_actions
86
+ if @column_options.has_key? :actions
87
+ if @column_options[:actions].is_a?(Array)
88
+ return @column_options[:actions]
89
+ else
90
+ return [ @column_options[:actions] ]
91
+ end
92
+ end
93
+ []
94
+ end
95
+
96
+ # Return an Array of the columns based on options :only or :except
97
+ def get_columns
98
+ if @column_options.has_key? :only
99
+ return @column_options[:only] if @column_options[:only].is_a?(Array)
100
+ [ @column_options[:only] ]
101
+ elsif @column_options.has_key? :except
102
+ return content_columns - @column_options[:except] if @column_options[:except].is_a?(Array)
103
+ content_columns - [ @column_options[:except] ]
104
+ else
105
+ content_columns
106
+ end
107
+ end
108
+
109
+ end
110
+ end
@@ -5,21 +5,28 @@ module SortingTableFor
5
5
  class << self
6
6
 
7
7
  # Set options to create a default scope
8
- def set_options(params, model_name, namespace, i18n_active = true)
9
- @params, @model_name, @namespace, @i18n_active = params, model_name, namespace, i18n_active
8
+ def set_options(params, model_name, i18n_active = true)
9
+ @model_name, @i18n_active = model_name, i18n_active
10
+ @action = (params.has_key? :action) ? params[:action].downcase : ''
11
+ @controller = (params.has_key? :controller) ? params[:controller].downcase : ''
12
+ if @controller.include? '/'
13
+ @namespace = @controller.split '/'
14
+ @controller = @namespace.pop
15
+ end
10
16
  end
11
17
 
12
- # Translate
13
18
  # Add a default scope if option scope isn't defined
14
19
  def translate(attribute, options = {}, type = nil)
15
- if !@i18n_active
20
+ unless @i18n_active
16
21
  return options[:value] if options.has_key? :value
17
22
  return attribute
18
23
  end
19
- if !options.has_key? :scope
20
- options[:scope] = create_scope
24
+ unless options.has_key? :scope
25
+ options[:scope] = create_scope
21
26
  options[:scope] << TableBuilder.i18n_add_header_action_scope if type and type == :header
22
27
  options[:scope] << TableBuilder.i18n_add_footer_action_scope if type and type == :footer
28
+ options[:scope] << options.delete(:add_scope)
29
+ options[:scope].flatten!
23
30
  end
24
31
  ::I18n.t(attribute, options)
25
32
  end
@@ -31,10 +38,10 @@ module SortingTableFor
31
38
  def create_scope
32
39
  return TableBuilder.i18n_default_scope.collect do |scope_value|
33
40
  case scope_value.to_sym
34
- when :controller then @params[:controller] ? @params[:controller].downcase : ''
35
- when :action then @params[:action] ? @params[:action].downcase : ''
41
+ when :controller then @controller
42
+ when :action then @action
36
43
  when :model then @model_name ? @model_name.downcase.to_s : ''
37
- when :namespace then @namespace ? @namespace.downcase.to_s : ''
44
+ when :namespace then @namespace ? @namespace : ''
38
45
  else scope_value.to_s
39
46
  end
40
47
  end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ module SortingTableFor
4
+ module ModelScope
5
+
6
+ # Include the methods in models
7
+ def self.included(base)
8
+ base.class_eval do
9
+ extend SingletonMethods
10
+ end
11
+ end
12
+
13
+ module SingletonMethods
14
+
15
+ # Return a scope of the object with an order
16
+ #
17
+ # === Usage
18
+ #
19
+ # sorting_table(the params) - Sorting by the given parameters
20
+ # sorting_table(the params, column name) - Sort by the column name with direction ascending, if no parameters
21
+ # sorting_table(the params, column name, direction) - Sort by the column name with the given direction, if no parameters
22
+ #
23
+ # === Exemples
24
+ #
25
+ # User.sorting_table(params)
26
+ # User.sorting_table(params, :username)
27
+ # User.sorting_table(params, :username, :desc)
28
+ #
29
+ def sorting_table(*args)
30
+ raise ArgumentError, 'sorting_table: Too many arguments (max : 3)' if args.size > 3
31
+ sort_table_param = get_sorting_table_params(args)
32
+ return scoped({}) if !sort_table_param and args.size == 1
33
+ sort, direction = get_sort_and_direction(sort_table_param, args)
34
+ return scoped({}) if !sort or !valid_column?(sort) or !valid_direction?(direction)
35
+ scoped({ :order => "#{sort} #{direction}" })
36
+ end
37
+
38
+ private
39
+
40
+ # Return the params for sorting table
41
+ def get_sorting_table_params(args)
42
+ return nil unless args.first.is_a? Hash
43
+ return nil unless args.first.has_key? SortingTableFor::TableBuilder.params_sort_table.to_s
44
+ args.first[SortingTableFor::TableBuilder.params_sort_table.to_s]
45
+ end
46
+
47
+ # Parse the params and return the column name and the direction
48
+ def get_sort_and_direction(sort_table_param, args)
49
+ if sort_table_param
50
+ key = sort_table_param.keys.first rescue nil
51
+ value = sort_table_param.values.first rescue nil
52
+ return nil if !key.is_a?(String) or !value.is_a?(String)
53
+ return key, value
54
+ end
55
+ return nil if args.size < 2
56
+ return args[1], 'asc' if args.size == 2
57
+ return args[1], args[2]
58
+ end
59
+
60
+ # Return true if the column name exist
61
+ def valid_column?(column)
62
+ column_names.include? column.to_s.downcase
63
+ end
64
+
65
+ # Return true if the direction exist
66
+ def valid_direction?(direction)
67
+ %[asc desc].include? direction.to_s.downcase
68
+ end
69
+
70
+ end
71
+ end
72
+ end
73
+
74
+ if defined? ActiveRecord
75
+ ActiveRecord::Base.send :include, SortingTableFor::ModelScope
76
+ end