class BigDecimal; def pretty_print(p) p.text to_s; end; end module GoodData class DataResult attr_reader :data def initialize(data) @data = data end def print puts to_s end def to_s(options={}) with_indices = options[:index] || false a = to_table.to_a data = a.transpose data.unshift((1..a.length).to_a) if with_indices data.each_with_index.map{|col, i| col.unshift(i.zero? ? nil : i) if with_indices # inserts row labels # w = col.map{|cell| cell.to_s.length}.max # w = "column width" # col.each_with_index.map{|cell, i| i.zero? ? cell.to_s.center(w) : cell.to_s.ljust(w)} # alligns the column # }.transpose.map{|row| "[#{row.join(' | ')}]"}.unshift("").join("\n") end def to_table raise "Should be implemented in subclass" end end class EmptyResult < DataResult def initialize(data, options = {}) super(data) @options = options assemble_table end def to_s "No Data" end def assemble_table @table = [[]] # CSV::Table.new([GoodData::Row.new([],[],false)]) end def to_table @table end def without_column_headers @table end def == (otherDataResult) false end def diff(otherDataResult) ['empty'] end end # class SFDataResult < DataResult # # def initialize(data, options = {}) # super(data) # @options = options # assemble_table # end # # def assemble_table # sf_data = data[:queryResponse][:result][:records] # sf_data = sf_data.is_a?(Hash) ? [sf_data] : sf_data # if @options[:soql] # # puts @options[:soql] # fields = @options[:soql].strip.match(/SELECT (.*) FROM/i)[1] # @headers = fields.strip.split(",").map do |item| # item.strip.split(/\s/) # end.map do |item| # item.last.to_sym # end # # pp @headers # elsif @options[:headers] # @headers = @options[:headers] # else # @headers = sf_data.first.keys - [:type, :Id] # end # @table = CSV::Table.new(sf_data.collect do |line| # GoodData::Row.new([], @headers.map {|h| line[h] || ' '}, false) # end) # rescue # fail "Unable to assemble the table. Either the data provided are empty or the SOQL is malformed." # end # # def to_table # @table # end # # def == (otherDataResult) # result = true # len = @table.length # other_table = otherDataResult.to_table # if len != other_table.length # # puts "TABLES ARE OF DIFFERENT SIZES" # return false # end # # diff(otherDataResult).empty?() ? true : false # # end # # def diff(otherDataResult) # other_table = otherDataResult.to_table # differences = [] # # @table.each do |row| # differences << row unless other_table.detect {|r| r == row} # end # differences # end # # end class ReportDataResult < DataResult ROW_LIMIT = 10000000 attr_reader :row_headers, :column_headers, :table, :headers_height, :headers_width def initialize(data) super @row_headers = [] @column_headers = [] @table = [] @row_headers, @headers_width = tabularize_rows @column_headers, @headers_height = tabularize_columns assemble_table end def without_column_headers @table = table.transpose[headers_height, ROW_LIMIT].transpose self end def to_data_table table.transpose[headers_height, ROW_LIMIT].transpose[headers_width, ROW_LIMIT] end def each_line to_table.each {|line| yield line} end alias :each_row :each_line def each_column table.each {|line| yield line} end def to_a table.to_a end def to_table table.transpose end def [](index) to_table[index] end alias :row :[] def column(index) table[index] end def include_row?(row) to_table.include?(row) end def include_column?(row) table.include?(row) end def == (otherDataResult) result = true csv_table = to_table len = csv_table.length table = otherDataResult.respond_to?(:to_table) ? otherDataResult.to_table : otherDataResult return false if len != table.length diff(otherDataResult).empty?() ? true : false end def diff(otherDataResult) csv_table = to_table other_table = otherDataResult.respond_to?(:to_table) ? otherDataResult.to_table : otherDataResult differences = [] csv_table.each do |row| differences << row unless other_table.detect {|r| r == row} end differences end private def each_level(table, level, children, lookup) max_level = level + 1 children.each do |kid| first = kid["first"] last = kid["last"] repetition = last - first + 1 repetition.times do |i| table[first + i] ||= [] if kid["type"] == 'total' table[first + i][level] = kid["id"] else table[first + i][level] = lookup[level][kid["id"].to_s] end end if (!kid["children"].empty?) new_level = each_level(table, level+1, kid["children"], lookup) max_level = [max_level, new_level].max end end max_level end def tabularize_rows rows = data["xtab_data"]["rows"] kids = rows["tree"]["children"] if kids.empty? || (kids.size == 1 && kids.first['type'] == 'metric') headers, size = [[nil]], 0 else headers = [] size = each_level(headers, 0, rows["tree"]["children"], rows["lookups"]) end return headers, size end def tabularize_columns columns = data["xtab_data"]["columns"] kids = columns["tree"]["children"] if kids.empty? || (kids.size == 1 && kids.first['type'] == 'metric') headers, size = [[nil]], 0 else headers = [] size = each_level(headers, 0, columns["tree"]["children"], columns["lookups"]) end return headers, size end def assemble_table() # puts "=== COLUMNS === #{column_headers.size}x#{headers_height}" (column_headers.size).times do |i| (headers_height).times do |j| table[headers_width + i] ||= [] # puts "[#{headers_width + i}][#{j}] #{column_headers[i][j]}" table[headers_width + i][j] = column_headers[i][j] end end # puts "=== ROWS ===" (row_headers.size).times do |i| (headers_width).times do |j| table[j] ||= [] # puts "[#{j}][#{headers_height + i}] #{row_headers[i][j]}" table[j][headers_height + i] = row_headers[i][j] end end xtab_data = data["xtab_data"]["data"] # puts "=== DATA === #{column_headers.size}x#{row_headers.size}" (column_headers.size).times do |i| (row_headers.size).times do |j| table[headers_width + i] ||= [] # puts "[#{headers_width + i}, #{headers_height + j}] [#{i}][#{j}]=#{xtab_data[j][i]}" val = xtab_data[j][i] table[headers_width + i][headers_height + j] = val.nil? ? val : BigDecimal(val) end end end end end