module ExpressTemplates
module Components
# Create an html table from a collection of data.
#
# Typically this will be a collection of models
# of the same type. Each member of the collection must
# respond to the column names provided.
#
# Example:
#
# ````ruby
# table_for(:people) do |t|
# t.column :name
# t.column :email
# t.column :phone
# t.column :hourly_rate, header: "Rate",
# formatter: -> (amount) {'$%0.2f' % amount rescue 'N/A'}
# end
# ````
#
# This assumes that a @people variable will exist in the
# view and that it will be a collection whose members respond to
# :name, :email, :phone, :hourly_rate
#
# This will result in markup like the following:
#
#
#
#
# Name |
# Email |
# Phone |
# Rate |
#
#
#
#
# Steven Talcott Smith |
# steve@aelogica.com |
# 415-555-1212 |
# $250.00 |
#
#
#
#
# Note that column options include :formatter and :header.
#
# :formatter may be a stabby lambda which is passed the value to be formatted.
#
# :header may be either a string
#
class TableFor < Base
include Capabilities::Configurable
include Capabilities::Building
def initialize(*args)
super(*args)
_process_args!(args) # from Configurable
yield(self) if block_given?
end
attr :columns
def column(name, options = {})
@columns ||= []
@columns << Column.new(name, options)
end
emits -> {
table(my[:id]) {
thead {
tr {
columns.each do |column|
th.send(column.name) {
column.title
}
end
}
}
tbody {
for_each("@#{my[:id]}".to_sym) {
tr(id: "#{my[:id].to_s.singularize}-{{#{my[:id].to_s.singularize}.id}}",
class: my[:id].to_s.singularize) {
columns.each do |column|
if(column.has_actions?)
td(class: column.name) {
column.show_actions(my[:id].to_s)
}
else
td(class: column.name) {
column.format(my[:id].to_s.singularize)
}
end
end
}
}
}
}
}
def wrap_for_stack_trace(body)
"ExpressTemplates::Components::TableFor.render_in(self) {\n#{body}\n}"
end
def compile
wrap_for_stack_trace(lookup(:markup))
end
class Column
attr :name, :options
def initialize(name, options = {})
@name = name
@options = options
@formatter = options[:formatter]
@header = options[:header]
@actions = options[:actions] || []
end
def has_actions?
@actions.any?
end
def format(item_name)
if @formatter.nil?
"\#\{#{item_name}.#{name}\}"
elsif @formatter.kind_of?(Proc)
"\#\{(#{@formatter.source}).call(#{item_name}.#{name})\}"
end
end
def show_actions(item_name)
action_links = StringIO.new
@actions.each do |action|
action_name = action.to_s
if action_name.eql?('edit')
action_links.puts "Edit"
elsif action_name.eql?('delete')
action_links.puts "Delete"
elsif action_name.eql?('show')
action_links.puts "Show"
end
end
action_links.string
end
def title
case
when @header.nil?
@name.to_s.try(:titleize)
when @header.kind_of?(String)
@header
when @header.kind_of?(Proc)
"{{(#{@header.source}).call(#{@name})}}"
end
end
end
end
end
end