sorting_table_for 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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