ERB in snippets or pages?

The short version: Is it possible to use ERB within Radiant snippets or
pages? (By filters maybe?)

The long version: I’ve a whole bunch of Rails applications within a same
domain including forum, wiki and so-on. They use a common template,
implemented by putting together a bunch of common partials, symlinking
to their physical location, then including those partials within the
default layout for each application. Repetition is minimised with in
general only application-specific variations in layouts. Ultimately
they’ll also share a secondary, common database with a superset of
authorisation/account details for single sign-on purposes.

Radiant is used for various reasons, for sections of the site that
aren’t publically editable. I’ve expressed the template partials through
snippets in Radiant with a default layout. It works well. I’ve extended
the snippet “new.rhtml” to include a “more/less”-style panel that takes
a filename; the database and table have an “auto_export” field validated
in the model; finally, a custom save method in the snippet controller
exports the snippet to the given filename if provided whenever that
snippet is updated.

This lets me export snippets as partials to the shared directory thus
providing control over the look and feel across multiple Rails
applications all from within Radiant. Only one problem; no ERB. The
snippets use it. In certain cases I’ve been able to hack an equivalent
using cunning but hideous arrangements of comments; application-specific
partials or snippets are included by commented-out ERB or Radiant tags,
then depending on whether Radiant or another app is running, one of
those two tags gets expanded so the snippet or partial gets included.
The included item closes off the open comment, does its thing, then
re-opens the comment again. I said it was hideous… Commented out bits
of ERB or Radiant tag end up in the served documents too, which is nasty
and it’s an extraordinarily limited approach compared to simply using
ERB directly.

Any recommendations - is this a job for a filter?

Andrew H. wrote:

The short version: Is it possible to use ERB within Radiant snippets or
pages? (By filters maybe?)

Any recommendations - is this a job for a filter?

A filter may not be the best choice here. Filters don’t have any concept
of where they are being used (the page or snippet). Instead they just
filter text. You could make an ERB filter work, but it wouldn’t know
anything about where it was being rendered.

You may need to resort to doing something with a behavior. Behaviors can
control every aspect of how a page is rendered (look at #process on
Behavior::Base). You should be able to add a hook in there for
evaluating ERB within the context of a page.


John L.
http://wiseheartdesign.com

John W. Long wrote:

A filter may not be the best choice here. Filters don’t have any concept
of where they are being used (the page or snippet). [ Use behaviors ]

Well, I’ve written a simple Filter very quickly that doesn’t have any
ERB context around it. ActionView uses much magic to get instance
variables into the ERB execution context so that you can use @session et
al in your .rhtml files. My Filter doesn’t have that, so it’s quite
limited although you can use self-contained ERB quite happily. The ERB
parser only works on the bit of text (Snippet, Part) that specifies such
a Filter should be run, by definition of how Filters are implemented.

I then worked on a Behavior, which has reached its first version that
really does inject the local variables in using a similar mechanism to
ActionView. This means I can do a footer snippet, say, which includes
this (ignoring the line wrapping); angle brackets removed and ‘href’
changed to ‘refh’ to try and get the RForum list gateway to stop
saying my message contains spam! Doh!

a refh=“W3C CSS Validator results for http: (CSS level 3 + SVG)<%=
url_encode(@request.protocol + @request.host + @request.request_uri)
%>” …link text…

That’ll produce a valid CSS validation (sic.) URL for any application
including Radiant. If included in a Part with the ERB Behavior, it gets
parsed. The Snippet or Part that includes such text must, of course, not
have any Filters defined which would destructively alter the ERB, so no
(say) Textile if you wanted ERB via the Behavior. Then again you can’t
have two Filters anyway; at least with an ERB Behaviour you could
additionally use a Filter which did not escape or otherwise mangle the
ERB code.

Now for the catch.

Provided all pages using the snippet (via a layout or directly) use the
ERB behaviour, all is well. However, what about a Not Found page or an
archive? That would probably use the same layout as other pages in a
site, which wants directly or via a Snippet to include ERB. But it
already has the Not Found Behavior, so it can’t have ERB Behavior too.
So, suddenly, I’m back at wanting an ERB filter; the fact that you use
ERB somewhere really is something specific to that block of text,
whereas a behavior seems to imply application of a concept globally, to
the whole compiled page.

I’ll look into what instance variables are available for Filter objects
tomorrow maybe, but for now you can look at the in-progress code here:

Pond's Place: Ruby software

Andrew H. wrote:

the whole compiled page.
You should consider overriding Behavior::Base#render_page in a plugin.
This would be the easiest way to give all pages and snippets global
access to ERB.


John

Oliver B. wrote:

What I have done in the past is to create a page with the particular
behavior and then create a snippet that includes the page, this snippet
can then be included into every other page, while original page with
the ERB behavior is executed in the ERB behavior context and the current
page you are looking at in its own behavior context.

This sounds potentially cunning but I don’t quite follow; how do you
include a particular page from a snippet? Is it possible to manage
layouts in this context, given the layout includes the ERB and parsed
layouts are included within the scope of Behaviors?

Of course, instead
of going the way through a snippet you can use <r:include> directly.

My installation of Radiant 0.50 doen’t recognise <r:include … /> in
pages or snippets.

On 16-Jul-2006 23:54 +0200, Andrew H. was heard to say:

John W. Long wrote:

A filter may not be the best choice here. Filters don’t have any concept
of where they are being used (the page or snippet). [ Use behaviors ]

Provided all pages using the snippet (via a layout or directly) use the
ERB behaviour, all is well. However, what about a Not Found page or an
archive? That would probably use the same layout as other pages in a
site, which wants directly or via a Snippet to include ERB. But it
already has the Not Found Behavior, so it can’t have ERB Behavior too.

What I have done in the past is to create a page with the particular
behavior and then create a snippet that includes the page, this snippet
can
then be included into every other page, while original page with the ERB
behavior is executed in the ERB behavior context and the current page
you
are looking at in its own behavior context. Of course, instead of going
the
way through a snippet you can use <r:include> directly.

Oliver

On 19-Jul-2006 15:05 +0200, Andrew H. was heard to say:

layouts in this context, given the layout includes the ERB and parsed
layouts are included within the scope of Behaviors?

Yes, that should be possible. You should be able to include the content
of
a specific page into a layout, while this included page is rendered
within
its own behavior context.

Of course, instead
of going the way through a snippet you can use <r:include> directly.

My installation of Radiant 0.50 doen’t recognise <r:include … /> in
pages or snippets.

Ooops, I ment <r:find><r:content/></r:find> so for example:

<r:find url="/search">
<r:content part=“form”/>
</r:find>

That is how I am including live search in my sidebar.

Cheers,
Oliver

You wrote:

What I have done in the past is to create a page with the particular
behavior and then create a snippet that includes the page, this snippet
can then be included into every other page, while original page with the
ERB behavior is executed in the ERB behavior context and the current page
you are looking at in its own behavior context.

Thanks for your help with this. Unfortunately I tried it using ‘find’ to
build what you suggest and it didn’t work. This is almost certainly
because a “Not found” behavior page gets automatically selected whenever
something else cannot be found. That page gets parsed, inserts a
snippet, the snippet inserts the actual “Not found” page - however, it
is the first page in that set which gets incorporated into a layout,
which means the ERB in the snippets that the layout includes does not
get executed since the first page does not have ERB behavior. It it is
impossible to get a page with the ERB behavior included first because
a “Not found” page is automatically chosen.

For other behavior types your suggestion should work though it will
always be the case that the first page in a set of pages is the one that
dominates the behavioral context since its layout will be the one that
is used for the final rendered result. Including page parts thereafter,
by any mechanism, does not include anything that goes around those
parts.

I still believe that the only way to achieve the specific result I
wanted originally would be to use a text filter but it would need
knowledge of the wider application environmet - @controller, @session,
@request and so-on - indeed, this thread is perilously similar in
fundamental concept to another one where somebody is trying to get
ActionController::Base included in a behavior so that Rails helper
methods are available to that behavior code. The argument seems to be
that behaviors and filters need access to application environmental
variables - if it’s good enough for .rhtml files in Rails applications
it ought to be good enough for page components in Radiant.

Given that Radiant is meant to be a lightweight CMS, of course, this
might be considered unnecessary application bloat. That would defeat
what I’m trying to do, but would be an entirely understandable position.

It should at least not be too big a job to modify my own Radiant
installation to pass similar variables to a Filter-derived class as are
already available to Behavior-derived classes; this wouldn’t let me do
Rails helper methods but would provide enough for the limited ERB I have
in mind.