2

I have a class

class DataListBuilder
    include ActionView::Helpers::TagHelper
    include ActionView::Helpers::CaptureHelper
    include ActionView::Helpers::UrlHelper
    attr_accessor :object, :output_buffer

    def initialize(object)
      @object, @output_buffer = object, nil
    end

    def column (&block)
      if block_given?
        content_tag(:li, block.call(self))
      else
        content_tag(:li, "")
      end
    end

    def options_column(&link_block)
      if block_given?
        content_tag(:li, content_tag(:dl, "<dt><a href='#'>&nbsp;</a></dt><dd><ul>#{link_block.call(self)}</ul></dd>".html_safe, :class=>'options'))
      else
        content_tag(:li, "")
      end
    end

    def link_item(title, url, options={})
      content_tag :li, link_to(title, url, options)
    end
  end

and calling it as

<%= l.options_column do |c| %>
        <%= c.link_item 'Show', lead_path(c.object) %>
        <%= c.link_item 'Edit', edit_lead_path(c.object) %>
        <%= c.link_item 'New Note', "leads/#{c.object.id}/notes/new", :class=>"display-newxdoc", :id=>c.object.id %>
        <%= c.link_item 'Create Opportunity', new_lead_opportunity_path(c.object) %>
    <% end %>

the desired output is

<li><dl class="options"><dt><a href="#">&nbsp;</a></dt><dd><ul style="display: none;">
    <li><a data-remote="true" class="plus" href="leads/details/309">&nbsp;</a></li>
    <li>3w</li>
    <li>Simon Wu</li>
    <li>1-714-553-0888</li>
    <li>omnisw@unifiedbeat.com</li>
    <li>Unified Beat</li>

        <li><a href="/leads/309">Show</a></li>
        <li><a href="/leads/309/edit">Edit</a></li>
        <li><a id="309" class="display-newxdoc" href="leads/309/notes/new">New Note</a></li>
        <li><a href="/leads/309/opportunities/new">Create Opportunity</a></li>

but it is generating

<li><a href="/leads/309">Show</a></li>
<li><a href="/leads/309/edit">Edit</a></li>
<li><a id="309" class="display-newxdoc" href="leads/309/notes/new">New Note</a></li>
<li><a href="/leads/309/opportunities/new">Create Opportunity</a></li>
<li><dl class="options"><dt><a href="#">&nbsp;</a></dt><dd><ul style="display: none;">
    <li><a data-remote="true" class="plus" href="leads/details/309">&nbsp;</a></li>
    <li>3w</li>
    <li>Simon Wu</li>
    <li>1-714-553-0888</li>
    <li>omnisw@unifiedbeat.com</li>
    <li>Unified Beat</li>

        <li><a href="/leads/309">Show</a></li>
        <li><a href="/leads/309/edit">Edit</a></li>
        <li><a id="309" class="display-newxdoc" href="leads/309/notes/new">New Note</a></li>
        <li><a href="/leads/309/opportunities/new">Create Opportunity</a></li>
</ul></dd></dl></li>
    </ul></dd></dl></li>

Can any one help me in it.

Complete code is listed here.

Nazar Hussain
  • 5,102
  • 6
  • 40
  • 67

2 Answers2

4

First of all we refactored your helper for more intensive usage of content_tag (just to get whats going in this code ^_^).

Next we add usage of output_buffer which was defined but not used at all in helper.

After it all methods wich should be called from erb should return nil so they didn't appear in HTML.

And last syntactic suger was usage of instance_eval so you don't need {|c| ...} style blocks. You can use all variables from DataListBuilder directly there.

module DataListHelper
  def list_headers(args=[])
    args    = Array.new(args)
    columns = []
    args.map { |o| columns << content_tag(:li, o.split(":").first, :style=>"width:#{o.split(":").second}px;") }
    content_tag(:ul, columns.join(" ").html_safe, :class=>"list-headers")
  end

  def data_list_total_records(array)
    content_tag(:div, page_entries_info(array).html_safe, :class=>"total-records")
  end

  def data_list_for(object, headers=[], &block)

    if object.is_a? Array
      if object.length == 0
        list_headers(headers).concat(content_tag(:strong, "<br />No records found".html_safe))
      else
        res_obj = data_list_total_records(object)
        res_obj << content_tag(:ol, :class=>"data-list") do
          res_ol = content_tag(:li) do
            res = list_headers(headers)
            object.each do |o|
              builder = DataListBuilder.new(o)
              res << content_tag(:li) do
                content_tag(:ul, :id=>o.id, :class=>"list-row #{cycle('odd', 'even')}") do
                  capture(builder, &block)
                  builder.output_buffer.html_safe
                end
              end
            end
            res
          end
          res_ol << data_list_pagination(object)
        end
        res_obj
      end
    else
      list_headers(headers).concat(content_tag(:strong, " <br />Not available."))
    end
  end

  class DataListBuilder
    include ActionView::Helpers::TagHelper
    include ActionView::Helpers::CaptureHelper
    include ActionView::Helpers::UrlHelper
    include Rails.application.routes.url_helpers

    attr_accessor :object, :output_buffer, :controller

    def initialize(object)
      @object, @output_buffer = object, ''
    end

    def column (&block)
      @output_buffer << if block_given?
        content_tag(:li, instance_eval(&block))
      else
        content_tag(:li, "")
      end
      nil
    end

    def options_column(&link_block)
      @output_buffer << if block_given?
        content_tag(:li) do
          content_tag(:dl, :class=>'options') do
            res = content_tag(:dt) do
              content_tag(:a, '&nbsp;'.html_safe, :href => '#')
            end
            res << content_tag(:dd) do
              content_tag(:ul) do
                instance_eval &link_block
              end
            end
          end
        end
      else
        content_tag(:li, "")
      end
      nil
    end

    def link_item(title, url, options={})
      content_tag :li, link_to(title, url, options)
    end
  end
end

And your view became this:

<%= data_list_for @leads, [" :10", "Age:30", "Contact:140", "Phone:140", "Email:180", "Company:100", ""] do |l| %>
    <%= l.column { link_to "&nbsp;".html_safe, "leads/details/#{object.id}", :class=>:plus, :remote=>true } %>
    <%= l.column { object.age } %>
    <%= l.column { object.contact.complete_name } %>
    <%= l.column { object.contact.phones.blank? ? "-" : object.contact.phones.first.phone_number } %>
    <%= l.column { object.contact.emails.blank? ? "-" : object.contact.emails.first.email } %>
    <%= l.column { object.company.title } %>
    <%= l.options_column do %>
        <%= link_item 'Show', lead_path(object.id) %>
        <%= link_item 'Edit', edit_lead_path(object.id) %>
        <%= link_item 'New Note', "leads/#{object.id}/notes/new", :class=>"display-newxdoc", :id=>object.id %>
        <%= link_item 'Create Opportunity', new_lead_opportunity_path(object.id) %>
    <% end %>
<% end %>
Viacheslav Molokov
  • 2,534
  • 21
  • 20
  • Really really really thanks. It works. and it also gave me complete insight of what is going on and inside. Now the only issue remains is that `lead_path(object)` and route methods are not working. It generates errors `ActionView::Template::Error (undefined method `lead_path' for #):`. This is the only thing remains in the list. Please help to solve it. – Nazar Hussain Apr 07 '11 at 07:02
  • @Nazar Huaain - I updated answer to solve your issue with links. Added `include Rails.application.routes.url_helpers` and attribute `controller` into `DataListBuilder`. Added `object.id` into view. http://stackoverflow.com/questions/341143/can-rails-routing-helpers-i-e-mymodel-pathmodel-be-used-in-models/5456103#5456103 for more info – Viacheslav Molokov Apr 07 '11 at 07:39
0

options_column is a "block view", I'd remove the = in <%= l.options_column do |c| %>, that's for sure. Then, I'd use concat in options_column instead of returning it directly as a string.

Anyway, the simple solution is here (I've used it, no problems at all):

https://github.com/markevans/block_helpers

tokland
  • 66,169
  • 13
  • 144
  • 170
  • It is really great. but i have question for it. How can we make such block helpers who iterate on the some collection. I can not found any example or way on the wiki of the helper you suggested. Please send me any example for it. – Nazar Hussain Apr 07 '11 at 12:46