Class: DataTable::Table
- Inherits:
-
Object
- Object
- DataTable::Table
- Defined in:
- lib/data-table/table.rb
Overview
Config Options
id: the html id title: the title of the data table subtitle: the subtitle of the data table css_class: an extra css class to get applied to the table empty_text: the text to display of the collection is empty display_header => false: hide the column headers for the data table alternate_rows => false: turn off alternating of row css classes alternate_cols => true: turn on alternating of column classes, defaults to false
columns: an array of hashes of the column specs for this table
group_by: an array of columns to group on
subtotals: an array of hashes that contain the subtotal information for each column that should be subtotaled totals: an array of hashes that contain the total information for each column that should be totaled
Instance Attribute Summary collapse
-
#alternate_cols ⇒ Object
Returns the value of attribute alternate_cols.
-
#alternate_rows ⇒ Object
Returns the value of attribute alternate_rows.
-
#collection ⇒ Object
readonly
Returns the value of attribute collection.
-
#columns ⇒ Object
readonly
Returns the value of attribute columns.
-
#css_class ⇒ Object
Returns the value of attribute css_class.
-
#custom_headers ⇒ Object
Returns the value of attribute custom_headers.
-
#display_header ⇒ Object
Returns the value of attribute display_header.
-
#empty_text ⇒ Object
Returns the value of attribute empty_text.
-
#grouped_data ⇒ Object
readonly
Returns the value of attribute grouped_data.
-
#hide_if_empty ⇒ Object
Returns the value of attribute hide_if_empty.
-
#id ⇒ Object
Returns the value of attribute id.
-
#repeat_headers_for_groups ⇒ Object
Returns the value of attribute repeat_headers_for_groups.
-
#subtotal_calculations ⇒ Object
readonly
Returns the value of attribute subtotal_calculations.
-
#subtotals ⇒ Object
readonly
Returns the value of attribute subtotals.
-
#title ⇒ Object
Returns the value of attribute title.
-
#total_calculations ⇒ Object
readonly
Returns the value of attribute total_calculations.
-
#totals ⇒ Object
readonly
Returns the value of attribute totals.
Instance Method Summary collapse
-
#calculate(data, column_name, function, path = nil) ⇒ Object
TODO: Write test for this.
- #calculate_array_and_proc(function, data, column = nil, path = nil) ⇒ Object
- #calculate_avg(collection, column_name) ⇒ Object
- #calculate_many(function, data, column_name, _path = nil) ⇒ Object
- #calculate_max(collection, column_name) ⇒ Object
- #calculate_min(collection, column_name) ⇒ Object
- #calculate_parent_subtotals ⇒ Object
- #calculate_subtotals! ⇒ Object
- #calculate_sum(collection, column_name) ⇒ Object
-
#calculate_totals! ⇒ Object
TODO: Refactor to shorten method.
- #calculate_with_proc(function, data, column = nil, path = nil) ⇒ Object
-
#column(id, title = '', opts = {}, &b) ⇒ Object
Define a new column for the table.
- #custom_header(&blk) ⇒ Object
- #default_options! ⇒ Object
-
#group_by(group_column, level = {level: 0}, &_blk) ⇒ Object
TODO: allow for group column only, block only and group column and block.
- #group_data! ⇒ Object
-
#initialize(collection) ⇒ Table
constructor
A new instance of Table.
- #prepare_data ⇒ Object
-
#render ⇒ Object
GENERAL RENDERING.
- #render_custom_table_header ⇒ Object
- #render_data_table ⇒ Object
- #render_data_table_body(collection) ⇒ Object
- #render_data_table_header ⇒ Object
- #render_group(group_header, group_data) ⇒ Object
- #render_group_header(group_header, index = nil) ⇒ Object
- #render_group_recursive(collection, index = 1, group_parent = nil, ancestors = nil) ⇒ Object
- #render_grouped_data_table_body(collection) ⇒ Object
- #render_parent_subtotals(group_array) ⇒ Object
- #render_row(row, row_index, css_class = '', row_attributes = {}) ⇒ Object
- #render_rows(collection) ⇒ Object
-
#render_subtotals(group_header, _group_data = nil, ancestors = nil) ⇒ Object
ancestors should be an array.
-
#render_totals ⇒ Object
TOTALS AND SUBTOTALS.
- #repeat_headers(html) ⇒ Object
- #row_attributes(&b) ⇒ Object
-
#row_style(&b) ⇒ Object
define a custom block to be used to determine the css class for a row.
- #subtotal(column_name, function = nil, index = 0, &block) ⇒ Object
- #subtotals? ⇒ Boolean
- #th(header_text, options) ⇒ Object
- #total(column_name, function = nil, index = 0, &block) ⇒ Object
- #totals? ⇒ Boolean
Constructor Details
#initialize(collection) ⇒ Table
Returns a new instance of Table.
30 31 32 33 34 35 36 37 38 39 |
# File 'lib/data-table/table.rb', line 30 def initialize(collection) @collection = collection @grouped_collection = nil @columns = [] @groupings = [] @grouped_data = false @subtotals = [] @totals = [] end |
Instance Attribute Details
#alternate_cols ⇒ Object
Returns the value of attribute alternate_cols.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def alternate_cols @alternate_cols end |
#alternate_rows ⇒ Object
Returns the value of attribute alternate_rows.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def alternate_rows @alternate_rows end |
#collection ⇒ Object (readonly)
Returns the value of attribute collection.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def collection @collection end |
#columns ⇒ Object (readonly)
Returns the value of attribute columns.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def columns @columns end |
#css_class ⇒ Object
Returns the value of attribute css_class.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def css_class @css_class end |
#custom_headers ⇒ Object
Returns the value of attribute custom_headers.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def custom_headers @custom_headers end |
#display_header ⇒ Object
Returns the value of attribute display_header.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def display_header @display_header end |
#empty_text ⇒ Object
Returns the value of attribute empty_text.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def empty_text @empty_text end |
#grouped_data ⇒ Object (readonly)
Returns the value of attribute grouped_data.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def grouped_data @grouped_data end |
#hide_if_empty ⇒ Object
Returns the value of attribute hide_if_empty.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def hide_if_empty @hide_if_empty end |
#id ⇒ Object
Returns the value of attribute id.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def id @id end |
#repeat_headers_for_groups ⇒ Object
Returns the value of attribute repeat_headers_for_groups.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def repeat_headers_for_groups @repeat_headers_for_groups end |
#subtotal_calculations ⇒ Object (readonly)
Returns the value of attribute subtotal_calculations.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def subtotal_calculations @subtotal_calculations end |
#subtotals ⇒ Object (readonly)
Returns the value of attribute subtotals.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def subtotals @subtotals end |
#title ⇒ Object
Returns the value of attribute title.
26 27 28 |
# File 'lib/data-table/table.rb', line 26 def title @title end |
#total_calculations ⇒ Object (readonly)
Returns the value of attribute total_calculations.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def total_calculations @total_calculations end |
#totals ⇒ Object (readonly)
Returns the value of attribute totals.
23 24 25 |
# File 'lib/data-table/table.rb', line 23 def totals @totals end |
Instance Method Details
#calculate(data, column_name, function, path = nil) ⇒ Object
TODO: Write test for this
381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/data-table/table.rb', line 381 def calculate(data, column_name, function, path = nil) column = @columns.select { |col| col.name == column_name } if function.is_a?(Proc) calculate_with_proc(function, data, column, path) elsif function.is_a?(Array) && function[1].is_a?(Proc) calculate_array_and_proc(function, data, column_name, path) elsif function.is_a?(Array) calculate_many(function, data, column_name, path) else send("calculate_#{function}", data, column_name) end end |
#calculate_array_and_proc(function, data, column = nil, path = nil) ⇒ Object
402 403 404 405 406 407 408 409 |
# File 'lib/data-table/table.rb', line 402 def calculate_array_and_proc(function, data, column = nil, path = nil) result = send("calculate_#{function[0]}", data, column) case function[1].arity when 1 then function[1].call(result) when 2 then function[1].call(result, column.first) when 3 then function[1].call(result, column.first, path.last) end end |
#calculate_avg(collection, column_name) ⇒ Object
425 426 427 428 |
# File 'lib/data-table/table.rb', line 425 def calculate_avg(collection, column_name) sum = calculate_sum(collection, column_name) sum / collection.size end |
#calculate_many(function, data, column_name, _path = nil) ⇒ Object
411 412 413 414 415 416 417 418 419 |
# File 'lib/data-table/table.rb', line 411 def calculate_many(function, data, column_name, _path = nil) function.each do |func| if func.is_a? Array send("calculate_#{func[0]}", data, column_name) else send("calculate_#{func}", data, column_name) end end end |
#calculate_max(collection, column_name) ⇒ Object
430 431 432 |
# File 'lib/data-table/table.rb', line 430 def calculate_max(collection, column_name) collection.collect { |r| r[column_name].to_f }.max end |
#calculate_min(collection, column_name) ⇒ Object
434 435 436 |
# File 'lib/data-table/table.rb', line 434 def calculate_min(collection, column_name) collection.collect { |r| r[column_name].to_f }.min end |
#calculate_parent_subtotals ⇒ Object
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
# File 'lib/data-table/table.rb', line 360 def calculate_parent_subtotals @parent_subtotals = Hash.new { |h, k| h[k] = [] } # Iterate over all the parent groups parent_groups = @groupings.slice(0, @groupings.count - 1).compact parent_groups.count.times do # Group by each parent on the fly @subtotals.each_with_index do |subtotal, index| @collection.group_by_recursive(parent_groups).each_pair_with_parents do |group_name, data, parents| subtotal.each do |s| path = parents + [group_name] result = calculate(data, s[0], s[1], path) @parent_subtotals[path][index] ||= {} if @parent_subtotals[path][index].nil? @parent_subtotals[path][index][s[0]] = {s[1] => result} end end end parent_groups.pop end end |
#calculate_subtotals! ⇒ Object
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/data-table/table.rb', line 343 def calculate_subtotals! raise 'Subtotals only work with grouped results sets' unless @grouped_data @subtotal_calculations ||= Hash.new { |h, k| h[k] = [] } @subtotals.each_with_index do |subtotal_type, index| subtotal_type.each do |subtotal| @collection.each_pair_with_parents(@groupings.count) do |group_name, group_data, parents| path = parents + [group_name] result = calculate(group_data, subtotal[0], subtotal[1], path) (0..index).each do |index| @subtotal_calculations[path][index] ||= {} end @subtotal_calculations[path][index][subtotal[0]] = {subtotal[1] => result} end end end end |
#calculate_sum(collection, column_name) ⇒ Object
421 422 423 |
# File 'lib/data-table/table.rb', line 421 def calculate_sum(collection, column_name) collection.inject(0) { |sum, row| sum + row[column_name].to_f } end |
#calculate_totals! ⇒ Object
TODO: Refactor to shorten method. Also revise tests.
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/data-table/table.rb', line 326 def calculate_totals! @total_calculations = [] @totals.each_with_index do |row, index| next if row.nil? if @collection.is_a?(Hash) collection = [] @collection.each_pair_recursive { |_k, v| collection.concat(v) } end collection = @collection if @collection.is_a? Array @total_calculations[index] = {} if @total_calculations[index].nil? row.each do |item| @total_calculations[index][item[0]] = calculate(collection, item[0], item[1]) end end end |
#calculate_with_proc(function, data, column = nil, path = nil) ⇒ Object
394 395 396 397 398 399 400 |
# File 'lib/data-table/table.rb', line 394 def calculate_with_proc(function, data, column = nil, path = nil) case function.arity when 1 then function.call(data) when 2 then function.call(data, column.first) when 3 then function.call(data, column.first, path.last) end end |
#column(id, title = '', opts = {}, &b) ⇒ Object
Define a new column for the table
59 60 61 |
# File 'lib/data-table/table.rb', line 59 def column(id, title = '', opts = {}, &b) @columns << DataTable::Column.new(id, title, opts, &b) end |
#custom_header(&blk) ⇒ Object
156 157 158 |
# File 'lib/data-table/table.rb', line 156 def custom_header(&blk) instance_eval(&blk) end |
#default_options! ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/data-table/table.rb', line 41 def @id = '' @title = '' @subtitle = '' @css_class = '' @empty_text = 'No records found' @hide_if_empty = false @display_header = true @alternate_rows = true @alternate_cols = false @subtotal_title = 'Subtotal:' @total_title = 'Total:' @repeat_headers_for_groups = false @custom_headers = [] @row_attributes = nil end |
#group_by(group_column, level = {level: 0}, &_blk) ⇒ Object
TODO: allow for group column only, block only and group column and block
173 174 175 176 177 178 179 180 |
# File 'lib/data-table/table.rb', line 173 def group_by(group_column, level = {level: 0}, &_blk) if level.nil? && @groupings.count >= 1 raise 'a level designation is required when using multiple groupings.' end @grouped_data = true @groupings[level ? level[:level] : 0] = group_column @columns.reject! { |c| c.name == group_column } end |
#group_data! ⇒ Object
182 183 184 185 186 187 188 189 |
# File 'lib/data-table/table.rb', line 182 def group_data! @groupings.compact! @collection = if @groupings.count > 1 collection.group_by_recursive(@groupings) else collection.group_by { |row| row[@groupings.first] } end end |
#prepare_data ⇒ Object
63 64 65 66 67 68 |
# File 'lib/data-table/table.rb', line 63 def prepare_data calculate_parent_subtotals if @groupings.count > 1 group_data! if @grouped_data calculate_subtotals! if subtotals? calculate_totals! if totals? end |
#render ⇒ Object
GENERAL RENDERING
73 74 75 |
# File 'lib/data-table/table.rb', line 73 def render render_data_table end |
#render_custom_table_header ⇒ Object
102 103 104 105 106 107 108 |
# File 'lib/data-table/table.rb', line 102 def render_custom_table_header html = "<tr class='custom-header'>" @custom_headers.each do |h| html << "<th class=\"#{h[:css]}\" colspan=\"#{h[:colspan]}\" style=\"#{h[:style]}\">#{h[:text]}</th>" end html << '</tr>' end |
#render_data_table ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/data-table/table.rb', line 77 def render_data_table html = "<table id='#{@id}' class='data_table #{@css_class}' cellspacing='0' cellpadding='0'>" html << "<caption>#{@title}</caption>" if @title html << render_data_table_header if @display_header if @collection.any? html << render_data_table_body(@collection) html << render_totals if totals? else html << "<tr><td class='empty_data_table' colspan='#{@columns.size}'>#{@empty_text}</td></tr>" end html << '</table>' end |
#render_data_table_body(collection) ⇒ Object
110 111 112 113 114 115 116 |
# File 'lib/data-table/table.rb', line 110 def render_data_table_body(collection) if @grouped_data render_grouped_data_table_body(collection) else "<tbody>#{render_rows(collection)}</tbody>" end end |
#render_data_table_header ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/data-table/table.rb', line 90 def render_data_table_header html = '<thead>' html << render_custom_table_header unless @custom_headers.empty? html << '<tr>' @columns.each do |col| html << col.render_column_header end html << '</tr></thead>' end |
#render_group(group_header, group_data) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/data-table/table.rb', line 217 def render_group(group_header, group_data) html = "<tbody class='#{group_header.to_s.downcase.gsub(/[^A-Za-z0-9]+/, '_')}'>" html << render_group_header(group_header, 0) if group_data.is_a? Array html << render_rows(group_data) html << render_subtotals(group_header, group_data) if subtotals? elsif group_data.is_a? Hash html << render_group_recursive(group_data, 1, group_header) end html << '</tbody>' end |
#render_group_header(group_header, index = nil) ⇒ Object
199 200 201 202 203 204 205 206 207 |
# File 'lib/data-table/table.rb', line 199 def render_group_header(group_header, index = nil) css_classes = ['group_header'] css_classes << ["level_#{index}"] unless index.nil? html = "<tr class='#{css_classes.join(' ')}'>" html << "<th colspan='#{@columns.size}'>#{group_header}</th>" html << '</tr>' repeat_headers(html) if @repeat_headers_for_groups html end |
#render_group_recursive(collection, index = 1, group_parent = nil, ancestors = nil) ⇒ Object
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/data-table/table.rb', line 229 def render_group_recursive(collection, index = 1, group_parent = nil, ancestors = nil) html = '' ancestors ||= [] collection.each_pair do |group_name, group_data| ancestors << group_parent unless ancestors[0] == group_parent ancestors << group_name unless ancestors.length == @groupings.length if group_data.is_a?(Hash) html << render_group_header(group_name, index) html << render_group_recursive(group_data, index + 1, nil, ancestors) elsif group_data.is_a?(Array) html << render_group_header(group_name, index) html << render_rows(group_data) ancestors.pop html << render_subtotals(group_name, group_data, ancestors) if subtotals? end end html << render_parent_subtotals(ancestors) if @parent_subtotals ancestors.pop html end |
#render_grouped_data_table_body(collection) ⇒ Object
191 192 193 194 195 196 197 |
# File 'lib/data-table/table.rb', line 191 def render_grouped_data_table_body(collection) html = '' collection.keys.each do |group_name| html << render_group(group_name, collection[group_name]) end html end |
#render_parent_subtotals(group_array) ⇒ Object
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/data-table/table.rb', line 268 def render_parent_subtotals(group_array) html = '' @parent_subtotals[group_array].each_with_index do |group, index| next if group.nil? html << "<tr class='parent_subtotal " html << "index_#{index} #{group_array.join('_').gsub(/\s/, '_').downcase}'>" @columns.each do |col| value = group[col.name] ? group[col.name].values[0] : nil html << col.render_cell(value) end html << '</tr>' end html end |
#render_row(row, row_index, css_class = '', row_attributes = {}) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/data-table/table.rb', line 132 def render_row(row, row_index, css_class = '', row_attributes = {}) attributes = if row_attributes.nil? '' else row_attributes.map { |attr, val| "#{attr}='#{val}'" }.join ' ' end html = "<tr class='row_#{row_index} #{css_class}' #{attributes}>" @columns.each_with_index do |col, col_index| cell = begin row[col.name] rescue nil end html << col.render_cell(cell, row, row_index, col_index) end html << '</tr>' end |
#render_rows(collection) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/data-table/table.rb', line 118 def render_rows(collection) html = '' collection.each_with_index do |row, row_index| css_class = @alternate_rows && row_index.odd? ? 'alt ' : '' if @row_style && style = @row_style.call(row, row_index) css_class << style end attributes = @row_attributes.nil? ? {} : @row_attributes.call(row) html << render_row(row, row_index, css_class, attributes) end html end |
#render_subtotals(group_header, _group_data = nil, ancestors = nil) ⇒ Object
ancestors should be an array
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/data-table/table.rb', line 285 def render_subtotals(group_header, _group_data = nil, ancestors = nil) html = '' path = ancestors.nil? ? [] : ancestors.dup path << group_header is_first_subtotal = true @subtotal_calculations[path].each_with_index do |group, index| next if group.empty? html << "<tr class='subtotal index_#{index} #{'first' if is_first_subtotal}'>" @columns.each do |col| value = group[col.name] ? group[col.name].values[0] : nil html << col.render_cell(value) end html << '</tr>' is_first_subtotal = false end html end |
#render_totals ⇒ Object
TOTALS AND SUBTOTALS
253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/data-table/table.rb', line 253 def render_totals html = '<tfoot>' @total_calculations.each_with_index do |totals_row, index| next if totals_row.nil? html << "<tr class='total index_#{index}'>" @columns.each do |col| value = totals_row[col.name] ||= nil html << col.render_cell(value) end html << '</tr>' end html << '</tfoot>' end |
#repeat_headers(html) ⇒ Object
209 210 211 212 213 214 215 |
# File 'lib/data-table/table.rb', line 209 def repeat_headers(html) html << "<tr class='col_headers'>" @columns.each_with_index do |col, _i| html << col.render_column_header end html << '</tr>' end |
#row_attributes(&b) ⇒ Object
164 165 166 |
# File 'lib/data-table/table.rb', line 164 def row_attributes(&b) @row_attributes = b end |
#row_style(&b) ⇒ Object
define a custom block to be used to determine the css class for a row.
152 153 154 |
# File 'lib/data-table/table.rb', line 152 def row_style(&b) @row_style = b end |
#subtotal(column_name, function = nil, index = 0, &block) ⇒ Object
307 308 309 310 |
# File 'lib/data-table/table.rb', line 307 def subtotal(column_name, function = nil, index = 0, &block) raise 'You must supply an index value' if @subtotals.count >= 1 && index.nil? total_row @subtotals, column_name, function, index, &block end |
#subtotals? ⇒ Boolean
312 313 314 |
# File 'lib/data-table/table.rb', line 312 def subtotals? !@subtotals.empty? end |
#th(header_text, options) ⇒ Object
160 161 162 |
# File 'lib/data-table/table.rb', line 160 def th(header_text, ) @custom_headers << .merge(text: header_text) end |
#total(column_name, function = nil, index = 0, &block) ⇒ Object
316 317 318 319 |
# File 'lib/data-table/table.rb', line 316 def total(column_name, function = nil, index = 0, &block) raise 'You must supply an index value' if @totals.count >= 1 && index.nil? total_row @totals, column_name, function, index, &block end |
#totals? ⇒ Boolean
321 322 323 |
# File 'lib/data-table/table.rb', line 321 def totals? !@totals.empty? end |