def multi_input(methods, options = {})
      raise ArgumentError.new('methods must be either a Hash or an Array') unless methods.is_a?(::Hash) || methods.is_a?(::Array)
      options = options.dup

      # add some defaults.
      options = {
          class: 'form-control',
          read_only: false,
          attrib: { },
          style: { },
          input_group_size: ''
      }.merge(options)

      # build the style attribute.
      options[:attrib][:style] ||= ''
      options[:style].each do |k,v|
        if k.to_s == 'width'
          options[:input_group_width] = "width: #{v};"
        else
          options[:attrib][:style] += "#{k}: #{v};"
        end
      end

      # Standardize the "methods" list to be an array of arrays.
      if methods.is_a?(::Hash)
        methods = methods.to_a
      elsif methods.is_a?(::Array)
        methods = methods.map{|v| v.is_a?(::Array) ? v : [ v, v.to_s.humanize ] }
      end

      # Extract field attributes.
      fields = { }
      methods.each_with_index do |(meth,label), index|
        index += 1
        fields[meth] = options[:attrib].merge(options.delete(:"field_#{index}") || {})
        fields[meth][:readonly] = 'readonly' if options[:read_only]
        fields[meth][:class] ||= options[:class]
        if fields[meth][:style].is_a?(::Hash)
          fields[meth][:style] = fields[meth][:style].to_a.map{|v| v.map(&:to_s).join(':') + ';'}.join(' ')
        end
        fields[meth][:placeholder] ||= label
      end

      if %w(sm small input-group-sm).include?(options[:input_group_size])
        options[:input_group_size] = 'input-group-sm'
      elsif %w(lg large input-group-lg).include?(options[:input_group_size])
        options[:input_group_size] = 'input-group-lg'
      else
        options[:input_group_size] = ''
      end

      # We want each field to have a width specified.
      remaining_width = 100.0
      remaining_fields = fields.count
      width_match = /(?:^|;)\s*width:\s*([^;]+);/

      # pass 1, compute remaining width.
      fields.each do |meth, attr|
        if attr[:style] =~ width_match
          width = $1
          if width[-1] == '%'
            width = width[0...-1].strip.to_f
            if width > remaining_width
              Incline::Log::warn "Field width adds up to more than 100% in multi_input affecting field \"#{meth}\"."
              width = remaining_width
              attr[:style] = attr[:style].gsub(width_match_1, '').gsub(width_match_2, '') + "width: #{width}%;"
            end
            remaining_width -= width
            remaining_width = 0 if remaining_width < 0
            remaining_fields -= 1
          else
            # we do not support pixel, em, etc, so dump the unsupported width.
            Incline::Log::warn "Unsupported width style in multi_input affecting field \"#{meth}\": #{width}"
            attr[:style] = attr[:style].gsub(width_match_1, '').gsub(width_match_2, '')
          end
        end
      end

      # pass 2, fill in missing widths.
      fields.each do |meth, attr|
        unless attr[:style] =~ width_match
          width =
            if remaining_fields > 1
              (remaining_width / remaining_fields).to_i
            else
              remaining_width
            end

          Incline::Log::warn "Computed field width of 0% in multi_input affecting field \"#{meth}\"." if width == 0

          attr[:style] += "width: #{width}%;"
          remaining_width -= width
          remaining_fields -= 1
          remaining_width = 0 if remaining_width < 0
        end
      end

      fld = []
      fields.each do |meth, attr|
        attr[:value] = object.send(meth)
        fld << text_field(meth, attr)
      end

      "<div class=\"input-group #{options[:input_group_size]}\" style=\"#{options[:input_group_width]}\">#{fld.join}</div>".html_safe
    end
def multi_input(methods, options = {})
      raise ArgumentError.new('methods must be either a Hash or an Array') unless methods.is_a?(::Hash) || methods.is_a?(::Array)
      options = options.dup

      # add some defaults.
      options = {
          class: 'form-control',
          read_only: false,
          attrib: { },
          style: { },
          input_group_size: ''
      }.merge(options)

      # build the style attribute.
      options[:attrib][:style] ||= ''
      options[:style].each do |k,v|
        if k.to_s == 'width'
          options[:input_group_width] = "width: #{v};"
        else
          options[:attrib][:style] += "#{k}: #{v};"
        end
      end

      # Standardize the "methods" list to be an array of arrays.
      if methods.is_a?(::Hash)
        methods = methods.to_a
      elsif methods.is_a?(::Array)
        methods = methods.map{|v| v.is_a?(::Array) ? v : [ v, v.to_s.humanize ] }
      end

      # Extract field attributes.
      fields = { }
      methods.each_with_index do |(meth,label), index|
        index += 1
        fields[meth] = options[:attrib].merge(options.delete(:"field_#{index}") || {})
        fields[meth][:readonly] = 'readonly' if options[:read_only]
        fields[meth][:class] ||= options[:class]
        if fields[meth][:style].is_a?(::Hash)
          fields[meth][:style] = fields[meth][:style].to_a.map{|v| v.map(&:to_s).join(':') + ';'}.join(' ')
        end
        fields[meth][:placeholder] ||= label
      end

      if %w(sm small input-group-sm).include?(options[:input_group_size])
        options[:input_group_size] = 'input-group-sm'
      elsif %w(lg large input-group-lg).include?(options[:input_group_size])
        options[:input_group_size] = 'input-group-lg'
      else
        options[:input_group_size] = ''
      end

      # We want each field to have a width specified.
      remaining_width = 100.0
      remaining_fields = fields.count
      width_match = /(?:^|;)\s*width:\s*([^;]+);/

      # pass 1, compute remaining width.
      fields.each do |meth, attr|
        if attr[:style] =~ width_match
          width = $1
          if width[-1] == '%'
            width = width[0...-1].strip.to_f
            if width > remaining_width
              Incline::Log::warn "Field width adds up to more than 100% in multi_input affecting field \"#{meth}\"."
              width = remaining_width
              attr[:style] = attr[:style].gsub(width_match_1, '').gsub(width_match_2, '') + "width: #{width}%;"
            end
            remaining_width -= width
            remaining_width = 0 if remaining_width < 0
            remaining_fields -= 1
          else
            # we do not support pixel, em, etc, so dump the unsupported width.
            Incline::Log::warn "Unsupported width style in multi_input affecting field \"#{meth}\": #{width}"
            attr[:style] = attr[:style].gsub(width_match_1, '').gsub(width_match_2, '')
          end
        end
      end

      # pass 2, fill in missing widths.
      fields.each do |meth, attr|
        unless attr[:style] =~ width_match
          width =
            if remaining_fields > 1
              (remaining_width / remaining_fields).to_i
            else
              remaining_width
            end

          Incline::Log::warn "Computed field width of 0% in multi_input affecting field \"#{meth}\"." if width == 0

          attr[:style] += "width: #{width}%;"
          remaining_width -= width
          remaining_fields -= 1
          remaining_width = 0 if remaining_width < 0
        end
      end

      fld = []
      fields.each do |meth, attr|
        attr[:value] = object.send(meth)
        fld << text_field(meth, attr)
      end

      "<div class=\"input-group #{options[:input_group_size]}\" style=\"#{options[:input_group_width]}\">#{fld.join}</div>".html_safe
    end
Creates a multiple input field control for the provided form.

 The +methods+ parameter can be either an array of method names, or a hash with method names as the
 keys and labels as the values.

 For instance:
   [ :alpha, :bravo, :charlie ]
   { :alpha => 'The first item', :bravo => 'The second item', :charlie => 'The third item' }

 *Valid options:*

 class::
     The CSS class to apply. Defaults to 'form-control'.

 read_only::
     Should the control be read-only?  Defaults to false.

 style::
     A hash containing CSS styling attributes to apply to the input fields.
     Width is generated automatically or specified individually using the "field_n" option.

 input_group_size::
     You can specific *small* or *large* to change the control's overall size.

 attrib::
     Any additional attributes you want to apply to the input fields.

 field_n::
     Sets specific attributes for field "n".  These values will override the "attrib" and "style" options.

   f.multi_input [ :city, :state, :zip ],
       :field_1 => { :maxlength => 30, :style => { :width => '65%' } },
       :field_2 => { :maxlength => 2 },
       :field_3 => { :maxlength => 10, :style => { :width => '25%' } }