Fri, 2007 Dec 21

Building a Builder, Part 3

Posted in Computing at 22:16 by jmorgan

This continues on two previous articles about setting up a custom FormBuilder for use in your Rails applications. See Part 1 (Introduction) and Part 2 (The helper method and related work on the NodeBuilder class). The particular Builder I’m working through is meant to generate simple tables and forms, nothing real fancy, with minimal view code. So, today, I’m going to look at creating a basic table via the builder.

In my last entry, I added the NodeBuilder#to_s method:

            def to_s
              "<div>" + @rows.join("<br />") + "</div>"
            end
          

I’m going to flesh this out to create a table instead of a div with line breaks:

            def to_s
          
              # Setup table header
              headers = ""
              @headers.each { |h| headers << @template.content_tag("th", "#{h[:label]}") }
              
              headers = @template.content_tag("thead",
                        @template.content_tag("tr", headers) )
              
              # Setup content rows of table
              content = ""
              @rows.each do |items|
                row_content = ""
                items.each { |item| row_content << @template.content_tag("td", item) }
                content << @template.content_tag("tr", row_content)
              end
              content = @template.content_tag("tbody", content)
          
              # Put header rows in content rows into table element
              return @template.content_tag("table", headers + content)
            end
          

This takes information from the @headers and @rows variables and puts that data in a table, great for index views. The trickier part is getting that information into the variables. I’ll use the #field method to do that:

            def field(label, content = "", options = {})
          
              # Allow just a symbol representing the attribute name to be passed. Use a titleized version of the field name for the label
              if label.kind_of?(Symbol) && content.empty?
                content = label
                label = label.to_s.titleize
              end
              
             # If this is the first item (row) populate the @header array; otherwise, it's already populated and can be skipped.
              if @item == 0
                @headers << {:label => label} # This is a hash because I might want to add more information later.
              end
          
              # If the content is a symbol, assume it's a method name on the current object.
              if content.kind_of?(Symbol)
                if content == :type # To avoid deprecation warnings
                  content = @object[content]
                else
                  content = @template.escape_once(@object.send(content)) 
                end
              end
          
              # Now, add the content to the @rows variable.
              @rows[@item] << content
            end
          

Well, that’s pretty close to what I want, except that I want to also be able to use the with text_fields, password_fields, etc. So:

            # create_labelled_field defines text_field and similar methods.
            def self.create_labelled_field(method_name)
              define_method(method_name) do |label, *args|
          
                # if label is a symbol, assume its an attribute name;
                # use the titleized form of attribute name as the label.
                # In either case, call super without the label to get the content.
                if label.kind_of?(Symbol)
                  args = [label] + args
                  field(label.to_s.titleize, super(*args))
                else
                  field(label, super(*args))
                end
              end
            end
            
            # This creates the methods for text_field, etc, which call create_labelled_field
            (field_helpers + ["select"]).each do |name|
              create_labelled_field(name)
            end
          

Yay. I may come back later and add some clearer explanations but this works (more or less) and provides a nice starting point to DRY-ing up forms. I may add a few more articles to this section, but they’ll probably be along the lines of “and here’s the goofy stuff you can do now…”


0 Comments

Add a comment