require 'table_helper/body_row' module TableHelper # Represents the body of the table. In HTML, you can think of this as # the tag of the table. class Body < HtmlElement # If set to :odd or :even, every odd or even-numbered row will have the # class 'alternate' appended to its html attributes. Default is nil. attr_accessor :alternate_rows # The caption to display in the collection is empty attr_accessor :empty_caption def initialize(collection, header) #:nodoc: super() @collection, @header = collection, header @empty_caption = 'No matches found.' end def alternate_rows=(value) #:nodoc: raise ArgumentError, 'alternate_rows must be set to :odd or :even' if value && ![:odd, :even].include?(value) @alternate_rows = value end # Builds the body of the table. This includes the actual data that is # generated for each object in the collection. # # +build+ expects a block that defines the data in each cell. Each # iteration of the block will provide the object being rendered, the row # within the table that will be built and the index of the object. For # example, # # body.build do |row, post, index| # row.title "
#{post.title}
" # row.category post.category.name # end # # In addition, to specifying the data, you can also modify the html # options of the row. For more information on doing this, see the # BodyRow class. # # If the collection is empty and +empty_caption+ is set on the body, # then the actual body will be replaced by a single row containing the # html that was stored in +empty_caption+. # # == Default Values # # Whenever possible, the default value of a cell will be set to the # object's attribute with the same name as the cell. For example, # if a Post consists of the attribute +title+, then the cell for the # title will be prepopulated with that attribute's value: # # body.build do |row, post index| # row.category post.category.name # end # # row.title is already set to post.category so there's no need to # manually set the value of that cell. However, it is always possible # to override the default value like so: # # body.build do |row, post, index| # row.title link_to(post.title, post_url(post)) # row.category post.category.name # end def build(&block) @content = '' # Display nothing if there are no objects to display if @collection.empty? && @empty_caption row = Row.new row[:class] = 'no_content' html_options = {} html_options[:colspan] = @header.column_names.size if @header.column_names.size > 1 row.cell nil, @empty_caption, html_options @content << row.html else @collection.each_with_index do |object, i| @content << build_row(object, i, &block) end end @content end # Builds a row for an object in the table. # # The provided block should set the values for each cell in the row. def build_row(object, index = @collection.index(object), &block) row = BodyRow.new(object, @header) row.alternate = alternate_rows ? index.send("#{@alternate_rows}?") : false yield row.builder, object, index if block_given? row.html end def html #:nodoc: html_options = @html_options.dup html_options[:class] = (html_options[:class].to_s + ' alternate').strip if alternate_rows content_tag(tag_name, content, html_options) end private def tag_name 'tbody' end def content @content end end end