Rendering Partials within a Radius Tag

In an extension I’m working on, I’d like to define some medium to large
blocks of dynamic html to be inserted via a single radius tag. Because
this html isn’t just a line or two and is tied to models and their data,
I’d love to be able to create a true rails partial and tell the snippet
to behave as a pseudo-controller and render the view in place of the
radius tag.

I’m trying to keep a separation of concerns here and keep my code clean
(and make use of all the html/erb sugar already built into rails).

I don’t suppose that there’s any way to have a tag call ‘render :partial
=> …’ much less get around the rails double-render when the
SiteController tries to render. Clever thoughts anyone?

-Chris

Chris P. wrote:

In an extension I’m working on, I’d like to define some medium to large
blocks of dynamic html to be inserted via a single radius tag. Because
this html isn’t just a line or two and is tied to models and their data,
I’d love to be able to create a true rails partial and tell the snippet
to behave as a pseudo-controller and render the view in place of the
radius tag.

I’m trying to keep a separation of concerns here and keep my code clean
(and make use of all the html/erb sugar already built into rails).

I don’t suppose that there’s any way to have a tag call ‘render :partial
=> …’ much less get around the rails double-render when the
SiteController tries to render. Clever thoughts anyone?

-Chris

Hi Chris,

I’ve a simple example how you can do this:

Model:

require ‘radius’
class Content < ActiveRecord::Base
def body
context = Radius::Context.new do |c|
c.define_tag ‘render’ do |tag|
content = “[render]=” + tag.attr[‘partial’] +
“”
end
end
parser = Radius::Parser.new(context, :tag_prefix => ‘r’)
return parser.parse(‘

hello <r:render
partial=“partials/google_adsense” />

’)
end
end

View:

<% @content.body.split("").each do |body| %> <% if body.include?("[render]=") %> <%= render :partial=>body.split("[render]=")[1] %> <% else %> <%= markdown(body) %> <% end %> <% end %>

Maybe i could help you

Hi Chris,

Now that Alexandro brings this up again, I have managed to add the
ability to truly render a partial inside a tag, model or anywhere else
you’d need to do it. It is, however, part of an extension with loads
of other stuff in it, and it requires some ugly hacks. Amongst other
things, Radiant has to be hacked to set the active controller, request
and response on tags.globals, so they are available to initialize an
ActiveView::Base instance to render a partial inside the tag.

If you are interested, I could provide the required code for in a
pastie, but you might want to stick with Alexandros solution which
seems less hacky, albeit not bullet proof. (How do you for instance
pass data to the partial with this approach?)

Cheers,
Casper F.

@Alexlandros - when I get some free time in the next couple of days, I’m
going to take a look at your solution. It’s not exactly the direction I
need (I have tags like <r:my_tag name=“some_name”> which spits out a big
block of html with data from my a model. But I’m not trying to let the
user render a partial by name. But the foundation is still the same so
I’d like to understand it better - maybe we’ll talk off the list.

@Casper - I’m not into hacks much but I would be interested in any
suggestions/improvements. Also, I didn’t feel like I needed another
controller since it would only be finding and creating a model to pass
to the view (pretty simple). But that doesn’t mean being able to trigger
another controller wouldn’t be useful (I’ve often wanted something sort
of like the now deprecated Rails ‘components.’ They were bad practice
there but in this environment where there’s a SiteController overseeing
all, being able to chain a 2nd controller into the mix would be nice).
If you’d be willing to email me a pastie directly I’d appreciate it.

Thanks both of you.

And, for the record, I came up with a solution that I was ok with (and
light on hacks). Here’s what I did:

[In the Model]

tag “my_tag” do |tag|
@my_tag_data = MyModel.find_by_name(tag.attr[‘name’])
parse_template ‘path_for/_my_template’
end
end

private

def parse_template(filename)
require ‘erb’
template = ‘’
File.open("#{MyExtensionNameExtension.root}/app/views/" + filename +
‘.html.erb’, ‘r’) { |f|
template = f.read
}
ERB.new(template).result(binding)
end

[In
/vendor/extensions/my_extension_name/app/views/path_for/_my_template.html.erb]

<%= @my_tag_data.name -%>

Value from my instance variable: <%= @my_tag_data.value -%>

So you can see that I chose to follow the Rails 2.0 template naming
rules of ‘*.html.erb’ (since John’s been making progress towards a 2.0
capable Radiant- yay!). I used ERB with binding so I was able to pass
instance variables to the template (something I needed).

This obviously isn’t full-on rails parsing so it doesn’t auto-mixin
helpers or anything fancy like that (I suppose I could require
ActiveView::Base or some such thing) but I didn’t need that much
capability.

-Chris

Chris P.'s solution to rendering partials within a Radius tag works
great. It’s been almost a year since he posted it though, and I was
wondering if this functionality should be done a different way now –
i.e. does Radiant has a built-in way to handle this now?

Thanks,

  • Dave

Chris P. wrote:

[In the Model]

tag “my_tag” do |tag|
@my_tag_data = MyModel.find_by_name(tag.attr[‘name’])
parse_template ‘path_for/_my_template’
end
end

private

def parse_template(filename)
require ‘erb’
template = ‘’
File.open("#{MyExtensionNameExtension.root}/app/views/" + filename +
‘.html.erb’, ‘r’) { |f|
template = f.read
}
ERB.new(template).result(binding)
end

[In
/vendor/extensions/my_extension_name/app/views/path_for/_my_template.html.erb]

<%= @my_tag_data.name -%>

Value from my instance variable: <%= @my_tag_data.value -%>

This obviously isn’t full-on rails parsing so it doesn’t auto-mixin
helpers or anything fancy like that (I suppose I could require
ActiveView::Base or some such thing) but I didn’t need that much
capability.

-Chris

David, I still use that code in my extensions today (though I’d be
interested to hear if anyone out there has come up with something
better).

Now that Haml is built into Radiant, it would also be safe to convert to
using that for your templates (it might be even easier to do the binding
bit but I haven’t tried yet).

-Chris

Chris, what can I say, your solution is better and cleaner than mine.
My solution lets the partial render in the context of the
SiteController, with the exact same possibilites as when rendered the
standard way, but I think I’ll switch to yours anyway and get rid of
some my ugly hacks and overrides.

Thanks for providing your solution - I’d provide mine, if I thought it
would actually be a help to anyone, but I don’t :wink:

/Casper

Chris – any idea why I can’t use the h() method in my partials?

NoMethodError in SiteController#show_page
undefined method `h’ for #MyControllerName:0x3336ca8

  • Dave

Chris P. wrote:

David, I still use that code in my extensions today (though I’d be
interested to hear if anyone out there has come up with something
better).

That pattern seems like a sufficiently good paradigm that some sort of
convention should be created to do this. Perhaps

tag “my_tag” do |tag|
@my_tag_data = MyModel.find_by_name(tag.attr[‘name’])
tag.render
end
end

would automatically use view/tags/my_tag.haml.html or something similar.

Chris P. wrote:

David, I still use that code in my extensions today (though I’d be
interested to hear if anyone out there has come up with something
better).

Now that Haml is built into Radiant, it would also be safe to convert to
using that for your templates (it might be even easier to do the binding
bit but I haven’t tried yet).

Here’s a HAML version that works, up to a point. It’s not really much
use, though: out of context like this you can render haml and
interpolate variables but that’s about it. You can’t render another
partial, for example, or call any helper methods. Perhaps someone more
up on scopes and evaluation can make it work?

def parse_template(filename, locals = {})
require ‘haml/engine’
haml_engine =
Haml::Engine.new(File.read("#{SomeExtension.root}/app/views/" + filename

  • ‘.html.haml’))
    haml_engine.to_html(Object.new, locals)
    end

best,

will

Is anyone aware of any progress that has been made in this area? I find
myself needing to do the same thing.

Thanks,
Wes

William Ross wrote:

Here’s a HAML version that works, up to a point. It’s not really much
use, though: out of context like this you can render haml and
interpolate variables but that’s about it. You can’t render another
partial, for example, or call any helper methods. Perhaps someone more
up on scopes and evaluation can make it work?

There is another possibility, that allows using @variables and render
:partials:

params = {:id => 15}

env = {
  'REQUEST_METHOD' => 'GET',
  'REQUEST_URI' => '/search/details',
  'action_controller.request.request_parameters' => params
}
r = SearchController.new
r.process(ActionController::Request.new(env), 

ActionController::Response.new, :details).body

and then implement search_controller as usual, but it looks like you
need to call render :action => ‘details’ explicitly.

I ended up using Cells (GitHub - trailblazer/cells: View components for Ruby and Rails.) to handle
this.

In my xxx_extension.rb, file I needed to do the following in order to
allow the PageController to handle cells:

#Modify view paths for ::Cell::Base to include local cell view
directories
::Cell::Base.view_paths += [CELL_PATH, “#{CELL_PATH}/layouts”]

#Add CELL_PATH to Rails $LOAD_PATH
$LOAD_PATH.unshift CELL_PATH

#Require each Cell class
CELL_FILES.each {|f| require f}

and I added a custom “cell” tag to handle rendering of cells:

desc %{
For use with projects that use the Cells gem
(GitHub - trailblazer/cells: View components for Ruby and Rails.)

  Renders the cell specified in the @name@ attribute _outside_ of 

the context of a page. The cell name and view are specified in the
appropriate attributes,
and local data required for rendering the view may be provided via
additional tags, which will be passed through to cell in its @opts hash.

  *Usage:*

  <pre><code><r:cell name="cell_name" view="view_name" 

data_param_1=“x” data_param_2=“y”/>
}
tag “cell” do |tag|
if tag.attr[‘name’] && tag.attr[‘view’]
name, view = tag.attr.delete(‘name’), tag.attr.delete(‘view’)
tag.locals.page.response.template.controller.render_cell(name,
view, tag.attr)
else
raise TagError.new(“cell' tag must contain name’ and `view’
attributes”)
end
end