lib/decisive/template_handler.rb in decisive-0.7.1 vs lib/decisive/template_handler.rb in decisive-0.8.0

- old
+ new

@@ -1,10 +1,11 @@ -require "csv" require "action_view" -require "active_support/core_ext/string/inflections" -require "spreadsheet" +require "decisive/stream_csv_context" +require "decisive/render_csv_context" +require "decisive/render_xls_context" + module Decisive class TemplateHandler def self.register ActionView::Template.register_template_handler 'decisive', self end @@ -31,11 +32,11 @@ else context.to_csv(force_quotes: true) end else - response.headers["Content-Type"] = "application/vnd.ms-excel" + response.headers["Content-Type"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" context.to_xls end RUBY end end @@ -55,223 +56,17 @@ module DSL def csv records, filename:, stream: true, &block if stream raise StreamingNotEnabledByControllerError unless controller.is_a?(ActionController::Live) raise StreamIncompatibleBlockArgumentError if block.arity != 0 - StreamContext.new([], records, filename, &block) + StreamCSVContext.new(records, filename, block) else - RenderContext.new(records, filename, block) + RenderCSVContext.new(records, filename, block) end end def xls worksheets=nil, filename:, &block - if worksheets - XLSContext.new(worksheets, filename, block) - else - XLSWithWorksheetsContext.new(filename, [], &block) - end - end - end - - class XLSWithWorksheetsContext < Struct.new(:filename, :worksheets) - class Worksheet < Struct.new(:records, :name, :block); end - - def initialize *args, &block - super - instance_eval &block - end - - def to_xls - to_string(render(Spreadsheet::Workbook.new)) - end - - def csv? - false - end - - private - - def worksheet records, name:, &block - worksheets.push Worksheet.new(records, name, block) - end - - def render xls - worksheets.each do |worksheet| - sheet = xls.create_worksheet(name: sanitize_name(worksheet.name)) - - rows = to_array(worksheet) - - rows.each.with_index do |row, index| - sheet.row(index).concat row - end - end - xls - end - - def sanitize_name name - name - .gsub(/[\[\]\*\?:\/\\\t\n\r]/, " ") - .gsub(/^'/, "") - .gsub(/'$/, "") - .strip - .slice(0,31) - end - - def to_array worksheet - context = RenderContext.new(worksheet.records, nil, worksheet.block) - context.send(:header) + context.send(:body) - end - - def to_string xls - io = StringIO.new - xls.write(io) - io.rewind - string = io.read - string.force_encoding(Encoding::ASCII_8BIT) - string - end - end - - - class XLSContext < Struct.new(:worksheets, :filename, :block) - def to_xls - to_string(render(Spreadsheet::Workbook.new)) - end - - def csv? - false - end - - private - - def render xls - worksheets.each do |name, enumerable| - sheet = xls.create_worksheet(name: sanitize_name(name)) - - rows = to_array(enumerable) - - rows.each.with_index do |row, index| - sheet.row(index).concat row - end - end - xls - end - - def sanitize_name name - name - .gsub(/[\[\]\*\?:\/\\\t\n\r]/, " ") - .gsub(/^'/, "") - .gsub(/'$/, "") - .strip - .slice(0,31) - end - - def to_array records - context = RenderContext.new(records, nil, block) - context.send(:header) + context.send(:body) - end - - def to_string xls - io = StringIO.new - xls.write(io) - io.rewind - string = io.read - string.force_encoding(Encoding::ASCII_8BIT) - string - end - end - - class StreamContext < Struct.new(:columns, :records, :filename) - class Column < Struct.new(:label, :block); end - - def initialize *args, &block - super - instance_eval &block - end - - def column label, value=nil, &block # field, label: field.to_s.humanize, &block - value ||= label.parameterize.underscore.to_sym - block ||= ->(record) { record.send(value) } - columns << Column.new(label, block) - end - - def each - yield header - - records.map do |record| - row = columns.map do |column| - column.block.call(record).to_s - end - yield row - end - end - - def csv? - true - end - - private - - def header - columns.map(&:label) - end - end - - class RenderContext < Struct.new(:records, :filename, :block) - def to_csv(*args, **kwargs) - (header + body).map do |row| - row.to_csv(*args, **kwargs) - end.join - end - - def csv? - true - end - - private - - def header - [keys] - end - - def body - hashes.map do |hash| - hash.values_at(*keys) - end - end - - def keys - @keys ||= hashes.flat_map(&:keys).uniq - end - - def hashes - @hashes ||= records.map do |record| - Row.new(record, block).to_hash - end - end - - class Row < Struct.new(:record, :block) - module Nothing; end - - def to_hash - @hash = {} - instance_exec record, &block - @hash - end - - private - - def column key, value=Nothing, &block - @hash[key] = if block_given? - block.call(record) - elsif value.is_a?(Symbol) - record.send(value) - elsif value == Nothing - record.send(key.parameterize.underscore.to_sym) - else - value - end.to_s - end + RenderXLSContext.new(worksheets, filename, block) end end end