Changeset 731

Show
Ignore:
Timestamp:
10/08/07 16:35:13 (1 year ago)
Author:
wyca..@gmail.com
Message:

Lots of progress on merb helpers

Files:

Legend:

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

    r730 r731  
     1class 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   
     17end 
     18 
    119module Merb 
    220  module Helpers 
     
    1836      end 
    1937       
    20       def form_for(obj, &block) 
    21         concat("<form>", block.binding) 
    22          
     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       
     46      def form_tag(attrs = {}, &block) 
     47        concat(open_tag("form", attrs), block.binding) 
     48        concat(capture(&block), block.binding) 
     49        concat("</form>", block.binding) 
     50      end 
     51       
     52      def form_for(obj, attrs=nil, &block) 
     53        concat(open_tag("form", attrs), block.binding) 
     54        fields_for(obj, attrs, &block) 
     55        concat("</form>", block.binding) 
     56      end 
     57       
     58      def fields_for(obj, attrs=nil, &block) 
    2359        old_obj, @_obj = @_obj, instance_variable_get("@#{obj}") 
    2460        @_object_name = obj 
    2561        old_block, @_block = @_block, block 
    2662         
    27         block.call 
    28          
    29         concat("</form>", block.binding) 
    30         @_obj, @_block = old_obj, old_block 
     63        concat(capture(&block), block.binding) 
     64 
     65        @_obj, @_block = old_obj, old_block         
    3166      end 
    3267       
    33       def text_control(col) 
    34         concat("<input type='text' name='#{@_object_name}[#{col}]' value='#{@_obj.send(col)}'/>", @_block.binding) 
     68      def name_value(col, attrs) 
     69        {:name => "#{@_object_name}[#{col}]", :value => "#{@_obj.send(col)}"}.merge(attrs) 
     70      end 
     71       
     72      def text_control(col, attrs = {}) 
     73        errorify_field(attrs, col) 
     74        text_field(name_value(col, attrs)) 
     75      end 
     76       
     77      def text_field(attrs = {}) 
     78        attrs.merge!(:type => "text") 
     79        self_closing_tag("input", attrs) 
     80      end 
     81       
     82      def checkbox_control(col, attrs = {}) 
     83        errorify_field(attrs, col) 
     84        val = @_obj.send(col) 
     85        attrs.merge!(:value => val ? "1" : "0") 
     86        attrs.merge!(:checked => "checked") if val 
     87        checkbox_field(name_value(col, attrs)) 
     88      end 
     89       
     90      def checkbox_field(attrs = {}) 
     91        attrs.merge!(:type => :checkbox) 
     92        attrs.add_html_class!("checkbox") 
     93        self_closing_tag("input", attrs) 
     94      end 
     95       
     96      def hidden_control(col, attrs = {}) 
     97        errorify_field(attrs, col) 
     98        hidden_field(name_value(col, attrs)) 
     99      end 
     100       
     101      def hidden_field(attrs = {}) 
     102        attrs.merge!(:type => :hidden) 
     103        self_closing_tag("input", attrs) 
     104      end 
     105       
     106      def radio_group_control(col, options = {}, attrs = {}) 
     107        errorify_field(attrs, col) 
     108        val = @_obj.send(col) 
     109        ret = "" 
     110        options.each do |opt| 
     111          hash = {:name => "#{@_object_name}[#{col}]", :value => opt} 
     112          hash.merge!(:selected => "selected") if val.to_s == opt.to_s 
     113          ret << radio_field(hash) 
     114        end 
     115        ret 
     116      end 
     117       
     118      def radio_field(attrs = {}) 
     119        attrs.merge!(:type => "radio") 
     120        attrs.add_html_class!("radio") 
     121        self_closing_tag("input", attrs) 
     122      end 
     123       
     124      def submit_button(contents, attrs = {}) 
     125        attrs.merge!(:type => "submit") 
     126        open_tag("button", attrs) + contents + "</button>" 
     127      end 
     128 
     129      def errorify_field(attrs, col) 
     130        attrs.add_html_class!("error") if !@obj.valid? && @obj.errors.on(col) 
    35131      end 
    36132       
  • plugins/merb_helpers/specs/merb_helpers_spec.rb

    r729 r731  
    55include Merb::ErubisCaptureMixin 
    66include Merb::Helpers::Form 
    7  
    87 
    98describe "error_messages_for" do 
     
    3736end 
    3837 
    39 class FakeModel 
    40   def self.columns 
    41     [FakeColumn.new(:foo, :string), FakeColumn.new(:bar, :integer)] 
    42   end 
     38describe "form_tag" do 
     39  it_should_behave_like "FakeBufferConsumer" 
    4340   
    44   def foo 
    45     "foowee" 
     41  it "should take create a form" do 
     42    form_tag(:action => "foo", :method => "POST") do 
     43      _buffer << "Hello" 
     44    end 
     45    _buffer.should match_tag(:form, :action => "foo", :method => "POST") 
     46    _buffer.should include("Hello") 
    4647  end 
    4748end 
    4849 
    49 class FakeColumn 
    50   attr_accessor :name, :type 
    51   def initialize(name, type) 
    52     @name, @type = name, type 
     50describe "form_for" do 
     51  it_should_behave_like "FakeBufferConsumer"   
     52   
     53  it "should wrap the contents in a form tag" do 
     54    form_for(:obj) do 
     55      _buffer("") << "Hello" 
     56    end 
     57    _buffer("").should == "<form>Hello</form>" 
    5358  end 
    5459end 
    5560 
    56 describe "text_control" do 
     61describe "fields_for" do 
     62  it_should_behave_like "FakeBufferConsumer" 
    5763   
    58   before :each do 
    59     @obj = FakeModel.new 
    60     def _buffer(buf) @buffer ||= "" end     
     64  it "should dump the contents in the context of the object" do 
     65    fields_for(:obj) do 
     66      _buffer("") << "Hello" 
     67    end 
     68    _buffer("").should == "Hello" 
     69  end   
     70 
     71  it "should be able to modify the context midstream" do 
     72    @obj2 = FakeModel2.new     
     73    form_for(:obj) do 
     74      text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee")       
     75      fields_for(:obj2) do 
     76        text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee2") 
     77      end 
     78      text_control(:foo).should match_tag(:input, :type => "text", :value => "foowee")       
     79    end 
    6180  end 
     81end 
     82 
     83describe "text_field (basic)" do 
     84  it "should return a basic text field based on the values passed in" do 
     85    text_field(:name => "foo", :value => "bar").should == "<input type=\"text\" name=\"foo\" value=\"bar\"/>"   
     86  end 
     87end 
     88 
     89describe "text_control (data bound)" do 
     90  it_should_behave_like "FakeBufferConsumer" 
    6291   
    6392  it "should take a string object and return a useful text control" do 
    6493    f = form_for :obj do 
    65       text_control(:foo).should == "<form><input type='text' name='obj[foo]' value='foowee'/>" 
     94      text_control(:foo).should match_tag(:input, :type => "text", :name => "obj[foo]", :value => "foowee") 
    6695    end 
    67     f.should == "<form><input type='text' name='obj[foo]' value='foowee'/></form>" 
     96  end 
     97 
     98  it "should take additional attributes and use them" do 
     99    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 
     102  end 
     103end 
     104 
     105describe "checkbox_field (basic)" do 
     106  include TagMatchers 
     107   
     108  it "should return a basic checkbox based on the values passed in" do 
     109    checkbox_field(:name => "foo", :checked => "checked").should match_tag(:input, :class => "checkbox", :name => "foo", :checked => "checked") 
     110  end 
     111end 
     112 
     113describe "checkbox_control (data bound)" do 
     114  it_should_behave_like "FakeBufferConsumer"   
     115     
     116  it "should take a string and return a useful checkbox control" do 
     117    form_for :obj do 
     118      checkbox_control(:baz).should match_tag(:input, :type =>"checkbox", :name => "obj[baz]", :class => "checkbox", :value => "1", :checked => "checked") 
     119      checkbox_control(:bat).should match_tag(:input, :type =>"checkbox", :name => "obj[bat]", :class => "checkbox", :value => "0") 
     120    end 
    68121  end 
    69122   
     123  it "should render controls with errors if their attribute contains an error" do 
     124    form_for :obj do 
     125      checkbox_control(:bazbad).should match_tag(:input, :type =>"checkbox", :name => "obj[bazbad]",  
     126        :class => "error checkbox", :value => "1", :checked => "checked") 
     127      checkbox_control(:batbad).should match_tag(:input, :type =>"checkbox", :name => "obj[batbad]",  
     128        :class => "error checkbox", :value => "0")         
     129    end 
     130  end 
     131     
    70132end 
     133 
     134describe "hidden_field (basic)" do 
     135  include TagMatchers 
     136   
     137  it "should return a basic checkbox based on the values passed in" do 
     138    hidden_field(:name => "foo", :value => "bar").should match_tag(:input, :type => "hidden", :name => "foo", :value => "bar") 
     139  end 
     140end 
     141 
     142describe "hidden_control (data bound)" do 
     143  it_should_behave_like "FakeBufferConsumer" 
     144     
     145  it "should take a string and return a useful checkbox control" do 
     146    form_for :obj do 
     147      hidden_control(:foo).should match_tag(:input, :type =>"hidden", :name => "obj[foo]", :value => "foowee") 
     148    end 
     149  end 
     150   
     151  it "should render controls with errors if their attribute contains an error" do 
     152    form_for :obj do 
     153      hidden_control(:foobad).should match_tag(:input, :type =>"hidden", :name => "obj[foobad]", :value => "foowee", :class => "error") 
     154    end 
     155  end 
     156     
     157end 
     158 
     159describe "radio button (basic)" do 
     160  include TagMatchers 
     161  it "should should return a basic radio button based on the values passed in" do 
     162    radio_field(:name => "foo", :value => "bar").should match_tag(:input, :type => "radio", :name => "foo", :value => "bar") 
     163  end 
     164end 
     165 
     166describe "radio button groups (data bound)" do 
     167  it_should_behave_like "FakeBufferConsumer" 
     168   
     169  it "should return a group of radio buttons" do 
     170    form_for :obj do 
     171      radio = radio_group_control(:foo, [:foowee, :baree]).scan(/<[^>]*>/) 
     172      radio[0].should match_tag(:input, :type => "radio", :name => "obj[foo]", :value => "foowee", :selected => "selected") 
     173      radio[1].should match_tag(:input, :type => "radio", :name => "obj[foo]", :value => "baree") 
     174      radio[1].should not_match_tag(:selected => "selected") 
     175    end 
     176  end 
     177end 
     178 
     179describe "submit_button" do 
     180  it_should_behave_like "FakeBufferConsumer" 
     181     
     182  it "should produce a simple submit button" do 
     183    submit_button("Foo").should == "<button type=\"submit\">Foo</button>" 
     184  end 
     185end 
  • plugins/merb_helpers/specs/spec_helper.rb

    r710 r731  
    33require 'rubygems' 
    44require 'merb' 
     5 
     6module TagMatchers 
     7  class MatchTag 
     8    def initialize(name, attrs) 
     9      @name, @attrs = name, attrs 
     10    end 
     11 
     12    def matches?(target) 
     13      @errors = [] 
     14      unless target.include?("<#{@name}") 
     15        @errors << "Expected a <#{@name}>, but was #{target}" 
     16      end 
     17      @attrs.each do |attr, val| 
     18        unless target.include?("#{attr}=\"#{val}\"") 
     19          @errors << "Expected #{attr}=\"#{val}\", but was #{target}" 
     20        end 
     21      end 
     22      @errors.size == 0 
     23    end 
     24     
     25    def failure_message 
     26      @errors[0] 
     27    end 
     28  end 
     29   
     30  class NotMatchTag 
     31    def initialize(attrs) 
     32      @attrs = attrs 
     33    end 
     34     
     35    def matches?(target) 
     36      @errors = [] 
     37      @attrs.each do |attr, val| 
     38        if target.include?("#{attr}=\"#{val}\"") 
     39          @errors << "Should not include #{attr}=\"#{val}\", but was #{target}" 
     40        end 
     41      end 
     42      @errors.size == 0 
     43    end 
     44     
     45    def failure_message 
     46      @errors[0] 
     47    end 
     48  end 
     49   
     50  def match_tag(name, attrs) 
     51    MatchTag.new(name, attrs) 
     52  end 
     53  def not_match_tag(attrs) 
     54    NotMatchTag.new(attrs) 
     55  end 
     56end 
     57 
     58class FakeModel 
     59  def self.columns 
     60    [FakeColumn.new(:foo, :string),  
     61     FakeColumn.new(:foobad, :string),        
     62     FakeColumn.new(:bar, :integer),  
     63     FakeColumn.new(:barbad, :integer),       
     64     FakeColumn.new(:baz, :boolean), 
     65     FakeColumn.new(:bazbad, :boolean), 
     66     FakeColumn.new(:bat, :boolean), 
     67     FakeColumn.new(:batbad, :boolean) 
     68     ]      
     69  end 
     70   
     71  def valid? 
     72    false 
     73  end 
     74   
     75  def errors 
     76    FakeErrors.new(self) 
     77  end 
     78   
     79  def foo 
     80    "foowee" 
     81  end 
     82  alias_method :foobad, :foo 
     83   
     84  def bar 
     85    7 
     86  end 
     87  alias_method :barbad, :bar 
     88   
     89  def baz 
     90    true 
     91  end 
     92  alias_method :bazbad, :baz 
     93   
     94  def bat 
     95    false 
     96  end 
     97  alias_method :batbad, :bat 
     98end 
     99 
     100class FakeModel2 < FakeModel 
     101 
     102  def foo 
     103    "foowee2" 
     104  end 
     105  alias_method :foobad, :foo 
     106   
     107end 
     108 
     109class FakeErrors 
     110   
     111  def initialize(model) 
     112    @model = model 
     113  end 
     114   
     115  def on(name) 
     116    name.to_s.include?("bad") 
     117  end 
     118   
     119end 
     120 
     121class FakeColumn 
     122  attr_accessor :name, :type 
     123  def initialize(name, type) 
     124    @name, @type = name, type 
     125  end 
     126end 
     127 
     128describe "FakeBufferConsumer", :shared => true do 
     129  include TagMatchers 
     130   
     131  before :each do 
     132    @obj = FakeModel.new 
     133    def _buffer(buf = "") @buffer ||= "" end     
     134  end 
     135end