Changeset 1006

Show
Ignore:
Timestamp:
11/15/07 20:22:02 (1 year ago)
Author:
iv..@gweezlebur.com
Message:

Adds selects, options, optgroups, fieldsets, etc., plus fully documents and specs them. Also, labels are explicit not implicit (refs #288). Thanks to Skiz and pimpmaster and everyone else who worked on this. Closes #300

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/merb_helpers/lib/form_helpers.rb

    r1002 r1006  
    1 module Merb 
     1module Merb #:nodoc: 
     2   
     3  # Merb helpers include several helpers used for simplifying view creation. 
     4  # The available helpers currently include form tag helpers for both resource based and generic HTML form tag creation 
    25  module Helpers 
     6    # Provides a number of methods for creating form tags which may be used either with or without the presence of ORM specific models. 
     7    # There are two types of form helpers: those that specifically work with model attributes and those that don't. 
     8          # This helper deals with both model attributes and generic form tags. Model attributes generally end in "_control" such as +text_control+, 
     9          # and generic tags end with "_field", such as +text_field+ 
     10    # 
     11          # The core method of this helper, +form_for+, gives you the ability to create a form for a resource. 
     12    # For example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it: 
     13    # 
     14    #     <% form_for :person, :action => url(:people) do %> 
     15    #       <%= text_control :first_name, :label => 'First Name' %> 
     16    #       <%= text_control :last_name,  :label => 'Last Name' %> 
     17    #       <%= submit_button 'Create' %> 
     18    #     <% end %> 
     19    # 
     20    # The HTML generated for this would be: 
     21    # 
     22    #     <form action="/people/create" method="post"> 
     23    #       <input id="person_first_name" name="person[first_name]" size="30" type="text" /> 
     24    #       <input id="person_last_name" name="person[last_name]" size="30" type="text" /> 
     25    #       <input name="commit" type="submit" value="Create" /> 
     26    #     </form> 
     27    # 
     28          # You may also create a normal form using form_tag 
     29    #     <% form_tag({url(:controller => "foo", :action => "bar", :id => 1)} do %> 
     30    #       <%= text_field :name => 'first_name', :label => 'First Name' %> 
     31    #       <%= submit_button 'Create' %> 
     32    #     <% end %> 
     33    # 
     34    # The HTML generated for this would be: 
     35    # 
     36    #     <form action="/foo/bar/1" method="post"> 
     37    #       <input id="first_name" name="first_name" size="30" type="text" /> 
     38    #       <input name="commit" type="submit" value="Create" /> 
     39    #     </form> 
    340    module Form 
    441     
     42      # Provides a HTML formatted display of resource errors in an unordered list with a h2 form submission error 
     43      # ==== Options 
     44      # +html_class+:: Set for custom error div class default is <tt>submittal_failed<tt> 
     45      # 
     46      # ==== Example 
     47      # 
     48      #   <%= error_messages_for :person %>       
    549      def error_messages_for(obj, error_li = nil, html_class='submittal_failed') 
    650        return "" if obj.errors.empty? 
     
    1862      end 
    1963       
    20       def obj_from_ivar_or_sym(obj) 
     64      def obj_from_ivar_or_sym(obj) #:nodoc: 
    2165        obj.is_a?(Symbol) ? instance_variable_get("@#{obj}") : obj 
    2266      end 
    23        
    24       def tag(tag_name, contents, attrs = {}) 
    25         open_tag(tag_name, attrs) + contents + "</#{tag_name}>" 
    26       end 
    27        
     67 
     68      # Creates a generic HTML tag  
     69      def tag(tag_name, contents, attrs = {}) #:nodoc: 
     70        open_tag(tag_name, attrs) + contents.to_s + "</#{tag_name}>" 
     71      end 
     72 
     73      # Generates a form tag, which accepts a block that is not directly based on resource attributes 
     74      #  
     75      #     <% form_tag({url(:controller => "foo", :action => "bar", :id => 1)} do %> 
     76      #       <%= text_field :name => 'first_name', :label => 'First Name' %> 
     77      #       <%= submit_button 'Create' %> 
     78      #     <% end %> 
     79      # 
     80      # The HTML generated for this would be: 
     81      # 
     82      #     <form action="/foo/bar/1" method="post"> 
     83      #       <input id="first_name" name="first_name" size="30" type="text" /> 
     84      #       <input name="commit" type="submit" value="Create" /> 
     85      #     </form> 
    2886      def form_tag(attrs = {}, &block) 
    2987        attrs.merge!( :enctype => "multipart/form-data" ) if attrs.delete(:multipart) 
     
    3593      end 
    3694       
     95      # Generates a resource specific form tag which accepts a block, this also provides automatic resource routing. 
     96      #     <% form_for :person, :action => url(:people) do %> 
     97      #       <%= text_control :first_name, :label => 'First Name' %> 
     98      #       <%= text_control :last_name,  :label => 'Last Name' %> 
     99      #       <%= submit_button 'Create' %> 
     100      #     <% end %> 
     101      # 
     102      # The HTML generated for this would be: 
     103      # 
     104      #     <form action="/people/create" method="post"> 
     105      #       <input id="person_first_name" name="person[first_name]" size="30" type="text" /> 
     106      #       <input id="person_last_name" name="person[last_name]" size="30" type="text" /> 
     107      #       <input name="commit" type="submit" value="Create" /> 
     108      #     </form> 
    37109      def form_for(obj, attrs={}, &block) 
    38110        obj = obj_from_ivar_or_sym(obj) 
     
    44116      end 
    45117       
     118      # Creates a scope around a specific resource object like form_for, but doesnt create the form tags themselves. 
     119      # This makes fields_for suitable for specifying additional resource objects in the same form.  
     120      # 
     121      # ==== Examples 
     122      # 
     123      #     <% form_for :person, :action => url(:people) do %> 
     124      #       <%= text_control :first_name, :label => 'First Name' %> 
     125      #       <%= text_control :last_name,  :label => 'Last Name' %> 
     126      #       <% fields_for :permission do %> 
     127      #         <%= checkbox_control :is_admin, :label => 'Administrator' %> 
     128      #       <% end %> 
     129      #       <%= submit_button 'Create' %> 
     130      #     <% end %> 
    46131      def fields_for(obj, attrs=nil, &block) 
    47132        obj = obj_from_ivar_or_sym(obj) 
     
    55140      end 
    56141       
    57       def control_name(col) 
     142      def control_name(col) #:nodoc: 
    58143        "#{@_object_name}[#{col}]" 
    59144      end 
    60145       
    61       def control_value(col) 
     146      def control_id(col) #:nodoc: 
     147        "#{@_object_name}_#{col}" 
     148      end 
     149       
     150      def control_value(col) #:nodoc: 
    62151        @_obj.send(col) 
    63152      end 
    64153       
    65       def control_name_value(col, attrs) 
     154      def control_name_value(col, attrs) #:nodoc: 
    66155        {:name => control_name(col), :value => control_value(col)}.merge(attrs) 
    67156      end 
    68157       
     158      # Provides a HTML text input tag based on a resource attribute. 
     159      # 
     160      # ==== Example 
     161      #     <% form_for :person, :action => url(:people) do %> 
     162      #       <%= text_control :first_name, :label => 'First Name' %> 
     163      #     <% end %> 
    69164      def text_control(col, attrs = {}) 
    70165        errorify_field(attrs, col) 
     
    72167      end 
    73168       
     169      # Provides a generic HTML text input tag 
     170      # Provides a HTML text input tag based on a resource attribute. 
     171      # 
     172      # ==== Example 
     173      #   <%= text_field :fav_color, :label => 'Your Favorite Color' %> 
     174      #   => <label for="fav_color">Your Favorite Color</label><input type="text" name="fav_color" id="fav_color"/> 
    74175      def text_field(attrs = {}) 
    75176        attrs.merge!(:type => "text") 
     
    77178      end 
    78179       
     180      # Provides a HTML password input based on a resource attribute. 
     181      # This is generally used within a resource block such as +form_for+ 
     182      # ==== Example 
     183      #    <%= password_control :password, :label => 'New Password' %> 
    79184      def password_control(col, attrs = {}) 
    80185        attrs.merge!(:name => control_name(col)) 
     
    91196      # translate column values from the db to boolean 
    92197      # nil, false, 0 and '0' are false. All others are true 
    93       def col_val_to_bool(val) 
     198      def col_val_to_bool(val) #:nodoc: 
    94199        !(val == "0" || val == 0 || !val) 
    95200      end 
     
    111216      end 
    112217       
     218      # Returns a hidden input tag tailored for accessing a specified attribute (identified by +col+) on an object 
     219      # resource within a +form_for+ resource block. Additional options on the input tag can be passed as a 
     220      # hash with +attrs+. These options will be tagged onto the HTML as an HTML element attribute as in the example 
     221      # shown. 
     222      # 
     223      # ==== Examples 
     224      # 
     225      #   <%= hidden_control :identifier %> 
     226      #   # => <input id="person_identifier" name="person[identifier]" type="hidden" value="#{@person.identifier}" /> 
     227      # 
    113228      def hidden_control(col, attrs = {}) 
    114229        attrs.delete(:label) 
     
    165280        tag("label", name.to_s + contents, attrs) 
    166281      end 
    167        
     282 
     283      def select_field(attrs = {}) 
     284        collection = attrs.delete(:collection) 
     285        option_attrs = { 
     286          :prompt => attrs.delete(:prompt), 
     287          :selected => attrs.delete(:selected), 
     288          :include_blank => attrs.delete(:include_blank), 
     289          :text_method => attrs.delete(:text_method), 
     290          :value_method => attrs.delete(:value_method) 
     291        } 
     292        optional_label(attrs) { open_tag('select', attrs) + options_for_select(collection, option_attrs) + "</select>"} 
     293      end 
     294       
     295      def select_control(col, attrs = {}) 
     296        attrs.merge!(:name => attrs[:name] || control_name(col)) 
     297        attrs.merge!(:id   => attrs[:id]   || control_id(col)) 
     298        errorify_field(attrs, col) 
     299        optional_label(attrs) { select_field(attrs) } 
     300      end 
     301       
     302      # Accepts a collection (hash, array, enumerable, your type) and returns a string of option tags.  
     303      # Given a collection where the elements respond to first and last (such as a two-element array),  
     304      # the "lasts" serve as option values and the "firsts" as option text. Hashes are turned into 
     305      # this form automatically, so the keys become "firsts" and values become lasts. If selected is 
     306      # specified, the matching "last" or element will get the selected option-tag. Selected may also 
     307      # be an array of values to be selected when using a multiple select. 
     308      # 
     309      # ==== Examples 
     310      #   <%= options_for_select( [['apple','Apple Pie'],['orange','Orange Juice']], :selected => 'orange' ) 
     311      #   => <option value="apple">Apple Pie</option><option value="orange" selected="selected">Orange Juice</option> 
     312      # 
     313      #   <%= options_for_select( [['apple','Apple Pie'],['orange','Orange Juice']], :selected => ['orange','apple'], :prompt => 'Select One' ) 
     314      #   => <option value="">Select One</option><option value="apple" selected="selected">Apple Pie</option><option value="orange" selected="selected">Orange Juice</option> 
     315      # 
     316      # ==== Options 
     317      # +selected+:: The value of a selected object, may be either a string or an array. 
     318      # +prompt+:: Adds an addtional option tag with the provided string with no value. 
     319      # +include_blank+:: Adds an additional blank option tag with no value. 
     320      def options_for_select(collection, attrs = {}) 
     321        prompt     = attrs.delete(:prompt) 
     322        blank      = attrs.delete(:include_blank) 
     323        selected   = attrs.delete(:selected) 
     324        returning '' do |ret| 
     325          ret << tag('option', prompt, :value => '') if prompt 
     326          ret << tag("option", '', :value => '') if blank 
     327          unless collection.blank? 
     328            if collection.is_a?(Hash) 
     329              collection.each do |label,group| 
     330                ret << open_tag("optgroup", :label => label.to_s.titleize) + options_for_select(group, :selected => selected) + "</optgroup>" 
     331              end 
     332            else 
     333              collection.each do |value,text| 
     334                options = selected.to_a.include?(value) ? {:selected => 'selected'} : {} 
     335                ret << tag( 'option', text, {:value => value}.merge(options) ) 
     336              end 
     337            end 
     338          end 
     339        end 
     340      end 
     341 
     342      # Returns a string of option tags that have been compiled by iterating over the collection and 
     343      # assigning the the result of a call to the value_method as the option value and the text_method 
     344      # as the option text. If selected_value is specified, the element returning a match on 
     345      # the value_method option will get the selected option tag. 
     346      # 
     347      # This method also also supports the automatic generation of optgroup tags by using a hash. 
     348      # ==== Examples 
     349      # If we had a collection of people within a @project object, and want to use 'id' as the value, and 'name' 
     350      # as the option content we could do something similar to this; 
     351      # 
     352      #   <%= options_from_collection_for_select(@project.people, :text_method => "id", :value_method => "name") %> 
     353      #   The iteration of the collection would create options in this manner; 
     354      #   =>  <option value="#{person.id}">#{person.name}</option> 
     355      # 
     356      #   <% @people = Person.find(:all).group_by( &:state ) 
     357      #   <%= options_for_select(@people, :text_method => 'full_name', :value_method => 'id', :selected => 3) %> 
     358      #   => <optgroup label="Washington"><option value="1">Josh Martin</option><option value="2">John Doe</option></optgroup> 
     359      #   => <optgroup label="Idaho"><option value="3" selected="selected">Jane Doe</option> 
     360      # 
     361      # ==== Options 
     362      # +text_method+:: Defines the method which will be used to provide the text of the option tags (required) 
     363      # +value_method+:: Defines the method which will be used to provide the value of the option tags (required) 
     364      # +selected+:: The value of a selected object, may be either a string or an array. 
     365      def options_from_collection_for_select(collection, attrs = {}) 
     366          if collection.is_a?(Hash) 
     367            returning '' do |ret| 
     368              collection.each do |label, group| 
     369                ret << open_tag("optgroup", :label => label.to_s) + options_from_collection_for_select(group, attrs) + "</optgroup>" 
     370              end 
     371            end 
     372          else 
     373            text_method    = attrs[:text_method] 
     374            value_method   = attrs[:value_method] 
     375            selected_value = attrs[:selected] 
     376            options_for_select( 
     377               collection.inject([]) { |options, object| options << [ object.send(value_method), object.send(text_method) ] }, 
     378                     :selected => selected_value  
     379            ) 
     380          end 
     381      end 
     382       
     383      # Provides the ability to create quick fieldsets as blocks for your forms. 
     384      # 
     385      # ==== Example 
     386      #   <% fieldset :legend => 'Customer Options' do -%> 
     387      #   ...your form elements 
     388      #   <% end -%> 
     389      # 
     390      #   => <fieldset><legend>Customer Options</legend>...your form elements</fieldset> 
     391      # 
     392      # ==== Options 
     393      # +legend+:: The name of this fieldset which will be provided in a HTML legend tag. 
     394      def fieldset(attrs = {}, &block) 
     395        legend = attrs.delete(:legend) 
     396        open_tag( 'fieldset', attrs ) + ( tag('legend', legend) if legend ) + yield + "</fieldset>" 
     397      end 
     398 
    168399      ## file input control 
    169400      def file_control(col, attrs = {}) 
     
    197428        if label 
    198429          title = label.is_a?(Hash) ? label.delete(:title) : label 
    199           label(title, yield, label.is_a?(Hash) ? label : {}) 
     430          named = attrs[:name].blank? ? {} : {:for => attrs[:name]} 
     431          label(title, '', label.is_a?(Hash) ? label : named) + yield 
    200432        else 
    201433          yield 
    202434        end 
    203       end    
     435      end 
    204436       
    205437      def errorify_field(attrs, col) 
     
    211443end 
    212444 
    213 class Merb::ViewContext 
     445class Merb::ViewContext #:nodoc: 
    214446  include Merb::Helpers::Form 
    215447end 
  • plugins/merb_helpers/specs/merb_helpers_spec.rb

    r1002 r1006  
    162162  end 
    163163   
    164   it "should wrap the field in a label if the :label option is passed to the text_field" do 
     164  it "should provide an additional label tag if the :label option is passed in" do 
    165165    result = text_field(:label => "LABEL" ) 
    166     result.should match(/<label>LABEL<input type="text"\s*\/><\/label>/) 
     166    result.should match(/<label>LABEL<\/label><input type="text"\s*\/>/) 
    167167  end 
    168168end 
     
    183183  end 
    184184   
    185   it "should wrap the text_control in a label if the :label option is passed in" do 
     185  it "should provide an additional label tag if the :label option is passed in" do 
    186186    form_for :obj do 
    187187      _buffer << text_control(:foo, :label => "LABEL") 
    188188    end 
    189     _buffer.should match(/<label>LABEL<input/) 
     189    _buffer.should match(/<label.*>LABEL<\/label><input/) 
    190190    res = _buffer.scan(/<[^>]*>/) 
    191191    res[2].should_not match_tag(:input, :label => "LABEL") 
     
    200200  end 
    201201   
    202   it "should wrap the field in a label if the :label option is passed to the text_field" do 
     202  it "should provide an additional label tag if the :label option is passed in" do 
    203203    result = password_field(:label => "LABEL" ) 
    204     result.should match(/<label>LABEL<input type="password"\s*\/><\/label>/) 
     204    result.should match(/<label.*>LABEL<\/label><input type="password"\s*\/>/) 
    205205  end 
    206206end 
     
    221221  end 
    222222   
    223   it "should wrap the text_control in a label if the :label option is passed in" do 
     223  it "should provide an additional label tag if the :label option is passed in" do 
    224224    form_for :obj do 
    225225      _buffer << password_control(:foo, :label => "LABEL") 
    226226    end 
    227     _buffer.should match(/<label>LABEL<input/) 
     227    _buffer.should match(/<label.*>LABEL<\/label><input/) 
    228228    res = _buffer.scan(/<[^>]*>/) 
    229229    res[2].should_not match_tag(:input, :label => "LABEL") 
     
    236236  end 
    237237   
    238   it "should wrap the checkbox_field in a label if the :label option is passed in" do 
     238  it "should provide an additional label tag if the :label option is passed in" do 
    239239    result = checkbox_field(:label => "LABEL" ) 
    240     result.should match(/<label>LABEL<input/) 
     240    result.should match(/<label>LABEL<\/label><input/) 
    241241    res = result.scan(/<[^>]*>/) 
    242242    res[2].should_not match_tag(:input, :label => "LABEL") 
     
    280280  end   
    281281   
    282   it "should wrap the checkbox_control in a label if the label option is passed in" do 
     282  it "should provide an additional label tag if the :label option is passed in" do 
    283283    form_for :obj do 
    284284      _buffer << checkbox_control(:foo, :label => "LABEL") 
    285285    end 
    286     _buffer.should match( /<label>LABEL<input/ ) 
     286    _buffer.should match( /<label.*>LABEL<\/label><input/ ) 
    287287    res = _buffer.scan(/<[^>]*>/) 
    288288    res[2].should_not match_tag(:input, :label => "LABEL") 
     
    333333  end 
    334334   
    335   it "should render a label when the label is passed in" do 
     335  it "should provide an additional label tag if the :label option is passed in" do 
    336336    result = radio_field(:name => "foo", :value => "bar", :label => "LABEL") 
    337     result.should match(/<label>LABEL<input/) 
     337    result.should match(/<label.*>LABEL<\/label><input/) 
    338338    res = result.scan(/<[^>]*>/) 
    339339    res[2].should_not match_tag(:input, :label => "LABEL") 
     
    348348    form_for :obj do 
    349349      radio = radio_group_control(:foo, [:foowee, :baree]).scan(/<[^>]*>/) 
    350       radio[1].should match_tag(:input, :type => "radio", :name => "fake_model[foo]", :value => "foowee", :selected => "selected") 
    351       radio[4].should match_tag(:input, :type => "radio", :name => "fake_model[foo]", :value => "baree") 
    352       radio[2].should not_match_tag(:selected => "selected") 
    353     end 
    354   end 
    355    
    356   it "should wrap the each radio button in the group in a label corresponding to the options" do 
     350      radio[2].should match_tag(:input, :type => "radio", :name => "fake_model[foo]", :value => "foowee", :selected => "selected") 
     351      radio[5].should match_tag(:input, :type => "radio", :name => "fake_model[foo]", :value => "baree") 
     352      radio[3].should not_match_tag(:selected => "selected") 
     353    end 
     354  end 
     355   
     356  it "should provide an additional label tag if the :label option is passed in" do 
    357357    form_for :obj do 
    358358      radio = radio_group_control(:foo, [:foowee, :baree]) 
    359       radio.scan( /<label>(foowee|baree)<input/ ).size.should == 2 
     359      radio.scan( /<label.*?>(foowee|baree)<\/label><input/ ).size.should == 2 
    360360      radio = radio.scan(/<[^>]*>/) 
    361361      radio[1].should_not match_tag(:input, :label => "LABEL") 
     
    380380  it "should render a label when the label is passed in" do 
    381381    result = text_area_field( "CONTENT", :name => "foo", :value => "bar", :label => "LABEL") 
    382     result.should match(/<label>LABEL<textarea/) 
     382    result.should match(/<label.*>LABEL<\/label><textarea/) 
    383383    res = result.scan(/<[^>]*>/) 
    384384    res[1].should_not match_tag(:textarea, :label => "LABEL") 
     
    386386end 
    387387 
    388 describe "text area (data bound)" do 
    389   it_should_behave_like "FakeBufferConsumer" 
    390    
    391   it "should return a bound text area" do 
    392     form_for :obj do 
    393       ta = text_area_control(:foo) 
    394       tab = text_area_control(:foobad) 
    395       ta.should match_tag(:textarea, :name => "fake_model[foo]") 
    396       tab.should match_tag(:textarea, :name => "fake_model[foobad]", :class => "error") 
    397       ta.should include("foowee") 
    398     end 
    399   end 
    400    
    401   it "should handle a nil content value" do 
    402     @obj.nothing.should be_nil 
    403     form_for :obj do 
    404       text_area_control(:nothing).should match_tag(:textarea, :name => "fake_model[nothing]") 
    405     end 
    406   end 
    407    
    408   it "should handle a nil attribute hash" do 
    409     form_for :obj do 
    410       text_area_control(:nothing, nil).should match_tag(:textarea, :name => "fake_model[nothing]") 
    411     end 
    412   end 
    413  
    414   it "should render a label when the label is passed in" do 
    415     form_for :obj do 
    416       result = text_area_control( :foo, :label => "LABEL") 
    417       result.should match(/<label>LABEL<textarea/) 
    418       res = result.scan(/<[^>]*>/) 
    419       res[1].should_not match_tag(:textarea, :label => "LABEL") 
    420     end 
    421   end 
    422  
    423 end 
    424  
    425 describe "form helper supporting methods for controls" do 
    426   it_should_behave_like "FakeBufferConsumer" 
    427    
    428   it "should give class_name[colname] for control_name" do 
    429     form_for :obj do 
    430       text_control( :foo ).should match_tag( :input, :type => "text", :name => "fake_model[foo]") 
    431     end 
    432   end 
    433    
    434   it "should provide value=method_value for the control_value method" do 
    435     form_for :obj do 
    436       text_control( :foo ).should match_tag( :input, :type => "text", :value => "foowee") 
    437     end 
    438   end 
    439    
    440   it "should give name and value for a call to control_name_value" do 
    441     form_for :obj do 
    442       control_name_value(:foo, :attribute => "ATTRIBUTE" ).should == {  :name => "fake_model[foo]", 
    443                                                                         :value => "foowee", 
    444                                                                         :attribute => "ATTRIBUTE"} 
    445     end     
    446   end 
    447 end 
    448  
    449 describe "submit_button" do 
    450   it_should_behave_like "FakeBufferConsumer" 
     388describe "select (data bound)" do 
     389   
     390  it_should_behave_like "FakeBufferConsumer" 
     391 
     392  it "should render the select tag with the correct id and name" do 
     393    form_for :obj do 
     394      content = select_control( :foo ) 
     395      content.should match_tag( :select, :id => "fake_model_foo", :name => "fake_model[foo]" ) 
     396    end 
     397  end 
     398   
     399  it "should include a blank option" do 
     400    form_for :obj do 
     401      content = select_control( :foo, :include_blank => true ) 
     402      content.should match_tag( :option, :value => '') 
     403    end 
     404  end 
     405   
     406  it "should render a prompt option without a value" do 
     407    form_for :obj do 
     408      content = select_control( :foo, :prompt => "Choose" ) 
     409    end 
     410  end 
     411   
     412  it "should render a select tag with options" do 
     413    form_for :obj do 
     414      content = select_control( :foo, :class => 'class1 class2', :title => 'This is the title' ) 
     415      content.should match_tag( :select, :class => "class1 class2", :title=> "This is the title" )  
     416    end 
     417  end 
     418   
     419  it "should render a select tag with options and a blank option" do 
     420    form_for :obj do 
     421      content = select_control( :foo, :title => "TITLE", :include_blank => true ) 
     422      content.should match_tag( :select, :title => "TITLE" )  
     423      content.should match_tag( :option, :value => '' ) 
     424    end 
     425  end 
     426   
     427  it "should render the text as the value if no text_method is specified" do 
     428    form_for :obj do 
     429      content = select_control( :foo, :collection => [FakeModel] ) 
     430      content.should match_tag( :option, :value => "FakeModel" ) 
     431    end 
     432  end 
     433       
     434end 
     435 
     436describe "option tag generation (data bound)" do 
     437  it_should_behave_like "FakeBufferConsumer" 
     438   
     439  it "should use text_method and value_method for tag generation" do 
     440    content = options_from_collection_for_select( [FakeModel.new, FakeModel2.new], :text_method => 'foo', :value_method => 'bar' ) 
     441    content.should match_tag( :option, :content => "foowee", :value => "7" ) 
     442    content.should match_tag( :option, :content => "foowee2", :value => "barbar" ) 
     443  end 
     444   
     445  it "should render a hash of arrays as a grouped select box" do 
     446    @model1 = FakeModel.new ; @model1.make = "Ford"   ; @model1.model = "Mustang"   ; @model1.vin = '1'  
     447    @model2 = FakeModel.new ; @model2.make = "Ford"   ; @model2.model = "Falcon"    ; @model2.vin = '2' 
     448    @model3 = FakeModel.new ; @model3.make = "Holden" ; @model3.model = "Commodore" ; @model3.vin = '3' 
     449   
     450    collection = [@model1, @model2, @model3].group_by( &:make ) 
     451    content = options_from_collection_for_select(collection, :text_method => 'model', :value_method => 'vin', :selected => '1') 
    451452     
    452   it "should produce a simple submit button" do 
    453     submit_button("Foo").should == "<button type=\"submit\">Foo</button>" 
    454   end 
    455 end 
    456  
    457 describe "label helpers" do 
    458   it_should_behave_like "FakeBufferConsumer" 
    459    
    460   it "should add a label to arbitrary markup in a template" do 
    461     result = label("Name:", text_field(:name => "name_value")) 
    462     result.should == "<label>Name:<input type=\"text\" name=\"name_value\"/></label>" 
     453    content.should match_tag( :optgroup, :label => "Ford" ) 
     454    content.should match_tag( :option, :selected => "selected", :value => "1", :content => "Mustang" ) 
     455    content.should match_tag( :option, :value => "2", :content => "Falcon" ) 
     456    content.should match_tag( :optgroup, :label => "Holden" ) 
     457    content.should match_tag( :option, :value => "3", :content => "Commodore" ) 
     458  end 
    463459     
    464   end 
    465      
     460  it "should humanize and titlize keys in the label for the option group" do 
     461    collection = { :some_snake_case_key => [FakeModel] } 
     462    form_for :obj do 
     463      content = select_control( :foo, :collection => collection ) 
     464      content.should match_tag( :optgroup, :label => "Some Snake Case Key" ) 
     465    end 
     466  end 
     467 
     468   
     469end 
     470 
     471describe "option tags generation (basic)" do 
     472  it_should_behave_like "FakeBufferConsumer" 
     473   
     474  before do 
     475    @collection = [['rabbit','Rabbit'],['horse','Horse'],['bird','Bird']] 
     476  end 
     477   
     478  it "should provide an option tag for each item in the collection" do 
     479    result = options_for_select( @collection ) 
     480    doc = Hpricot( result ) 
     481    (doc/"option").size.should == 3 
     482  end 
     483   
     484  it "should provide a blank option" do 
     485    content = options_for_select(@collection, :include_blank => true ) 
     486    content.should match_tag( :option, :value => '' ) 
     487  end 
     488   
     489  it "should provide a prompt option" do 
     490    content = options_for_select( [], :prompt => 'Choose' ) 
     491    content.should match_tag( :option, :value => '', :content => 'Choose' ) 
     492  end 
     493   
     494  it "should provide selected options by value" do 
     495    content = options_for_select( [['rabbit','Rabbit'],['chicken','Chicken']], :selected => 'rabbit' ) 
     496    content.should match_tag( :option, :value => 'rabbit', :selected => 'selected', :content => 'Rabbit' ) 
     497    content.should_not match_tag( :option, :value => 'chicken', :selected => nil, :content => 'Chicken' ) 
     498  end 
     499   
     500  it "should render a hash of options as optgroup" do 
     501    collection = { :fruit => [['orange','Orange'],['banana','Banana']], :vegatables => [['corn','Corn']]} 
     502    content = options_for_select(collection, :selected => 'banana') 
     503    content.should match_tag( :optgroup, :label => 'Fruit' ) 
     504    content.should match_tag( :optgroup, :label => 'Vegatables' ) 
     505    content.should match_tag( :option, :value => 'banana', :selected => 'selected', :content => 'Banana' ) 
     506  end 
     507   
     508end 
     509 
     510describe "fieldset generation (basic)" do 
     511  it_should_behave_like "FakeBufferConsumer" 
     512 
     513  it "should provide legend option" do 
     514    form_tag do 
     515      _buffer << "CONTENT" 
     516    end 
     517    _buffer.should match_tag(:form, :method => "post") 
     518    _buffer.should include("CONTENT") 
     519  end 
    466520end 
    467521 
     
    475529  it "should wrap the field in a label if the :label option is passed to the file_field" do 
    476530    result = file_field(:label => "LABEL" ) 
    477     result.should match(/<label>LABEL<input type="file"\s*\/><\/label>/) 
     531    result.should match(/<label>LABEL<\/label><input type="file"\s*\/>/) 
    478532  end 
    479533end 
     
    498552      _buffer << text_control(:foo, :label => "LABEL") 
    499553    end 
    500     _buffer.should match(/<label>LABEL<input/) 
     554    _buffer.should match(/<label.*>LABEL<\/label><input/) 
    501555    res = _buffer.scan(/<[^>]*>/) 
    502556    res[2].should_not match_tag(:input, :label => "LABEL") 
  • plugins/merb_helpers/specs/spec_helper.rb

    r746 r1006  
    66 
    77class FakeModel 
     8   
     9  attr_accessor :vin, :make, :model 
     10   
    811  def self.columns 
    912    [FakeColumn.new(:foo, :string),  
    10      FakeColumn.new(:foobad, :string),        
     13     FakeColumn.new(:foobad, :string), 
     14     FakeColumn.new(:desc, :string), 
    1115     FakeColumn.new(:bar, :integer),  
    1216     FakeColumn.new(:barbad, :integer),       
     
    3337    "foowee" 
    3438  end 
    35    
    3639  alias_method :foobad, :foo 
    3740   
     
    5760 
    5861class FakeModel2 < FakeModel 
    59  
     62   
    6063  def foo 
    6164    "foowee2" 
    6265  end 
    6366  alias_method :foobad, :foo 
     67   
     68  def bar 
     69    "barbar" 
     70  end 
    6471   
    6572  def new_record? 
     
    8390class FakeColumn 
    8491  attr_accessor :name, :type 
     92   
    8593  def initialize(name, type) 
    8694    @name, @type = name, type 
    8795  end 
     96   
    8897end 
    8998 
    90  
    91    
    92  
    9399describe "FakeBufferConsumer", :shared => true do 
    94    
    95100  before :each do 
    96101    @obj = FakeModel.new