2011-01-08 11 views
22

Sto cercando di creare un semplice view helper ma non appena provo nido un paio di tag di contenuto si getterà content_tag nidificati getta metodo non definito `` output_buffer = a semplice aiutante

NoMethodError: undefined method `output_buffer=' for

def table_for(list, &proc) 
    t = Table.new 
    proc.call(t) 
    t.render_column(list) 
end 

class Table 
    include ActionView::Helpers::TagHelper 

    attr_accessor :columns, :block 

    def initialize 
    @columns = Array.new 
    end 

    def col(name) 
    @columns << name 
    end 

    def render_column(list) 
    content_tag :table do 
     list.each do |c| 
     content_tag :td, c 
     end 
    end 
    end 
end 

Eventuali suggerimenti di cosa c'è che non va? Ho anche visto che c'è un XmlBuilder è quello migliore per il mio scopo?

risposta

-3

Con l'aiuto di Nested content_tag throws undefined method `output_buffer=` in simple helper ho finito con la seguente soluzione ispirata alla API per Formtastic.

<%= table_for(@users) do |t| %> 
    <% t.col :name %> 
    <% t.col :email %> 
    <% t.col :test, :value => lambda { |u| u.email }, :th => 'Custom column name' %> 
    <% t.col :static, :value => 'static value' %> 
<% end %> 

Utilizzando l'output_buffer direttamente e probabilmente reinventare la ruota il codice è simile

module ApplicationHelper 
    def table_for(list, &block) 
    table = Table.new(self) 
    block.call(table) 
    table.show(list) 
    end 

    class Column 
    include ActiveSupport::Inflector 

    attr_accessor :name, :options 

    def initialize(name, options = {}) 
     @name = name 
     @options = options 
    end 

    def td_value(item) 
     value = options[:td] 
     if (value) 
     if (value.respond_to?('call')) 
      value.call(item) 
     else 
      value 
     end 
     else 
     item[name] 
     end 
    end 

    def th_value 
     options[:th] ||= humanize(name) 
    end 
    end 

    class Table 
    include ActionView::Helpers::TagHelper 

    attr_accessor :template, :columns 

    def initialize(temp) 
     @columns = Array.new 
     @template = temp 
    end 

    def col(name, options = {}) 
     columns << Column.new(name, options) 
    end 


    def show(list) 
     template.content_tag(:table) do 
     template.output_buffer << template.content_tag(:tr) do 
      columns.collect do |c| 
      template.output_buffer << content_tag(:th, c.th_value) 
      end 
     end 
     list.collect do |item| 
      template.output_buffer << template.content_tag(:tr) do 
      columns.collect do |c| 
       template.output_buffer << template.content_tag(:td, c.td_value(item)) 
      end 
      end 
     end 
     end 
    end 

    end 
end 
2

penso che ci sono stati alcuni cambiamenti su questo in 3.0, ma nelle versioni precedenti il ​​trucco è stato quello di passare self:

def table_for(list, &proc) 
    Table.new(self) 
    # ... 

def initialize(binding) 
    @binding = binding 
    #... 

def render_column 
    @binding.content_tag :table do 
    # ... 
    end 
end 

io non sono sicuro se questo è ancora come si fa a rotaie 3.

Un'altra cosa da risolvere in ordere affinché il codice funzioni è salvare l'output del contenuto content_tag da qualche parte, come con each il contenuto viene generato e quindi scartato. Una delle possibili soluzioni:

def render_column(list) 
    @binding.content_tag :table do 
    list.inject "" do |out, c| 
     out << @binding.content_tag(:td, c) 
    end.html_safe 
    end 
end 
+0

Passando sé al tavolo ha aiutato, ma ho trovato difficile capire/leggere passare come un doppio blocco all'iniezione. – orjan

57

ActionView :: Base ha costruito in esso il modulo Context, che fornisce i metodi output_buffer() e output_buffer = ().

Così si può risolvere il problema sia di avere la classe fare:

include ActionView::Context 

O ancora più semplicemente:

attr_accessor :output_buffer