# frozen_string_literal: true
class INat::Report::Table
attr_reader :columns
def initialize
@columns = []
@rows = []
@line_no = 0
end
def column title, width: nil, align: nil, data: nil, marker: false, &block
if data == nil && !block_given?
raise ArgumentError, "Data argument or block must be provided!", caller
end
@columns << {
title: title,
width: width,
align: align,
data: data,
marker: marker,
block: block
}
end
private :column
def row **data
if !data.has_key?(:line_no)
@line_no += 1
data[:line_no] ||= @line_no
end
@rows << data
end
def rows *data
data.each do |r|
row(**r)
end
@rows
end
def empty?
@rows.empty?
end
def << data
if Array === data
rows(*data)
elsif Hash === data
row(**data)
else
raise TypeError, "Invalid data type: #{ data.inspect }!", caller
end
end
private def th column
style = ""
case column[:width]
when Numeric
style += "width:#{ column[:width] }em;"
when String
style += "width:#{ column[:width] };"
end
style += "text-align:#{ column[:align] };" if column[:align]
if style.empty?
"
#{ column[:title] } | "
else
"#{ column[:title] } | "
end
end
private def header
result = []
result << ""
result += @columns.map { |c| th(c) }
result << "
"
result.join ""
end
private def td column, row
inner = case column[:data]
when String, Symbol
row[column[:data].intern]
when Proc
column[:data].call row
when nil
column[:block].call row
end
inner = inner.to_html if inner.respond_to?(:to_html)
style = ""
case column[:width]
when Numeric
style += "width:#{ column[:width] }em;"
when String
style += "width:#{ column[:width] };"
end
style += "text-align:#{ column[:align] };" if column[:align]
marker = nil
if column[:marker]
anchor = row[:anchor]
marker = ""
end
if style.empty?
"#{ marker }#{ inner } | "
else
"#{ marker }#{ inner } | "
end
end
private def row_to_html row
result = []
result << ""
result += @columns.map { |c| td(c, row) }
result << ""
result.join "\n"
end
def to_html
result = []
result << ""
result << header
result += @rows.map { |r| row_to_html(r) }
result << "
"
result.join "\n"
end
end
module INat::Report::Table::DSL
include INat::Report
def table &block
tbl = Table::new
tbl.instance_eval(&block) if block_given?
tbl
end
module_function :table
end