I'm trying to call a view helper from a custom RedCloth tag and am having absolutely no luck at all. All of the examples I can find have the custom tag method create and return a literal string without calling any other method at all.
I am trying to create a custom RedCloth tag to output a weekly schedule; the code for which has already been written in a helper as it is also used in other areas of the application.
So here's the (massively reduced) code that my problem boils down to:
app/views/page/show.html.erb:
<%= raw redcloth @page.content %
app/helpers/pages_helper.rb:
module PagesHelper
def redcloth(content)
r = RedCloth.new content
r.extend ScheduleTag
r.to_html
end
end
app/helpers/schedule_tag.rb:
module ScheduleTag
include ScheduleHelper
def schedule(options)
build_schedule
end
end
app/helpers/schedule_helper.rb:
module ScheduleHelper
def build_schedule
content_tag :div do
content_tag :span,
"Why won't my helper just work by magic like the rest of " \
"rails seems to?"
end
end
end
When executed, rails claims the following:
TypeError in Pages#show
Showing /blah/blah/blah/app/views/pages/show.html.erb where line #1 raised:
can't convert Symbol into Integer
Extracted source (around line #1):
1: <%= raw redcloth @page.content %>
Rails.root: /blah/blah/blah
Application Trace | Framework Trace | Full Trace
app/helpers/schedule_helper.rb:3:in `build_schedule'
app/helpers/schedule_tag.rb:42:in `schedule'
app/helpers/pages_helper.rb:22:in `redcloth'
app/views/pages/show.html.erb:1:in _app_views_pages_show_html_erb___756530284_81829440__535015588'
app/controllers/pages_controller.rb:23:in `show'
I can make a little bit of progress by changing from using a helper to a class, but that class is still having issues using the standard helpers:
/app/helpers/schedule_builder.rb:
class ScheduleBuilder
extend ActionView::Helpers::TagHelper
def self.build_schedule
content_tag :div do
content_tag :span,
"Why won't my helper just work by magic like the rest of " \
"rails seems to?"
end
end
end
And rails says:
NoMethodError in Pages#show
Showing /blah/blah/blah/app/views/pages/show.html.erb where line #1 raised:
undefined method `output_buffer=' for ScheduleBuilder:Class
Extracted source (around line #1):
1: <%= raw redcloth @page.content %>
Rails.root: /blah/blah/blah
Application Trace | Framework Trace | Full Trace
app/helpers/schedule_builder.rb:5:in `build_schedule'
app/helpers/schedule_tag.rb:42:in `schedule'
app/helpers/pages_helper.rb:22:in `redcloth'
app/views/pages/show.html.erb:1:in `_app_views_pages_show_html_erb___756530284_81708830__535015588'
app/controllers/pages_controller.rb:23:in `show'
So what am I to do? Am I missing something completely obvious and ought to bury my head in shame, or is it simply not possible to call helpers from anywhere one fancies? Do I need to just suck it up and get the custom tag to duplicate all of the code in my ScheduleHelper module?
Any help would be greatly appreciated.
Okay, so I've come to an acceptable solution myself. So I'll be a good little boy and share.
Instead of a helper, I'm going to use a class that renders a partial, so the question should really become: "Calling partials from custom RedCloth tags".
(I've not commented the code, it should be pretty self documenting)
app/helpers/schedule_tag.rb:
module ScheduleTag
def schedule(options)
Schedule.new.render Date.today.monday
end
end
app/helpers/schedule.rb:
class Schedule < ActionView::Base
include ActionView::Helpers::TagHelper
include Rails.application.routes.url_helpers
def render week
self.view_paths = "app/views"
super :partial => "schedule/schedule",
:locals => { :week => week,
:schedule => ScheduledClass.get_schedule(week) }
end
end
app/view/schedule/_schedule.html.erb:
<table id="schedule">
<tbody>
<tr>
<td>Hoop-dee-doo, it works!</td>
<%= content_tag :td do %>
<%= link_to "so do tag & route helpers", happy_monkey_path(:happy => "very") %>
<% end %>
</tr>
</tbody>
</table>
As a slight aside, if you were to try this you may stumble upon the problem of RedCloth escaping your options string. Just enclose the options within ==
's to get it to pass the options literally through. eg schedule. =={"weeks":["this","next"]}==
. This may be a little on the dangerous side, so be sure to check for nastyness.