forme 1.11.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +54 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +235 -209
- data/Rakefile +1 -7
- data/lib/forme/bs3.rb +18 -4
- data/lib/forme/erb.rb +13 -25
- data/lib/forme/form.rb +144 -149
- data/lib/forme/input.rb +1 -1
- data/lib/forme/rails.rb +39 -83
- data/lib/forme/raw.rb +2 -2
- data/lib/forme/tag.rb +3 -12
- data/lib/forme/template.rb +110 -0
- data/lib/forme/transformers/formatter.rb +4 -1
- data/lib/forme/transformers/helper.rb +0 -1
- data/lib/forme/transformers/inputs_wrapper.rb +4 -4
- data/lib/forme/version.rb +2 -2
- data/lib/forme.rb +15 -2
- data/lib/roda/plugins/forme.rb +1 -1
- data/lib/roda/plugins/forme_erubi_capture.rb +57 -0
- data/lib/roda/plugins/forme_route_csrf.rb +16 -34
- data/lib/roda/plugins/forme_set.rb +39 -76
- data/lib/sequel/plugins/forme.rb +40 -50
- data/lib/sequel/plugins/forme_i18n.rb +3 -1
- data/lib/sequel/plugins/forme_set.rb +3 -1
- data/spec/all.rb +1 -1
- data/spec/bs3_reference_spec.rb +18 -18
- data/spec/bs3_sequel_plugin_spec.rb +17 -17
- data/spec/bs3_spec.rb +23 -11
- data/spec/erb_helper.rb +69 -58
- data/spec/erubi_capture_helper.rb +198 -0
- data/spec/forme_spec.rb +21 -32
- data/spec/rails_integration_spec.rb +39 -25
- data/spec/roda_integration_spec.rb +118 -70
- data/spec/sequel_helper.rb +0 -1
- data/spec/sequel_i18n_helper.rb +1 -1
- data/spec/sequel_i18n_plugin_spec.rb +3 -2
- data/spec/sequel_plugin_spec.rb +29 -12
- data/spec/sequel_set_plugin_spec.rb +9 -2
- data/spec/shared_erb_specs.rb +71 -0
- data/spec/sinatra_integration_spec.rb +5 -6
- data/spec/spec_helper.rb +21 -8
- metadata +9 -7
- data/lib/forme/erb_form.rb +0 -74
- data/lib/forme/sinatra.rb +0 -17
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # frozen-string-literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'rack/utils'
         | 
| 4 | 
            -
             | 
| 4 | 
            +
            require_relative '../../forme/template'
         | 
| 5 5 |  | 
| 6 6 | 
             
            class Roda
         | 
| 7 7 | 
             
              module RodaPlugins
         | 
| @@ -34,74 +34,6 @@ class Roda | |
| 34 34 | 
             
                    :missing_namespace=>"no content in expected namespace"
         | 
| 35 35 | 
             
                  }.freeze
         | 
| 36 36 |  | 
| 37 | 
            -
                  # Forme::Form subclass that adds hidden fields with metadata that can be used
         | 
| 38 | 
            -
                  # to automatically process form submissions.
         | 
| 39 | 
            -
                  class Form < ::Forme::ERB::Form
         | 
| 40 | 
            -
                    def initialize(obj, opts=nil)
         | 
| 41 | 
            -
                      super
         | 
| 42 | 
            -
                      @forme_namespaces = @opts[:namespace]
         | 
| 43 | 
            -
                    end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                    # Try adding hidden fields to all forms
         | 
| 46 | 
            -
                    def form(*)
         | 
| 47 | 
            -
                      if block_given?
         | 
| 48 | 
            -
                        super do |f|
         | 
| 49 | 
            -
                          yield f
         | 
| 50 | 
            -
                          hmac_hidden_fields
         | 
| 51 | 
            -
                        end
         | 
| 52 | 
            -
                      else
         | 
| 53 | 
            -
                        t = super
         | 
| 54 | 
            -
                        if tags = hmac_hidden_fields
         | 
| 55 | 
            -
                          tags.each{|tag| t << tag}
         | 
| 56 | 
            -
                        end
         | 
| 57 | 
            -
                        t
         | 
| 58 | 
            -
                      end
         | 
| 59 | 
            -
                    end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                    private
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    # Add hidden fields with metadata, if the form has an object associated that
         | 
| 64 | 
            -
                    # supports the forme_inputs method, and it includes inputs.
         | 
| 65 | 
            -
                    def hmac_hidden_fields
         | 
| 66 | 
            -
                      if (obj = @opts[:obj]) && obj.respond_to?(:forme_inputs) && (forme_inputs = obj.forme_inputs)
         | 
| 67 | 
            -
                        columns = []
         | 
| 68 | 
            -
                        valid_values = {}
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                        forme_inputs.each do |field, input|
         | 
| 71 | 
            -
                          next unless col = obj.send(:forme_column_for_input, input)
         | 
| 72 | 
            -
                          col = col.to_s
         | 
| 73 | 
            -
                          columns << col
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                          next unless validation = obj.send(:forme_validation_for_input, field, input)
         | 
| 76 | 
            -
                          validation[0] = validation[0].to_s
         | 
| 77 | 
            -
                          has_nil = false
         | 
| 78 | 
            -
                          validation[1] = validation[1].map do |v|
         | 
| 79 | 
            -
                            has_nil ||= v.nil?
         | 
| 80 | 
            -
                            v.to_s
         | 
| 81 | 
            -
                          end
         | 
| 82 | 
            -
                          validation[1] << nil if has_nil
         | 
| 83 | 
            -
                          valid_values[col] = validation
         | 
| 84 | 
            -
                        end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                        return if columns.empty?
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                        data = {}
         | 
| 89 | 
            -
                        data['columns'] = columns
         | 
| 90 | 
            -
                        data['namespaces'] = @forme_namespaces
         | 
| 91 | 
            -
                        data['csrf'] = @opts[:csrf]
         | 
| 92 | 
            -
                        data['valid_values'] = valid_values unless valid_values.empty?
         | 
| 93 | 
            -
                        data['form_version'] = @opts[:form_version] if @opts[:form_version]
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                        data = data.to_json
         | 
| 96 | 
            -
                        tags = []
         | 
| 97 | 
            -
                        tags << tag(:input, :type=>:hidden, :name=>:_forme_set_data, :value=>data)
         | 
| 98 | 
            -
                        tags << tag(:input, :type=>:hidden, :name=>:_forme_set_data_hmac, :value=>OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, @opts[:roda].class.opts[:forme_set_hmac_secret], data))
         | 
| 99 | 
            -
                        tags.each{|tag| emit(tag)}
         | 
| 100 | 
            -
                        tags
         | 
| 101 | 
            -
                      end
         | 
| 102 | 
            -
                    end
         | 
| 103 | 
            -
                  end
         | 
| 104 | 
            -
             | 
| 105 37 | 
             
                  module InstanceMethods
         | 
| 106 38 | 
             
                    # Return hash based on submitted parameters, with :values key
         | 
| 107 39 | 
             
                    # being submitted values for the object, and :validations key
         | 
| @@ -151,15 +83,46 @@ class Roda | |
| 151 83 | 
             
                      raise Error, ERROR_MESSAGES[type]
         | 
| 152 84 | 
             
                    end
         | 
| 153 85 |  | 
| 154 | 
            -
                    # Use form class that adds hidden fields for metadata.
         | 
| 155 | 
            -
                    def _forme_form_class
         | 
| 156 | 
            -
                      Form
         | 
| 157 | 
            -
                    end
         | 
| 158 | 
            -
             | 
| 159 86 | 
             
                    # Include a reference to the current scope to the form.  This reference is needed
         | 
| 160 87 | 
             
                    # to correctly construct the HMAC.
         | 
| 161 | 
            -
                    def _forme_form_options( | 
| 162 | 
            -
                       | 
| 88 | 
            +
                    def _forme_form_options(obj, attr, opts)
         | 
| 89 | 
            +
                      super
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                      opts[:_after] = lambda do |form|
         | 
| 92 | 
            +
                        if (obj = form.opts[:obj]) && obj.respond_to?(:forme_inputs) && (forme_inputs = obj.forme_inputs)
         | 
| 93 | 
            +
                          columns = []
         | 
| 94 | 
            +
                          valid_values = {}
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                          forme_inputs.each do |field, input|
         | 
| 97 | 
            +
                            next unless col = obj.send(:forme_column_for_input, input)
         | 
| 98 | 
            +
                            col = col.to_s
         | 
| 99 | 
            +
                            columns << col
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                            next unless validation = obj.send(:forme_validation_for_input, field, input)
         | 
| 102 | 
            +
                            validation[0] = validation[0].to_s
         | 
| 103 | 
            +
                            has_nil = false
         | 
| 104 | 
            +
                            validation[1] = validation[1].map do |v|
         | 
| 105 | 
            +
                              has_nil ||= v.nil?
         | 
| 106 | 
            +
                              v.to_s
         | 
| 107 | 
            +
                            end
         | 
| 108 | 
            +
                            validation[1] << nil if has_nil
         | 
| 109 | 
            +
                            valid_values[col] = validation
         | 
| 110 | 
            +
                          end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                          return if columns.empty?
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                          data = {}
         | 
| 115 | 
            +
                          data['columns'] = columns
         | 
| 116 | 
            +
                          data['namespaces'] = form.opts[:namespace]
         | 
| 117 | 
            +
                          data['csrf'] = form.opts[:csrf]
         | 
| 118 | 
            +
                          data['valid_values'] = valid_values unless valid_values.empty?
         | 
| 119 | 
            +
                          data['form_version'] = form.opts[:form_version] if form.opts[:form_version]
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                          data = data.to_json
         | 
| 122 | 
            +
                          form.tag(:input, :type=>:hidden, :name=>:_forme_set_data, :value=>data)
         | 
| 123 | 
            +
                          form.tag(:input, :type=>:hidden, :name=>:_forme_set_data_hmac, :value=>OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, self.class.opts[:forme_set_hmac_secret], data))
         | 
| 124 | 
            +
                        end
         | 
| 125 | 
            +
                      end
         | 
| 163 126 | 
             
                    end
         | 
| 164 127 |  | 
| 165 128 | 
             
                    # Internals of forme_parse_hmac and forme_set_hmac.
         | 
    
        data/lib/sequel/plugins/forme.rb
    CHANGED
    
    | @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            # frozen-string-literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
            require 'thread'
         | 
| 3 | 
            +
            require_relative '../../forme'
         | 
| 5 4 |  | 
| 6 5 | 
             
            module Sequel # :nodoc:
         | 
| 7 6 | 
             
              module Plugins # :nodoc:
         | 
| @@ -37,7 +36,8 @@ module Sequel # :nodoc: | |
| 37 36 | 
             
                    # of the association for the form's +obj+. Inside the block, calls to
         | 
| 38 37 | 
             
                    # the +input+ and +inputs+ methods for the receiver treat the associated
         | 
| 39 38 | 
             
                    # object as the recevier's +obj+, using name and id attributes that work
         | 
| 40 | 
            -
                    # with the Sequel +nested_attributes+ plugin.
         | 
| 39 | 
            +
                    # with the Sequel +nested_attributes+ plugin. Returns the HTML generated by
         | 
| 40 | 
            +
                    # the subform.
         | 
| 41 41 | 
             
                    #
         | 
| 42 42 | 
             
                    # The following options are currently supported:
         | 
| 43 43 | 
             
                    # :inputs :: Automatically call +inputs+ with the given values.  Using
         | 
| @@ -59,45 +59,50 @@ module Sequel # :nodoc: | |
| 59 59 | 
             
                    # :skip_primary_key :: Skip adding a hidden primary key field for existing
         | 
| 60 60 | 
             
                    #                      objects.
         | 
| 61 61 | 
             
                    def subform(association, opts={}, &block)
         | 
| 62 | 
            -
                       | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 62 | 
            +
                      content_added do
         | 
| 63 | 
            +
                        nested_obj = opts.has_key?(:obj) ? opts[:obj] : obj.send(association)
         | 
| 64 | 
            +
                        ref = obj.class.association_reflection(association)
         | 
| 65 | 
            +
                        multiple = ref.returns_array?
         | 
| 66 | 
            +
                        grid = opts[:grid]
         | 
| 67 | 
            +
                        ns = "#{association}_attributes"
         | 
| 67 68 |  | 
| 68 | 
            -
                      contents = proc do
         | 
| 69 69 | 
             
                        send(multiple ? :each_obj : :with_obj, nested_obj, ns) do |no, i|
         | 
| 70 | 
            -
                           | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                           | 
| 75 | 
            -
                             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
                              end
         | 
| 70 | 
            +
                          input(ref.associated_class.primary_key, :type=>:hidden, :label=>nil, :wrapper=>nil) unless no.new? || opts[:skip_primary_key]
         | 
| 71 | 
            +
                        end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                        contents = proc do
         | 
| 74 | 
            +
                          send(multiple ? :each_obj : :with_obj, nested_obj, ns) do |no, i|
         | 
| 75 | 
            +
                            options = opts.dup
         | 
| 76 | 
            +
                            if grid
         | 
| 77 | 
            +
                              options.delete(:legend)
         | 
| 79 78 | 
             
                            else
         | 
| 80 | 
            -
                              if  | 
| 81 | 
            -
                                options[:legend] | 
| 79 | 
            +
                              if options.has_key?(:legend)
         | 
| 80 | 
            +
                                if options[:legend].respond_to?(:call)
         | 
| 81 | 
            +
                                  options[:legend] = multiple ? options[:legend].call(no, i) : options[:legend].call(no)
         | 
| 82 | 
            +
                                end
         | 
| 82 83 | 
             
                              else
         | 
| 83 | 
            -
                                 | 
| 84 | 
            +
                                if multiple
         | 
| 85 | 
            +
                                  options[:legend] = humanize("#{obj.model.send(:singularize, association)} ##{i+1}")
         | 
| 86 | 
            +
                                else
         | 
| 87 | 
            +
                                  options[:legend] = humanize(association)
         | 
| 88 | 
            +
                                end
         | 
| 84 89 | 
             
                              end
         | 
| 85 90 | 
             
                            end
         | 
| 91 | 
            +
                            options[:subform] = true
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                            inputs(options[:inputs]||[], options, &block)
         | 
| 86 94 | 
             
                          end
         | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                        
         | 
| 97 | 
            +
                        if grid
         | 
| 98 | 
            +
                          labels = opts.fetch(:labels){opts[:inputs].map{|l, *| humanize(l)} if opts[:inputs]}
         | 
| 99 | 
            +
                          legend = opts.fetch(:legend){humanize(association)}
         | 
| 100 | 
            +
                          inputs_opts = opts[:inputs_opts] || {}
         | 
| 101 | 
            +
                          inputs(inputs_opts.merge(:inputs_wrapper=>:table, :nested_inputs_wrapper=>:tr, :wrapper=>:td, :labeler=>nil, :labels=>labels, :legend=>legend), &contents)
         | 
| 102 | 
            +
                        else
         | 
| 103 | 
            +
                          contents.call
         | 
| 89 104 | 
             
                        end
         | 
| 90 105 | 
             
                      end
         | 
| 91 | 
            -
                      
         | 
| 92 | 
            -
                      if grid
         | 
| 93 | 
            -
                        labels = opts.fetch(:labels){opts[:inputs].map{|l, *| humanize(l)} if opts[:inputs]}
         | 
| 94 | 
            -
                        legend = opts.fetch(:legend){humanize(association)}
         | 
| 95 | 
            -
                        inputs_opts = opts[:inputs_opts] || {}
         | 
| 96 | 
            -
                        inputs(inputs_opts.merge(:inputs_wrapper=>:table, :nested_inputs_wrapper=>:tr, :wrapper=>:td, :labeler=>nil, :labels=>labels, :legend=>legend), &contents)
         | 
| 97 | 
            -
                      else
         | 
| 98 | 
            -
                        contents.call
         | 
| 99 | 
            -
                      end
         | 
| 100 | 
            -
                      nil
         | 
| 101 106 | 
             
                    end
         | 
| 102 107 | 
             
                  end
         | 
| 103 108 |  | 
| @@ -192,6 +197,7 @@ module Sequel # :nodoc: | |
| 192 197 |  | 
| 193 198 | 
             
                    # Set the error option correctly if the field contains errors
         | 
| 194 199 | 
             
                    def handle_errors(f)
         | 
| 200 | 
            +
                      return if opts.has_key?(:error)
         | 
| 195 201 | 
             
                      if e = obj.errors.on(f)
         | 
| 196 202 | 
             
                        opts[:error] = e.join(', ')
         | 
| 197 203 | 
             
                      end
         | 
| @@ -346,7 +352,7 @@ module Sequel # :nodoc: | |
| 346 352 |  | 
| 347 353 | 
             
                    # Delegate to the +form+.
         | 
| 348 354 | 
             
                    def humanize(s)
         | 
| 349 | 
            -
                      form.humanize(s)
         | 
| 355 | 
            +
                      form.respond_to?(:humanize) ? form.humanize(s) : s.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
         | 
| 350 356 | 
             
                    end
         | 
| 351 357 |  | 
| 352 358 | 
             
                    # If the column allows +NULL+ values, use a three-valued select
         | 
| @@ -451,21 +457,6 @@ module Sequel # :nodoc: | |
| 451 457 | 
             
                    end
         | 
| 452 458 | 
             
                  end
         | 
| 453 459 |  | 
| 454 | 
            -
                  # Helper module used for Sequel forms using ERB template integration.  Necessary for
         | 
| 455 | 
            -
                  # proper subform handling when using such forms with partials.
         | 
| 456 | 
            -
                  module ERBSequelForm
         | 
| 457 | 
            -
                    # Capture the inside of the inputs, injecting it into the template
         | 
| 458 | 
            -
                    # if a block is given, or returning it as a string if not.
         | 
| 459 | 
            -
                    def subform(*, &block)
         | 
| 460 | 
            -
                      if block
         | 
| 461 | 
            -
                        capture(block){super}
         | 
| 462 | 
            -
                      else
         | 
| 463 | 
            -
                        capture{super}
         | 
| 464 | 
            -
                      end
         | 
| 465 | 
            -
                    end
         | 
| 466 | 
            -
                  end
         | 
| 467 | 
            -
                  SinatraSequelForm = ERBSequelForm
         | 
| 468 | 
            -
             | 
| 469 460 | 
             
                  class Form < ::Forme::Form
         | 
| 470 461 | 
             
                    include SequelForm
         | 
| 471 462 | 
             
                  end
         | 
| @@ -485,7 +476,6 @@ module Sequel # :nodoc: | |
| 485 476 | 
             
                      unless klass = MUTEX.synchronize{FORM_CLASSES[base]}
         | 
| 486 477 | 
             
                        klass = Class.new(base)
         | 
| 487 478 | 
             
                        klass.send(:include, SequelForm)
         | 
| 488 | 
            -
                        klass.send(:include, ERBSequelForm) if defined?(::Forme::ERB::Form) && base == ::Forme::ERB::Form
         | 
| 489 479 | 
             
                        MUTEX.synchronize{FORM_CLASSES[base] = klass}
         | 
| 490 480 | 
             
                      end
         | 
| 491 481 | 
             
                      klass
         | 
| @@ -18,6 +18,7 @@ module Sequel # :nodoc: | |
| 18 18 | 
             
                  module InstanceMethods
         | 
| 19 19 | 
             
                    # Hash with column name symbol keys and Forme::SequelInput values
         | 
| 20 20 | 
             
                    def forme_inputs
         | 
| 21 | 
            +
                      return (@forme_inputs || {}) if frozen?
         | 
| 21 22 | 
             
                      @forme_inputs ||= {}
         | 
| 22 23 | 
             
                    end
         | 
| 23 24 |  | 
| @@ -25,12 +26,13 @@ module Sequel # :nodoc: | |
| 25 26 | 
             
                    # is a boolean flag, if true, the uploaded values should be a subset of the allowed values,
         | 
| 26 27 | 
             
                    # otherwise, there should be a single uploaded value that is a member of the allowed values.
         | 
| 27 28 | 
             
                    def forme_validations
         | 
| 29 | 
            +
                      return (@forme_validations || {}) if frozen?
         | 
| 28 30 | 
             
                      @forme_validations ||= {}
         | 
| 29 31 | 
             
                    end
         | 
| 30 32 |  | 
| 31 33 | 
             
                    # Keep track of the inputs used.
         | 
| 32 34 | 
             
                    def forme_input(_form, field, _opts)
         | 
| 33 | 
            -
                      forme_inputs[field] = super
         | 
| 35 | 
            +
                      frozen? ? super : (forme_inputs[field] = super)
         | 
| 34 36 | 
             
                    end
         | 
| 35 37 |  | 
| 36 38 | 
             
                    # Given the hash of submitted parameters, return a hash containing information on how to
         | 
    
        data/spec/all.rb
    CHANGED
    
    | @@ -1,2 +1,2 @@ | |
| 1 1 | 
             
            $: << 'lib'
         | 
| 2 | 
            -
            Dir | 
| 2 | 
            +
            Dir.new(File.dirname(__FILE__)).each{|f| require_relative f if f.end_with?('_spec.rb')}
         | 
    
        data/spec/bs3_reference_spec.rb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 1 | 
            +
            require_relative 'spec_helper'
         | 
| 2 | 
            +
            require_relative 'sequel_helper'
         | 
| 3 | 
            +
            require_relative '../lib/forme/bs3'
         | 
| 4 4 |  | 
| 5 5 | 
             
            describe "Forme Bootstrap3 (BS3) forms" do
         | 
| 6 6 | 
             
              def sel(opts, s)
         | 
| @@ -33,7 +33,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 33 33 | 
             
                end
         | 
| 34 34 |  | 
| 35 35 | 
             
                it "should create an input[:text] tag with a label when ':id => bar' with an error" do
         | 
| 36 | 
            -
                  @f.input(:text, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-text-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input class="form-control foo" id="bar" type="text"/><span class="help-block with-errors">input-text-error</span></div>'
         | 
| 36 | 
            +
                  @f.input(:text, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-text-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input aria-describedby="bar_error_message" aria-invalid="true" class="form-control foo" id="bar" type="text"/><span class="help-block with-errors">input-text-error</span></div>'
         | 
| 37 37 | 
             
                end
         | 
| 38 38 |  | 
| 39 39 | 
             
                describe 'from a Sequel model' do
         | 
| @@ -43,7 +43,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 43 43 |  | 
| 44 44 | 
             
                  it "should correctly handle an input[:text] tag from Sequel model with error" do
         | 
| 45 45 | 
             
                    @ac.errors.add(:name, 'name not valid')
         | 
| 46 | 
            -
                    @c.input(:name).to_s.must_equal '<div class="form-group has-error string"><label for="album_name">Name</label> <input class="form-control" id="album_name" maxlength="255" name="album[name]" type="text" value="c"/><span class="help-block with-errors">name not valid</span></div>'
         | 
| 46 | 
            +
                    @c.input(:name).to_s.must_equal '<div class="form-group has-error string"><label for="album_name">Name</label> <input aria-describedby="album_name_error_message" aria-invalid="true" class="form-control" id="album_name" maxlength="255" name="album[name]" type="text" value="c"/><span class="help-block with-errors">name not valid</span></div>'
         | 
| 47 47 | 
             
                  end
         | 
| 48 48 | 
             
                end
         | 
| 49 49 | 
             
              end
         | 
| @@ -66,7 +66,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 66 66 | 
             
                end
         | 
| 67 67 |  | 
| 68 68 | 
             
                it "should create an input[:password] tag with a label when ':id => bar' with an error" do
         | 
| 69 | 
            -
                  @f.input(:password, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-password-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input class="form-control foo" id="bar" type="password"/><span class="help-block with-errors">input-password-error</span></div>'
         | 
| 69 | 
            +
                  @f.input(:password, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-password-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input aria-describedby="bar_error_message" aria-invalid="true" class="form-control foo" id="bar" type="password"/><span class="help-block with-errors">input-password-error</span></div>'
         | 
| 70 70 | 
             
                end
         | 
| 71 71 | 
             
              end
         | 
| 72 72 |  | 
| @@ -88,7 +88,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 88 88 | 
             
                end
         | 
| 89 89 |  | 
| 90 90 | 
             
                it "should create an input[:email] tag with a label when ':id => bar' with an error" do
         | 
| 91 | 
            -
                  @f.input(:email, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-email-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input class="form-control foo" id="bar" type="email"/><span class="help-block with-errors">input-email-error</span></div>'
         | 
| 91 | 
            +
                  @f.input(:email, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-email-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input aria-describedby="bar_error_message" aria-invalid="true" class="form-control foo" id="bar" type="email"/><span class="help-block with-errors">input-email-error</span></div>'
         | 
| 92 92 | 
             
                end
         | 
| 93 93 | 
             
              end
         | 
| 94 94 |  | 
| @@ -110,7 +110,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 110 110 | 
             
                end
         | 
| 111 111 |  | 
| 112 112 | 
             
                it "should create an input[:file] tag with a label when ':id => bar' with an error" do
         | 
| 113 | 
            -
                  @f.input(:file, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-file-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input class="foo" id="bar" type="file"/><span class="help-block with-errors">input-file-error</span></div>'
         | 
| 113 | 
            +
                  @f.input(:file, :label=>"Name", :id=>'bar', :class=>'foo', :error=>'input-file-error').to_s.must_equal '<div class="form-group has-error"><label for="bar">Name</label> <input aria-describedby="bar_error_message" aria-invalid="true" class="foo" id="bar" type="file"/><span class="help-block with-errors">input-file-error</span></div>'
         | 
| 114 114 | 
             
                end
         | 
| 115 115 | 
             
              end
         | 
| 116 116 |  | 
| @@ -132,7 +132,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 132 132 | 
             
                end
         | 
| 133 133 |  | 
| 134 134 | 
             
                it "should create an input[:submit] tag without error message " do
         | 
| 135 | 
            -
                  @f.input(:submit, :value=>'Save', :id=>'foo', :error=>'error-message').to_s.must_equal '<input class="btn btn-default" id="foo" type="submit" value="Save"/>'
         | 
| 135 | 
            +
                  @f.input(:submit, :value=>'Save', :id=>'foo', :error=>'error-message').to_s.must_equal '<input aria-describedby="foo_error_message" aria-invalid="true" class="btn btn-default" id="foo" type="submit" value="Save"/>'
         | 
| 136 136 | 
             
                end
         | 
| 137 137 | 
             
              end
         | 
| 138 138 |  | 
| @@ -154,7 +154,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 154 154 | 
             
                end
         | 
| 155 155 |  | 
| 156 156 | 
             
                it "should create an input[:reset] tag without error message " do
         | 
| 157 | 
            -
                  @f.input(:reset, :value=>'Save', :id=>'foo', :error=>'error-message').to_s.must_equal '<input class="btn btn-default" id="foo" type="reset" value="Save"/>'
         | 
| 157 | 
            +
                  @f.input(:reset, :value=>'Save', :id=>'foo', :error=>'error-message').to_s.must_equal '<input aria-describedby="foo_error_message" aria-invalid="true" class="btn btn-default" id="foo" type="reset" value="Save"/>'
         | 
| 158 158 | 
             
                end
         | 
| 159 159 | 
             
              end
         | 
| 160 160 |  | 
| @@ -180,7 +180,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 180 180 | 
             
                end
         | 
| 181 181 |  | 
| 182 182 | 
             
                it "should create a textarea tag with .has-error and error message when ':error => is-a-string'" do
         | 
| 183 | 
            -
                  @f.input(:textarea, :error=>'input-textarea-error').to_s.must_equal '<div class="form-group has-error"><textarea class="form-control"></textarea><span class="help-block with-errors">input-textarea-error</span></div>'
         | 
| 183 | 
            +
                  @f.input(:textarea, :error=>'input-textarea-error').to_s.must_equal '<div class="form-group has-error"><textarea aria-invalid="true" class="form-control"></textarea><span class="help-block with-errors">input-textarea-error</span></div>'
         | 
| 184 184 | 
             
                end
         | 
| 185 185 |  | 
| 186 186 | 
             
                describe 'from a Sequel model' do
         | 
| @@ -190,7 +190,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 190 190 |  | 
| 191 191 | 
             
                  it "should correctly handle an input[:text] tag from Sequel model with error" do
         | 
| 192 192 | 
             
                    @ac.errors.add(:name, 'name not valid')
         | 
| 193 | 
            -
                    @c.input(:name, :as=>:textarea).to_s.must_equal '<div class="form-group has-error string"><label for="album_name">Name</label> <textarea class="form-control" id="album_name" maxlength="255" name="album[name]">c</textarea><span class="help-block with-errors">name not valid</span></div>'
         | 
| 193 | 
            +
                    @c.input(:name, :as=>:textarea).to_s.must_equal '<div class="form-group has-error string"><label for="album_name">Name</label> <textarea aria-describedby="album_name_error_message" aria-invalid="true" class="form-control" id="album_name" maxlength="255" name="album[name]">c</textarea><span class="help-block with-errors">name not valid</span></div>'
         | 
| 194 194 | 
             
                  end
         | 
| 195 195 | 
             
                end
         | 
| 196 196 | 
             
              end
         | 
| @@ -225,7 +225,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 225 225 | 
             
                end
         | 
| 226 226 |  | 
| 227 227 | 
             
                it "should create a select tag with .has-error and error message when ':error => is-a-string'" do
         | 
| 228 | 
            -
                  @f.input(:select, :error=>'input-select-error').to_s.must_equal '<div class="form-group has-error"><select class="form-control"></select><span class="help-block with-errors">input-select-error</span></div>'
         | 
| 228 | 
            +
                  @f.input(:select, :error=>'input-select-error').to_s.must_equal '<div class="form-group has-error"><select aria-invalid="true" class="form-control"></select><span class="help-block with-errors">input-select-error</span></div>'
         | 
| 229 229 | 
             
                end
         | 
| 230 230 |  | 
| 231 231 | 
             
                it "should correctly handle a Sequel model output :as => :select" do
         | 
| @@ -234,7 +234,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 234 234 |  | 
| 235 235 | 
             
                it "should correctly handle a Sequel model output :as => :select with error" do
         | 
| 236 236 | 
             
                  @ac.errors.add(:artist, 'error message')
         | 
| 237 | 
            -
                  @c.input(:artist, :as=>:select).to_s.must_equal '<div class="form-group has-error many_to_one"><label for="album_artist_id">Artist</label> <select class="form-control" id="album_artist_id" name="album[artist_id]"><option value=""></option><option value="1">a</option><option selected="selected" value="2">d</option></select><span class="help-block with-errors">error message</span></div>'
         | 
| 237 | 
            +
                  @c.input(:artist, :as=>:select).to_s.must_equal '<div class="form-group has-error many_to_one"><label for="album_artist_id">Artist</label> <select aria-describedby="album_artist_id_error_message" aria-invalid="true" class="form-control" id="album_artist_id" name="album[artist_id]"><option value=""></option><option value="1">a</option><option selected="selected" value="2">d</option></select><span class="help-block with-errors">error message</span></div>'
         | 
| 238 238 | 
             
                end
         | 
| 239 239 |  | 
| 240 240 | 
             
                describe 'from a Sequel model' do
         | 
| @@ -244,7 +244,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 244 244 |  | 
| 245 245 | 
             
                  it "should correctly handle a boolean attribute from a Sequel model with an error" do
         | 
| 246 246 | 
             
                    @ac.errors.add(:gold, 'error message')
         | 
| 247 | 
            -
                    @c.input(:gold).to_s.must_equal '<div class="boolean form-group has-error"><label for="album_gold">Gold</label> <select class="form-control" id="album_gold" name="album[gold]"><option value=""></option><option selected="selected" value="t">True</option><option value="f">False</option></select><span class="help-block with-errors">error message</span></div>'
         | 
| 247 | 
            +
                    @c.input(:gold).to_s.must_equal '<div class="boolean form-group has-error"><label for="album_gold">Gold</label> <select aria-describedby="album_gold_error_message" aria-invalid="true" class="form-control" id="album_gold" name="album[gold]"><option value=""></option><option selected="selected" value="t">True</option><option value="f">False</option></select><span class="help-block with-errors">error message</span></div>'
         | 
| 248 248 | 
             
                  end
         | 
| 249 249 | 
             
                end
         | 
| 250 250 | 
             
              end
         | 
| @@ -267,7 +267,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 267 267 | 
             
                end
         | 
| 268 268 |  | 
| 269 269 | 
             
                it "should create an input[:checkbox] tag with a label when ':id => bar' with an error" do
         | 
| 270 | 
            -
                  @f.input(:checkbox, :label=>"Gold", :id=>'bar', :class=>'foo', :error=>'input-checkbox-error').to_s.must_equal '<div class="has-error"><div class="checkbox"><label for="bar"><input class="foo" id="bar" type="checkbox"/> Gold</label></div><span class="help-block with-errors">input-checkbox-error</span></div>'
         | 
| 270 | 
            +
                  @f.input(:checkbox, :label=>"Gold", :id=>'bar', :class=>'foo', :error=>'input-checkbox-error').to_s.must_equal '<div class="has-error"><div class="checkbox"><label for="bar"><input aria-describedby="bar_error_message" aria-invalid="true" class="foo" id="bar" type="checkbox"/> Gold</label></div><span class="help-block with-errors">input-checkbox-error</span></div>'
         | 
| 271 271 | 
             
                end
         | 
| 272 272 |  | 
| 273 273 | 
             
                describe 'from a Sequel model' do
         | 
| @@ -277,7 +277,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 277 277 |  | 
| 278 278 | 
             
                  it "should correctly handle a boolean attribute ':as=>:checkbox' with an error" do
         | 
| 279 279 | 
             
                    @ac.errors.add(:gold, 'error message')
         | 
| 280 | 
            -
                    @c.input(:gold, :as=>:checkbox).to_s.must_equal '<div class="boolean has-error"><div class="checkbox"><label for="album_gold"><input id="album_gold_hidden" name="album[gold]" type="hidden" value="f"/><input checked="checked" id="album_gold" name="album[gold]" type="checkbox" value="t"/> Gold</label></div><span class="help-block with-errors">error message</span></div>'
         | 
| 280 | 
            +
                    @c.input(:gold, :as=>:checkbox).to_s.must_equal '<div class="boolean has-error"><div class="checkbox"><label for="album_gold"><input id="album_gold_hidden" name="album[gold]" type="hidden" value="f"/><input aria-describedby="album_gold_error_message" aria-invalid="true" checked="checked" id="album_gold" name="album[gold]" type="checkbox" value="t"/> Gold</label></div><span class="help-block with-errors">error message</span></div>'
         | 
| 281 281 | 
             
                  end
         | 
| 282 282 | 
             
                end
         | 
| 283 283 | 
             
              end
         | 
| @@ -301,7 +301,7 @@ describe "Forme Bootstrap3 (BS3) forms" do | |
| 301 301 | 
             
                end
         | 
| 302 302 |  | 
| 303 303 | 
             
                it "should create an input[:radio] tag with a label when ':id => bar' with an error" do
         | 
| 304 | 
            -
                  @f.input(:radio, :label=>"Gold", :id=>'bar', :class=>'foo', :error=>'input-radio-error').to_s.must_equal '<div class="has-error"><div class="radio"><label for="bar"><input class="foo" id="bar" type="radio"/> Gold</label></div><span class="help-block with-errors">input-radio-error</span></div>'
         | 
| 304 | 
            +
                  @f.input(:radio, :label=>"Gold", :id=>'bar', :class=>'foo', :error=>'input-radio-error').to_s.must_equal '<div class="has-error"><div class="radio"><label for="bar"><input aria-describedby="bar_error_message" aria-invalid="true" class="foo" id="bar" type="radio"/> Gold</label></div><span class="help-block with-errors">input-radio-error</span></div>'
         | 
| 305 305 | 
             
                end
         | 
| 306 306 |  | 
| 307 307 | 
             
                describe 'from a Sequel model' do
         | 
| @@ -1,6 +1,6 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 1 | 
            +
            require_relative 'spec_helper'
         | 
| 2 | 
            +
            require_relative 'sequel_helper'
         | 
| 3 | 
            +
            require_relative '../lib/forme/bs3'
         | 
| 4 4 |  | 
| 5 5 | 
             
            describe "Forme Sequel::Model BS3 forms" do
         | 
| 6 6 | 
             
              before do
         | 
| @@ -263,7 +263,7 @@ describe "Forme Sequel::Model BS3 forms" do | |
| 263 263 |  | 
| 264 264 | 
             
              it "should handle an error message on the underlying column for pg_array_to_many associations" do
         | 
| 265 265 | 
             
                @ab.errors.add(:atag_ids, 'tis not valid')
         | 
| 266 | 
            -
                @b.input(:atags).to_s.must_equal '<div class="form-group has-error pg_array_to_many"><label for="album_atag_ids">Atags</label> <select class="form-control" id="album_atag_ids" multiple="multiple" name="album[atag_ids][]"><option selected="selected" value="1">s</option><option selected="selected" value="2">t</option><option value="3">u</option></select><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 266 | 
            +
                @b.input(:atags).to_s.must_equal '<div class="form-group has-error pg_array_to_many"><label for="album_atag_ids">Atags</label> <select aria-describedby="album_atag_ids_error_message" aria-invalid="true" class="form-control" id="album_atag_ids" multiple="multiple" name="album[atag_ids][]"><option selected="selected" value="1">s</option><option selected="selected" value="2">t</option><option value="3">u</option></select><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 267 267 | 
             
                @b.input(:atags, :as=>:checkbox).to_s.must_equal '<div class="pg_array_to_many checkboxset has-error"><label>Atags</label><div class="checkbox"><label class="option" for="album_atag_ids_1"><input checked="checked" id="album_atag_ids_1" name="album[atag_ids][]" type="checkbox" value="1"/> s</label></div><div class="checkbox"><label class="option" for="album_atag_ids_2"><input checked="checked" id="album_atag_ids_2" name="album[atag_ids][]" type="checkbox" value="2"/> t</label></div><div class="checkbox"><label class="option" for="album_atag_ids_3"><input id="album_atag_ids_3" name="album[atag_ids][]" type="checkbox" value="3"/> u</label></div><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 268 268 | 
             
                @b.input(:atags, :as=>:checkbox,:wrapper=>:li).to_s.must_equal '<li class="pg_array_to_many checkboxset has-error"><label>Atags</label><div class="checkbox"><label class="option" for="album_atag_ids_1"><input checked="checked" id="album_atag_ids_1" name="album[atag_ids][]" type="checkbox" value="1"/> s</label></div><div class="checkbox"><label class="option" for="album_atag_ids_2"><input checked="checked" id="album_atag_ids_2" name="album[atag_ids][]" type="checkbox" value="2"/> t</label></div><div class="checkbox"><label class="option" for="album_atag_ids_3"><input id="album_atag_ids_3" name="album[atag_ids][]" type="checkbox" value="3"/> u</label></div><span class="help-block with-errors">tis not valid</span></li>'
         | 
| 269 269 | 
             
              end
         | 
| @@ -308,7 +308,7 @@ describe "Forme Sequel::Model BS3 forms" do | |
| 308 308 |  | 
| 309 309 | 
             
              it "should handle errors on methods not backed by columns" do
         | 
| 310 310 | 
             
                @ab.errors.add(:artist_name, 'foo')
         | 
| 311 | 
            -
                @b.input(:artist_name).to_s.must_equal '<div class="form-group has-error"><label for="album_artist_name">Artist name</label> <input class="form-control" id="album_artist_name" name="album[artist_name]" type="text" value="a"/><span class="help-block with-errors">foo</span></div>'
         | 
| 311 | 
            +
                @b.input(:artist_name).to_s.must_equal '<div class="form-group has-error"><label for="album_artist_name">Artist name</label> <input aria-describedby="album_artist_name_error_message" aria-invalid="true" class="form-control" id="album_artist_name" name="album[artist_name]" type="text" value="a"/><span class="help-block with-errors">foo</span></div>'
         | 
| 312 312 | 
             
              end
         | 
| 313 313 |  | 
| 314 314 | 
             
              it "should respect a :type option with a schema type as the input type for methods not backed by columns" do
         | 
| @@ -323,12 +323,12 @@ describe "Forme Sequel::Model BS3 forms" do | |
| 323 323 |  | 
| 324 324 | 
             
              it "should correctly show an error message if there is one" do
         | 
| 325 325 | 
             
                @ab.errors.add(:name, 'tis not valid')
         | 
| 326 | 
            -
                @b.input(:name).to_s.must_equal '<div class="form-group has-error string"><label for="album_name">Name</label> <input class="form-control" id="album_name" maxlength="255" name="album[name]" type="text" value="b"/><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 326 | 
            +
                @b.input(:name).to_s.must_equal '<div class="form-group has-error string"><label for="album_name">Name</label> <input aria-describedby="album_name_error_message" aria-invalid="true" class="form-control" id="album_name" maxlength="255" name="album[name]" type="text" value="b"/><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 327 327 | 
             
              end
         | 
| 328 328 |  | 
| 329 329 | 
             
              it "should correctly show an error message for many_to_one associations if there is one" do
         | 
| 330 330 | 
             
                @ab.errors.add(:artist_id, 'tis not valid')
         | 
| 331 | 
            -
                @b.input(:artist).to_s.must_equal '<div class="form-group has-error many_to_one"><label for="album_artist_id">Artist</label> <select class="form-control" id="album_artist_id" name="album[artist_id]"><option value=""></option><option selected="selected" value="1">a</option><option value="2">d</option></select><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 331 | 
            +
                @b.input(:artist).to_s.must_equal '<div class="form-group has-error many_to_one"><label for="album_artist_id">Artist</label> <select aria-describedby="album_artist_id_error_message" aria-invalid="true" class="form-control" id="album_artist_id" name="album[artist_id]"><option value=""></option><option selected="selected" value="1">a</option><option value="2">d</option></select><span class="help-block with-errors">tis not valid</span></div>'
         | 
| 332 332 | 
             
              end
         | 
| 333 333 |  | 
| 334 334 | 
             
              it "should raise an error for unhandled associations" do
         | 
| @@ -390,19 +390,19 @@ describe "Forme Sequel::Model BS3 forms" do | |
| 390 390 | 
             
              end
         | 
| 391 391 |  | 
| 392 392 | 
             
              it "should use allow nested forms with one_to_many associations" do
         | 
| 393 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks){f.input(:name)}}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="m"/></div></fieldset>< | 
| 393 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks){f.input(:name)}}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/><input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="m"/></div></fieldset><fieldset class="inputs"><legend>Track #2</legend><div class="form-group string"><label for="album_tracks_attributes_1_name">Name</label> <input class="form-control" id="album_tracks_attributes_1_name" maxlength="255" name="album[tracks_attributes][1][name]" type="text" value="n"/></div></fieldset></form>'
         | 
| 394 394 | 
             
              end
         | 
| 395 395 |  | 
| 396 396 | 
             
              it "should support :obj option for *_to_many associations" do
         | 
| 397 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks, :obj=>[Track.new(:name=>'x'), Track.load(:id=>5, :name=>'y')]){f.input(:name)}}.to_s.must_equal '<form class="forme album" method="post"><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="x"/></div></fieldset>< | 
| 397 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks, :obj=>[Track.new(:name=>'x'), Track.load(:id=>5, :name=>'y')]){f.input(:name)}}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="5"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="x"/></div></fieldset><fieldset class="inputs"><legend>Track #2</legend><div class="form-group string"><label for="album_tracks_attributes_1_name">Name</label> <input class="form-control" id="album_tracks_attributes_1_name" maxlength="255" name="album[tracks_attributes][1][name]" type="text" value="y"/></div></fieldset></form>'
         | 
| 398 398 | 
             
              end
         | 
| 399 399 |  | 
| 400 400 | 
             
              it "should auto number legends when using subform with inputs for *_to_many associations" do
         | 
| 401 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks, :inputs=>[:name])}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="m"/></div></fieldset>< | 
| 401 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks, :inputs=>[:name])}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/><input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="m"/></div></fieldset><fieldset class="inputs"><legend>Track #2</legend><div class="form-group string"><label for="album_tracks_attributes_1_name">Name</label> <input class="form-control" id="album_tracks_attributes_1_name" maxlength="255" name="album[tracks_attributes][1][name]" type="text" value="n"/></div></fieldset></form>'
         | 
| 402 402 | 
             
              end
         | 
| 403 403 |  | 
| 404 404 | 
             
              it "should support callable :legend option when using subform with inputs for *_to_many associations" do
         | 
| 405 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks, :inputs=>[:name], :legend=>proc{|o, i| "Track #{i} (#{o.name})"})}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Track 0 (m)</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="m"/></div></fieldset>< | 
| 405 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tracks, :inputs=>[:name], :legend=>proc{|o, i| "Track #{i} (#{o.name})"})}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/><input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Track 0 (m)</legend><div class="form-group string"><label for="album_tracks_attributes_0_name">Name</label> <input class="form-control" id="album_tracks_attributes_0_name" maxlength="255" name="album[tracks_attributes][0][name]" type="text" value="m"/></div></fieldset><fieldset class="inputs"><legend>Track 1 (n)</legend><div class="form-group string"><label for="album_tracks_attributes_1_name">Name</label> <input class="form-control" id="album_tracks_attributes_1_name" maxlength="255" name="album[tracks_attributes][1][name]" type="text" value="n"/></div></fieldset></form>'
         | 
| 406 406 | 
             
              end
         | 
| 407 407 |  | 
| 408 408 | 
             
              it "should not add hidden primary key field for nested forms with one_to_many associations with new objects" do
         | 
| @@ -411,7 +411,7 @@ describe "Forme Sequel::Model BS3 forms" do | |
| 411 411 | 
             
              end
         | 
| 412 412 |  | 
| 413 413 | 
             
              it "should use allow nested forms with many_to_many associations" do
         | 
| 414 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tags){f.input(:name)}}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tags_attributes_0_id" name="album[tags_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Tag #1</legend><div class="form-group string"><label for="album_tags_attributes_0_name">Name</label> <input class="form-control" id="album_tags_attributes_0_name" maxlength="255" name="album[tags_attributes][0][name]" type="text" value="s"/></div></fieldset>< | 
| 414 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:tags){f.input(:name)}}.to_s.must_equal '<form class="forme album" method="post"><input id="album_tags_attributes_0_id" name="album[tags_attributes][0][id]" type="hidden" value="1"/><input id="album_tags_attributes_1_id" name="album[tags_attributes][1][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Tag #1</legend><div class="form-group string"><label for="album_tags_attributes_0_name">Name</label> <input class="form-control" id="album_tags_attributes_0_name" maxlength="255" name="album[tags_attributes][0][name]" type="text" value="s"/></div></fieldset><fieldset class="inputs"><legend>Tag #2</legend><div class="form-group string"><label for="album_tags_attributes_1_name">Name</label> <input class="form-control" id="album_tags_attributes_1_name" maxlength="255" name="album[tags_attributes][1][name]" type="text" value="t"/></div></fieldset></form>'
         | 
| 415 415 | 
             
              end
         | 
| 416 416 |  | 
| 417 417 | 
             
              it "should not add hidden primary key field for nested forms with many_to_many associations with new objects" do
         | 
| @@ -420,23 +420,23 @@ describe "Forme Sequel::Model BS3 forms" do | |
| 420 420 | 
             
              end
         | 
| 421 421 |  | 
| 422 422 | 
             
              it "should handle multiple nested levels" do
         | 
| 423 | 
            -
                Forme.form(Artist[1], {},{:config=>:bs3}){|f| f.subform(:albums){f.input(:name); f.subform(:tracks){f.input(:name)}}}.to_s.to_s.must_equal '<form class="forme artist" method="post"><input id="artist_albums_attributes_0_id" name="artist[albums_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Album #1</legend><div class="form-group string"><label for="artist_albums_attributes_0_name">Name</label> <input class="form-control" id="artist_albums_attributes_0_name" maxlength="255" name="artist[albums_attributes][0][name]" type="text" value="b"/></div><input id="artist_albums_attributes_0_tracks_attributes_0_id" name="artist[albums_attributes][0][tracks_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="artist_albums_attributes_0_tracks_attributes_0_name">Name</label> <input class="form-control" id="artist_albums_attributes_0_tracks_attributes_0_name" maxlength="255" name="artist[albums_attributes][0][tracks_attributes][0][name]" type="text" value="m"/></div></fieldset>< | 
| 423 | 
            +
                Forme.form(Artist[1], {},{:config=>:bs3}){|f| f.subform(:albums){f.input(:name); f.subform(:tracks){f.input(:name)}}}.to_s.to_s.must_equal '<form class="forme artist" method="post"><input id="artist_albums_attributes_0_id" name="artist[albums_attributes][0][id]" type="hidden" value="1"/><fieldset class="inputs"><legend>Album #1</legend><div class="form-group string"><label for="artist_albums_attributes_0_name">Name</label> <input class="form-control" id="artist_albums_attributes_0_name" maxlength="255" name="artist[albums_attributes][0][name]" type="text" value="b"/></div><input id="artist_albums_attributes_0_tracks_attributes_0_id" name="artist[albums_attributes][0][tracks_attributes][0][id]" type="hidden" value="1"/><input id="artist_albums_attributes_0_tracks_attributes_1_id" name="artist[albums_attributes][0][tracks_attributes][1][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Track #1</legend><div class="form-group string"><label for="artist_albums_attributes_0_tracks_attributes_0_name">Name</label> <input class="form-control" id="artist_albums_attributes_0_tracks_attributes_0_name" maxlength="255" name="artist[albums_attributes][0][tracks_attributes][0][name]" type="text" value="m"/></div></fieldset><fieldset class="inputs"><legend>Track #2</legend><div class="form-group string"><label for="artist_albums_attributes_0_tracks_attributes_1_name">Name</label> <input class="form-control" id="artist_albums_attributes_0_tracks_attributes_1_name" maxlength="255" name="artist[albums_attributes][0][tracks_attributes][1][name]" type="text" value="n"/></div></fieldset></fieldset></form>'
         | 
| 424 424 | 
             
              end
         | 
| 425 425 |  | 
| 426 426 | 
             
              it "should have #subform :grid option create a grid" do
         | 
| 427 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true)}.to_s.must_equal '<form class="forme album" method="post">< | 
| 427 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true)}.to_s.must_equal '<form class="forme album" method="post"><input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1"/><table><caption>Artist</caption><thead><tr><th>Name</th></tr></thead><tbody><tr><td class="string"><input class="form-control" id="album_artist_attributes_name" maxlength="255" name="album[artist_attributes][name]" type="text" value="a"/></td></tr></tbody></table></form>'
         | 
| 428 428 | 
             
              end
         | 
| 429 429 |  | 
| 430 430 | 
             
              it "should have #subform :grid option respect :inputs_opts option to pass options to inputs" do
         | 
| 431 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true, :inputs_opts=>{:attr=>{:class=>'foo'}})}.to_s.must_equal '<form class="forme album" method="post">< | 
| 431 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true, :inputs_opts=>{:attr=>{:class=>'foo'}})}.to_s.must_equal '<form class="forme album" method="post"><input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1"/><table class="foo"><caption>Artist</caption><thead><tr><th>Name</th></tr></thead><tbody><tr><td class="string"><input class="form-control" id="album_artist_attributes_name" maxlength="255" name="album[artist_attributes][name]" type="text" value="a"/></td></tr></tbody></table></form>'
         | 
| 432 432 | 
             
              end
         | 
| 433 433 |  | 
| 434 434 | 
             
              it "should have #subform :grid option handle :legend and :labels options" do
         | 
| 435 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true, :legend=>'Foo', :labels=>%w'Bar')}.to_s.must_equal '<form class="forme album" method="post">< | 
| 435 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true, :legend=>'Foo', :labels=>%w'Bar')}.to_s.must_equal '<form class="forme album" method="post"><input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1"/><table><caption>Foo</caption><thead><tr><th>Bar</th></tr></thead><tbody><tr><td class="string"><input class="form-control" id="album_artist_attributes_name" maxlength="255" name="album[artist_attributes][name]" type="text" value="a"/></td></tr></tbody></table></form>'
         | 
| 436 436 | 
             
              end
         | 
| 437 437 |  | 
| 438 438 | 
             
              it "should have #subform :grid option handle :legend and :labels nil values" do
         | 
| 439 | 
            -
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true, :legend=>nil, :labels=>nil)}.to_s.must_equal '<form class="forme album" method="post">< | 
| 439 | 
            +
                Forme.form(@ab, {},{:config=>:bs3}){|f| f.subform(:artist, :inputs=>[:name], :grid=>true, :legend=>nil, :labels=>nil)}.to_s.must_equal '<form class="forme album" method="post"><input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1"/><table><tbody><tr><td class="string"><input class="form-control" id="album_artist_attributes_name" maxlength="255" name="album[artist_attributes][name]" type="text" value="a"/></td></tr></tbody></table></form>'
         | 
| 440 440 | 
             
              end
         | 
| 441 441 |  | 
| 442 442 | 
             
              it "should have #subform :grid option handle :skip_primary_key option" do
         |