require 'postrunner/HTMLBuilder'
module PostRunner
class FlexiTable
class Attributes
attr_accessor :min_terminal_width, :halign
def initialize(attrs = {})
@min_terminal_width = nil
@halign = nil
attrs.each do |name, value|
ivar_name = '@' + name.to_s
unless instance_variable_defined?(ivar_name)
Log.fatal "Unsupported attribute #{name}"
end
instance_variable_set(ivar_name, value)
end
end
def [](name)
ivar_name = '@' + name.to_s
return nil unless instance_variable_defined?(ivar_name)
instance_variable_get(ivar_name)
end
end
class Cell
def initialize(table, row, content, attributes)
@table = table
@row = row
@content = content
@attributes = attributes
@column_index = nil
@row_index = nil
end
def min_terminal_width
@content.to_s.length
end
def set_indicies(col_idx, row_idx)
@column_index = col_idx
@row_index = row_idx
end
def to_s
s = @content.to_s
width = get_attribute(:min_terminal_width)
case get_attribute(:halign)
when :left, nil
s + ' ' * (width - s.length)
when :right
' ' * (width - s.length) + s
when :center
w = width - s.length
left_padding = w / 2
right_padding = w / 2 + w % 2
' ' * left_padding + s + ' ' * right_padding
else
raise "Unknown alignment"
end
end
def to_html(doc)
doc.td(@content.respond_to?('to_html') ?
@content.to_html(doc) : @content.to_s)
end
private
def get_attribute(name)
@attributes[name] ||
@row.attributes[name] ||
@table.column_attributes[@column_index][name]
end
end
class Row < Array
attr_reader :attributes
def initialize(table)
@table = table
@attributes = Attributes.new
super()
end
def cell(content, attributes)
c = Cell.new(@table, self, content, attributes)
self << c
c
end
def set_indicies(col_idx, row_idx)
self[col_idx].set_indicies(col_idx, row_idx)
end
def set_row_attributes(attributes)
@attributes = Attributes.new(attributes)
end
def to_s
s = ''
frame = @table.frame
s << '|' if frame
s << join(frame ? '|' : ' ')
s << '|' if frame
s
end
def to_html(doc)
doc.tr {
each { |c| c.to_html(doc) }
}
end
end
attr_reader :frame, :column_attributes
def initialize(&block)
@head_rows = []
@body_rows = []
@foot_rows = []
@column_count = 0
@current_section = :body
@current_row = nil
@frame = true
@column_attributes = []
instance_eval(&block) if block_given?
end
def head
@current_section = :head
end
def body
@current_section = :body
end
def foot
@current_section = :foot
end
def new_row
@current_row = nil
end
def cell(content, attributes = {})
if @current_row.nil?
case @current_section
when :head
@head_rows
when :body
@body_rows
when :foot
@foot_rows
else
raise "Unknown section #{@current_section}"
end << (@current_row = Row.new(self))
end
@current_row.cell(content, attributes)
end
def row(cells, attributes = {})
cells.each { |c| cell(c) }
set_row_attributes(attributes)
new_row
end
def set_column_attributes(col_attributes)
col_attributes.each.with_index do |ca, idx|
@column_attributes[idx] = Attributes.new(ca)
end
end
def set_row_attributes(row_attributes)
unless @current_row
raise "No current row. Use after first cell definition but before " +
"new_row call."
end
@current_row.set_row_attributes(row_attributes)
end
def enable_frame(enabled)
@frame = enabled
end
def to_s
index_table
calc_terminal_columns
s = frame_line_to_s
s << rows_to_s(@head_rows)
s << frame_line_to_s unless @head_rows.empty?
s << rows_to_s(@body_rows)
s << frame_line_to_s unless @body_rows.empty?
s << rows_to_s(@foot_rows)
s << frame_line_to_s unless @foot_rows.empty?
s
end
def to_html(doc)
doc.table {
@head_rows.each { |r| r.to_html(doc) }
@body_rows.each { |r| r.to_html(doc) }
@foot_rows.each { |r| r.to_html(doc) }
}
end
private
def index_table
@column_count = (@head_rows[0] || @body_rows[0]).length
@column_count.times do |i|
index_table_rows(i, @head_rows)
index_table_rows(i, @body_rows)
index_table_rows(i, @foot_rows)
end
end
def index_table_rows(col_idx, rows)
rows.each.with_index do |r, row_idx|
r.set_indicies(col_idx, row_idx)
end
end
def calc_terminal_columns
@column_count.times do |i|
col_mtw = nil
col_mtw = calc_section_teminal_columns(i, col_mtw, @head_rows)
col_mtw = calc_section_teminal_columns(i, col_mtw, @body_rows)
col_mtw = calc_section_teminal_columns(i, col_mtw, @foot_rows)
@column_attributes[i] = Attributes.new unless @column_attributes[i]
@column_attributes[i].min_terminal_width = col_mtw
end
end
def calc_section_teminal_columns(col_idx, col_mtw, rows)
rows.each do |r|
if r[col_idx].nil?
raise ArgumentError, "Not all rows have same number of cells"
end
mtw = r[col_idx].min_terminal_width
if col_mtw.nil? || col_mtw < mtw
col_mtw = mtw
end
end
col_mtw
end
def rows_to_s(x_rows)
x_rows.empty? ? '' : (x_rows.map { |r| r.to_s}.join("\n") + "\n")
end
def frame_line_to_s
return '' unless @frame
s = '+'
@column_attributes.each do |c|
s += '-' * c.min_terminal_width + '+'
end
s + "\n"
end
end
end