Changeset 746

Show
Ignore:
Timestamp:
10/14/07 19:40:35 (1 year ago)
Author:
wyca..@gmail.com
Message:

Fixes various bugs in form_helpers

Files:

Legend:

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

    r732 r746  
    1 class Hash 
    2    
    3   def to_html_attributes 
    4     map do |k,v| 
    5       "#{k.to_s.camelize.downcase}=\"#{v}\"" 
    6     end.join(" ") 
    7   end 
    8    
    9   def add_html_class!(html_class) 
    10     if self[:class] 
    11       self[:class] = "#{self[:class]} #{html_class}" 
    12     else 
    13       self[:class] = html_class 
    14     end 
    15   end 
    16    
    17 end 
    18  
    191module Merb 
    202  module Helpers 
     
    3618      end 
    3719       
    38       def open_tag(name, attrs = nil) 
    39         "<#{name}#{' ' + attrs.to_html_attributes if attrs}>" 
    40       end 
    41        
    42       def self_closing_tag(name, attrs = nil) 
    43         "<#{name}#{' ' + attrs.to_html_attributes if attrs}/>" 
    44       end 
    45        
    4620      def form_tag(attrs = {}, &block) 
     21        attrs.merge!( :enctype => "multipart/form-data" ) if attrs.delete(:multipart) 
     22        fake_form_method = set_form_method(attrs) 
    4723        concat(open_tag("form", attrs), block.binding) 
     24        concat(generate_fake_form_method(fake_form_method), block.binding) if fake_form_method 
    4825        concat(capture(&block), block.binding) 
    4926        concat("</form>", block.binding) 
    5027      end 
    5128       
    52       def form_for(obj, attrs=nil, &block) 
     29      def form_for(obj, attrs={}, &block) 
     30        fake_form_method = set_form_method(attrs, instance_variable_get("@#{obj}")) 
    5331        concat(open_tag("form", attrs), block.binding) 
     32        concat(generate_fake_form_method(fake_form_method), block.binding) if fake_form_method 
    5433        fields_for(obj, attrs, &block) 
    5534        concat("</form>", block.binding) 
     
    5837      def fields_for(obj, attrs=nil, &block) 
    5938        old_obj, @_obj = @_obj, instance_variable_get("@#{obj}") 
    60         @_object_name = obj 
     39        @_object_name = "#{@_obj.class}".snake_case 
    6140        old_block, @_block = @_block, block 
    6241         
     
    6645      end 
    6746       
    68       def name(col) 
     47      def control_name(col) 
    6948        "#{@_object_name}[#{col}]" 
    7049      end 
    7150       
    72       def value(col) 
     51      def control_value(col) 
    7352        @_obj.send(col) 
    7453      end 
    7554       
    76       def name_value(col, attrs) 
    77         {:name => name(col), :value => value(col)}.merge(attrs) 
     55      def control_name_value(col, attrs) 
     56        {:name => control_name(col), :value => control_value(col)}.merge(attrs) 
    7857      end 
    7958       
    8059      def text_control(col, attrs = {}) 
    8160        errorify_field(attrs, col) 
    82         text_field(name_value(col, attrs)) 
     61        text_field(control_name_value(col, attrs)) 
    8362      end 
    8463       
    8564      def text_field(attrs = {}) 
    8665        attrs.merge!(:type => "text") 
    87         self_closing_tag("input", attrs) 
     66        add_field_label(attrs){self_closing_tag("input", attrs)} 
    8867      end 
    8968       
     
    9372        attrs.merge!(:value => val ? "1" : "0") 
    9473        attrs.merge!(:checked => "checked") if val 
    95         checkbox_field(name_value(col, attrs)) 
     74        checkbox_field(control_name_value(col, attrs)) 
    9675      end 
    9776       
     
    9978        attrs.merge!(:type => :checkbox) 
    10079        attrs.add_html_class!("checkbox") 
    101         self_closing_tag("input", attrs) 
     80        add_field_label(attrs){self_closing_tag("input", attrs)} 
    10281      end 
    10382       
    10483      def hidden_control(col, attrs = {}) 
     84        attrs.delete(:label) 
    10585        errorify_field(attrs, col) 
    106         hidden_field(name_value(col, attrs)) 
     86        hidden_field(control_name_value(col, attrs)) 
    10787      end 
    10888       
    10989      def hidden_field(attrs = {}) 
     90        attrs.delete(:label) 
    11091        attrs.merge!(:type => :hidden) 
    11192        self_closing_tag("input", attrs) 
     
    11798        ret = "" 
    11899        options.each do |opt| 
    119           hash = {:name => "#{@_object_name}[#{col}]", :value => opt
     100          hash = {:name => "#{@_object_name}[#{col}]", :value => opt, :label => opt
    120101          hash.merge!(:selected => "selected") if val.to_s == opt.to_s 
    121102          ret << radio_field(hash) 
     
    127108        attrs.merge!(:type => "radio") 
    128109        attrs.add_html_class!("radio") 
    129         self_closing_tag("input", attrs) 
     110        add_field_label(attrs){self_closing_tag("input", attrs)} 
    130111      end 
    131112       
    132113      def text_area_control(col, attrs = {}) 
     114        attrs ||= {} 
    133115        errorify_field(attrs, col) 
    134         text_area_field(value(col), attrs.merge(:name => name(col))) 
     116        text_area_field(control_value(col), attrs.merge(:name => control_name(col))) 
    135117      end 
    136118       
    137119      def text_area_field(val, attrs = {}) 
    138         open_tag("textarea", attrs) + 
    139         val + 
    140         "</textarea>" 
     120        val ||="" 
     121        add_field_label(attrs) do 
     122          open_tag("textarea", attrs) + 
     123          val + 
     124          "</textarea>" 
     125        end 
    141126      end 
    142127       
     
    147132 
    148133      def errorify_field(attrs, col) 
    149         attrs.add_html_class!("error") if !@obj.valid? && @obj.errors.on(col) 
     134        attrs.add_html_class!("error") if !@_obj.valid? && @_obj.errors.on(col) 
     135      end 
     136       
     137      def add_label(label, &block) 
     138        concat("<label>#{label}", block.binding) 
     139        yield 
     140        concat("</label>", block.binding ) 
     141      end 
     142       
     143       
     144      def add_field_label( attrs, &block ) 
     145        case attrs 
     146        when Hash 
     147          label_name = attrs.delete :label 
     148        when String, Symbol 
     149          label_name = attrs.to_s 
     150        end 
     151        ret = "" 
     152        ret << "<label>#{label_name}" if label_name 
     153        ret << yield 
     154        ret << "</label>" if label_name 
     155        ret 
     156      end 
     157       
     158      private 
     159      # Fake out the browser to send back the method for RESTful stuff. 
     160      # Fall silently back to post if a method is given that is not supported here 
     161      def set_form_method(options = {}, obj = nil) 
     162        options[:method] ||= (!obj || obj.new_record? ? :post : :put) 
     163        if ![:get,:post].include?(options[:method]) 
     164          fake_form_method = options[:method] if [:put, :delete].include?(options[:method]) 
     165          options[:method] = :post 
     166        end 
     167        fake_form_method 
     168      end 
     169 
     170      def generate_fake_form_method(fake_form_method) 
     171        fake_form_method ? hidden_field(:name => "_method", :value => "#{fake_form_method}") : "" 
    150172      end 
    151173       
  • plugins/merb_helpers/specs/merb_helpers_spec.rb

    r734 r746  
    3939  it_should_behave_like "FakeBufferConsumer" 
    4040   
     41  it "should use the post method by default" do 
     42    form_tag do 
     43      _buffer << "CONTENT" 
     44    end 
     45    _buffer.should match_tag(:form, :method => "post") 
     46    _buffer.should include("CONTENT") 
     47  end 
     48   
     49  it "should use the get method if set" do 
     50    form_tag :method => :get do 
     51      _buffer << "CONTENT" 
     52    end 
     53    _buffer.should match_tag(:form, :method => "get") 
     54    _buffer.should include("CONTENT")         
     55  end 
     56   
     57  it "should fake out the put method if set" do 
     58    form_tag :method => :put do 
     59      _buffer << "CONTENT" 
     60    end 
     61    _buffer.should match_tag(:form, :method => "post") 
     62    _buffer.should match_tag(:input, :type => "hidden", :name => "_method", :value => "put")     
     63  end 
     64   
     65  it "should fake out the delete method if set" do 
     66    form_tag :method => :delete do 
     67      _buffer << "CONTENT" 
     68    end 
     69    _buffer.should match_tag(:form, :method => "post") 
     70    _buffer.should match_tag(:input, :type => "hidden", :name => "_method", :value => "delete") 
     71  end 
     72   
     73  it "should silently set method to post if an unsupported method is used" do 
     74      form_tag :method => :dodgy do 
     75        _buffer << "CONTENT" 
     76      end 
     77      _buffer.should match_tag(:form, :method => "post") 
     78      _buffer.should_not match_tag(:input, :type => "hidden", :name => "_method", :value => "dodgy") 
     79  end 
     80   
    4181  it "should take create a form" do 
    42     form_tag(:action => "foo", :method => "POST") do 
     82    form_tag(:action => "foo", :method => :post) do 
    4383      _buffer << "Hello" 
    4484    end 
    45     _buffer.should match_tag(:form, :action => "foo", :method => "POST") 
     85    _buffer.should match_tag(:form, :action => "foo", :method => "post") 
    4686    _buffer.should include("Hello") 
     87  end 
     88   
     89  it "should set a form to be mutlipart" do 
     90    form_tag( :action => "foo", :method => :post, :multipart => true ) do 
     91      _buffer << "CONTENT" 
     92    end 
     93    _buffer.should match_tag( :form, :action => "foo", :method => "post", :enctype => "multipart/form-data") 
     94    _buffer.should include("CONTENT")   
    4795  end 
    4896end 
     
    53101  it "should wrap the contents in a form tag" do 
    54102    form_for(:obj) do 
    55       _buffer("") << "Hello" 
    56     end 
    57     _buffer("").should == "<form>Hello</form>" 
    58   end 
     103      _buffer << "Hello" 
     104    end 
     105    _buffer.should match_tag(:form, :method => "post") 
     106    _buffer.should match_tag(:input, :type => "hidden", :value => "put", :name => "_method")     
     107  end 
     108   
     109  it "should set the method to post be default" do 
     110    @obj2 = FakeModel2.new 
     111    form_for(:obj2) do 
     112    end 
     113    _buffer.should match_tag(:form, :method => "post") 
     114    _buffer.should_not match_tag(:input, :type => "hidden", :name => "_method") 
     115  end 
     116   
     117  it "should support PUT if the object passed in is not a new_record? via a hidden field" do 
     118    form_for(:obj) do 
     119    end 
     120    _buffer.should match_tag(:form, :method => "post") 
     121    _buffer.should match_tag(:input, :type => "hidden", :value => "put", :name => "_method")     
     122  end 
     123   
    59124end 
    60125 
     
    64129  it "should dump the contents in the context of the object" do 
    65130    fields_for(:obj) do 
    66       _buffer("") << "Hello" 
    67     end 
    68     _buffer("").should == "Hello" 
     131      text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee") 
     132      _buffer << "Hello" 
     133    end 
     134    _buffer.should == "Hello" 
    69135  end   
    70136 
     
    74140      text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee")       
    75141      fields_for(:obj2) do 
    76         text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee2") 
     142        text_control(:foo).should match_tag(:input, :name => "fake_model2[foo]", :type => "text", :value => "foowee2") 
    77143      end 
    78144      text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee")       
    79145    end 
    80146  end 
     147   
     148  it "should handle an explicit nil attribute" do 
     149    fields_for(:obj, nil) do 
     150      _buffer << text_control(:foo) 
     151    end 
     152    _buffer.should match_tag(:input, :name => "fake_model[foo]", :value => "foowee", :type => "text") 
     153  end 
     154   
    81155end 
    82156 
    83157describe "text_field (basic)" do 
     158  it_should_behave_like "FakeBufferConsumer" 
     159   
    84160  it "should return a basic text field based on the values passed in" do 
    85161    text_field(:name => "foo", :value => "bar").should match_tag( :input, :type => "text", :name => "foo", :value => "bar") 
    86162  end 
     163   
     164  it "should wrap the field in a label if the :label option is passed to the text_field" do 
     165    result = text_field(:label => "LABEL" ) 
     166    result.should match(/<label>LABEL<input type="text"\s*\/><\/label>/) 
     167  end 
    87168end 
    88169 
     
    92173  it "should take a string object and return a useful text control" do 
    93174    f = form_for :obj do 
    94       text_control(:foo).should match_tag(:input, :type => "text", :name => "obj[foo]", :value => "foowee") 
     175      text_control(:foo).should match_tag(:input, :type => "text", :name => "fake_model[foo]", :value => "foowee") 
    95176    end 
    96177  end 
     
    98179  it "should take additional attributes and use them" do 
    99180    form_for :obj do 
    100       text_control(:foo, :bar => "7").should match_tag(:input, :type => "text", :name => "obj[foo]", :value => "foowee", :bar => "7") 
    101     end 
     181      text_control(:foo, :bar => "7").should match_tag(:input, :type => "text", :name => "fake_model[foo]", :value => "foowee", :bar => "7") 
     182    end 
     183  end 
     184   
     185  it "should wrap the text_control in a label if the :label option is passed in" do 
     186    form_for :obj do 
     187      _buffer << text_control(:foo, :label => "LABEL") 
     188    end 
     189    _buffer.should match(/<label>LABEL<input/) 
     190    res = _buffer.scan(/<[^>]*>/) 
     191    res[2].should_not match_tag(:input, :label => "LABEL") 
    102192  end 
    103193end 
     
    106196  it "should return a basic checkbox based on the values passed in" do 
    107197    checkbox_field(:name => "foo", :checked => "checked").should match_tag(:input, :class => "checkbox", :name => "foo", :checked => "checked") 
     198  end 
     199   
     200  it "should wrap the checkbox_field in a label if the :label option is passed in" do 
     201    result = checkbox_field(:label => "LABEL" ) 
     202    result.should match(/<label>LABEL<input/) 
     203    res = result.scan(/<[^>]*>/) 
     204    res[2].should_not match_tag(:input, :label => "LABEL") 
    108205  end 
    109206end 
     
    114211  it "should take a string and return a useful checkbox control" do 
    115212    form_for :obj do 
    116       checkbox_control(:baz).should match_tag(:input, :type =>"checkbox", :name => "obj[baz]", :class => "checkbox", :value => "1", :checked => "checked") 
    117       checkbox_control(:bat).should match_tag(:input, :type =>"checkbox", :name => "obj[bat]", :class => "checkbox", :value => "0") 
     213      checkbox_control(:baz).should match_tag(:input, :type =>"checkbox", :name => "fake_model[baz]", :class => "checkbox", :value => "1", :checked => "checked") 
     214      checkbox_control(:bat).should match_tag(:input, :type =>"checkbox", :name => "fake_model[bat]", :class => "checkbox", :value => "0") 
    118215    end 
    119216  end 
     
    121218  it "should render controls with errors if their attribute contains an error" do 
    122219    form_for :obj do 
    123       checkbox_control(:bazbad).should match_tag(:input, :type =>"checkbox", :name => "obj[bazbad]",  
     220      checkbox_control(:bazbad).should match_tag(:input, :type =>"checkbox", :name => "fake_model[bazbad]",  
    124221        :class => "error checkbox", :value => "1", :checked => "checked") 
    125       checkbox_control(:batbad).should match_tag(:input, :type =>"checkbox", :name => "obj[batbad]",  
     222      checkbox_control(:batbad).should match_tag(:input, :type =>"checkbox", :name => "fake_model[batbad]",  
    126223        :class => "error checkbox", :value => "0")         
    127224    end 
    128   end 
    129      
     225  end   
     226   
     227  it "should wrap the checkbox_control in a label if the label option is passed in" do 
     228    form_for :obj do 
     229      _buffer << checkbox_control(:foo, :label => "LABEL") 
     230    end 
     231    _buffer.should match( /<label>LABEL<input/ ) 
     232    res = _buffer.scan(/<[^>]*>/) 
     233    res[2].should_not match_tag(:input, :label => "LABEL") 
     234    end 
    130235end 
    131236 
     
    135240    hidden_field(:name => "foo", :value => "bar").should match_tag(:input, :type => "hidden", :name => "foo", :value => "bar") 
    136241  end 
     242   
     243  it "should not render a label if the :label option is passed in" do 
     244    res = hidden_field(:label => "LABEL") 
     245    res.should_not match(/<label>LABEL/) 
     246    res.should_not match_tag(:input, :label=> "LABEL")   
     247  end 
    137248end 
    138249 
     
    141252     
    142253  it "should take a string and return a useful checkbox control" do 
    143     form_for :obj do 
    144       hidden_control(:foo).should match_tag(:input, :type =>"hidden", :name => "obj[foo]", :value => "foowee") 
     254    form_for :obj do  
     255      hidden_control(:foo).should match_tag(:input, :type =>"hidden", :name => "fake_model[foo]", :value => "foowee") 
    145256    end 
    146257  end 
     
    148259  it "should render controls with errors if their attribute contains an error" do 
    149260    form_for :obj do 
    150       hidden_control(:foobad).should match_tag(:input, :type =>"hidden", :name => "obj[foobad]", :value => "foowee", :class => "error") 
     261      hidden_control(:foobad).should match_tag(:input, :type =>"hidden", :name => "fake_model[foobad]", :value => "foowee", :class => "error") 
     262    end 
     263  end 
     264   
     265  it "should not render a label if the :label option is passed in" do 
     266    form_for :obj do 
     267      res = hidden_control(:foo, :label => "LABEL") 
     268      res.should_not match(/<label>LABEL/) 
     269      res.should_not match_tag(:input, :label=> "LABEL")   
    151270    end 
    152271  end 
     
    158277    radio_field(:name => "foo", :value => "bar").should match_tag(:input, :type => "radio", :name => "foo", :value => "bar") 
    159278  end 
     279   
     280  it "should render a label when the label is passed in" do 
     281    result = radio_field(:name => "foo", :value => "bar", :label => "LABEL") 
     282    result.should match(/<label>LABEL<input/) 
     283    res = result.scan(/<[^>]*>/) 
     284    res[2].should_not match_tag(:input, :label => "LABEL") 
     285  end 
     286   
    160287end 
    161288 
     
    166293    form_for :obj do 
    167294      radio = radio_group_control(:foo, [:foowee, :baree]).scan(/<[^>]*>/) 
    168       radio[0].should match_tag(:input, :type => "radio", :name => "obj[foo]", :value => "foowee", :selected => "selected") 
    169       radio[1].should match_tag(:input, :type => "radio", :name => "obj[foo]", :value => "baree") 
    170       radio[1].should not_match_tag(:selected => "selected") 
     295      radio[1].should match_tag(:input, :type => "radio", :name => "fake_model[foo]", :value => "foowee", :selected => "selected") 
     296      radio[4].should match_tag(:input, :type => "radio", :name => "fake_model[foo]", :value => "baree") 
     297      radio[2].should not_match_tag(:selected => "selected") 
     298    end 
     299  end 
     300   
     301  it "should wrap the each radio button in the group in a label corresponding to the options" do 
     302    form_for :obj do 
     303      radio = radio_group_control(:foo, [:foowee, :baree]) 
     304      radio.scan( /<label>(foowee|baree)<input/ ).size.should == 2 
     305      radio = radio.scan(/<[^>]*>/) 
     306      radio[1].should_not match_tag(:input, :label => "LABEL") 
     307      radio[4].should_not match_tag(:input, :label => "LABEL") 
    171308    end 
    172309  end 
     
    174311 
    175312describe "text area (basic)" do 
    176   # include TagMatchers 
    177313  it "should should return a basic text area based on the values passed in" do 
    178314    text_area_field("foo", :name => "foo").should match_tag(:textarea, :name => "foo") 
    179315  end 
     316   
     317  it "should handle a nil content" do 
     318    text_area_field(nil, :name => "foo").should == "<textarea name=\"foo\"></textarea>" 
     319  end 
     320   
     321  it "should handle a nil attributes hash" do 
     322    text_area_field("CONTENT", nil).should == "<textarea>CONTENT</textarea>" 
     323  end 
     324   
     325  it "should render a label when the label is passed in" do 
     326    result = text_area_field( "CONTENT", :name => "foo", :value => "bar", :label => "LABEL") 
     327    result.should match(/<label>LABEL<textarea/) 
     328    res = result.scan(/<[^>]*>/) 
     329    res[1].should_not match_tag(:textarea, :label => "LABEL") 
     330  end 
    180331end 
    181332 
     
    187338      ta = text_area_control(:foo) 
    188339      tab = text_area_control(:foobad) 
    189       ta.should match_tag(:textarea, :name => "obj[foo]") 
    190       tab.should match_tag(:textarea, :name => "obj[foobad]", :class => "error") 
     340      ta.should match_tag(:textarea, :name => "fake_model[foo]") 
     341      tab.should match_tag(:textarea, :name => "fake_model[foobad]", :class => "error") 
    191342      ta.should include("foowee") 
    192343    end 
    193344  end 
    194 end 
    195  
     345   
     346  it "should handle a nil content value" do 
     347    @obj.nothing.should be_nil 
     348    form_for :obj do 
     349      text_area_control(:nothing).should match_tag(:textarea, :name => "fake_model[nothing]") 
     350    end 
     351  end 
     352   
     353  it "should handle a nil attribute hash" do 
     354    form_for :obj do 
     355      text_area_control(:nothing, nil).should match_tag(:textarea, :name => "fake_model[nothing]") 
     356    end 
     357  end 
     358 
     359  it "should render a label when the label is passed in" do 
     360    form_for :obj do 
     361      result = text_area_control( :foo, :label => "LABEL") 
     362      result.should match(/<label>LABEL<textarea/) 
     363      res = result.scan(/<[^>]*>/) 
     364      res[1].should_not match_tag(:textarea, :label => "LABEL") 
     365    end 
     366  end 
     367 
     368end 
     369 
     370describe "form helper supporting methods for controls" do 
     371  it_should_behave_like "FakeBufferConsumer" 
     372   
     373  it "should give class_name[colname] for control_name" do 
     374    form_for :obj do 
     375      text_control( :foo ).should match_tag( :input, :type => "text", :name => "fake_model[foo]") 
     376    end 
     377  end 
     378   
     379  it "should provide value=method_value for the control_value method" do 
     380    form_for :obj do 
     381      text_control( :foo ).should match_tag( :input, :type => "text", :value => "foowee") 
     382    end 
     383  end 
     384   
     385  it "should give name and value for a call to control_name_value" do 
     386    form_for :obj do 
     387      control_name_value(:foo, :attribute => "ATTRIBUTE" ).should == {  :name => "fake_model[foo]", 
     388                                                                        :value => "foowee", 
     389                                                                        :attribute => "ATTRIBUTE"} 
     390    end     
     391  end 
     392end 
    196393 
    197394describe "submit_button" do 
     
    202399  end 
    203400end 
     401 
     402describe "label helpers" do 
     403  it_should_behave_like "FakeBufferConsumer" 
     404   
     405  it "should add a label to arbitrary markup in a template" do 
     406    result = add_label("Name:"){ _buffer << text_field(:name => "name_value") } 
     407    result.should == "<label>Name:<input type=\"text\" name=\"name_value\"/></label>" 
     408     
     409  end 
     410   
     411  it "should add a label to a text returning helper" do 
     412    form_for(:obj) do 
     413      _buffer << add_field_label({:label => "LABEL"}){ text_control(:foo) } 
     414    end 
     415    _buffer.should match(/<label>LABEL<input .+\/><\/label>/) 
     416  end 
     417   
     418end 
     419 
  • plugins/merb_helpers/specs/spec_helper.rb

    r734 r746  
    33require 'rubygems' 
    44require 'merb' 
    5 require 'merb/test/rspec' 
     5# require 'merb/test/rspec' 
    66 
    77class FakeModel 
     
    2222  end 
    2323   
     24  def new_record? 
     25    false 
     26  end 
     27   
    2428  def errors 
    2529    FakeErrors.new(self) 
     
    2933    "foowee" 
    3034  end 
     35   
    3136  alias_method :foobad, :foo 
    3237   
     
    4550  end 
    4651  alias_method :batbad, :bat 
     52   
     53  def nothing 
     54    nil 
     55  end 
    4756end 
    4857 
     
    5362  end 
    5463  alias_method :foobad, :foo 
     64   
     65  def new_record? 
     66    true 
     67  end 
    5568   
    5669end