AJAX Controller/View Methods/Helpers in Extension/Tag Block


#1

So I’ve got this project where I would like Radiant to render the
initial page, but do subsequent updates to various divs via AJAX. I
decided this because I don’t want a separate but identical layout files
within Radiant and the app/views/layout of my extension. I won’t be the
one to use this CMS once it is installed and if the user has to come
back to me for minor layout changes once things are in place, that won’t
be nice.

I created an extension with the idea that I would create a tag for each
div and define them to render the same partials that my the AJAX calls
return, just with some default parameters. I imagined I would use
render_component or maybe render_partial but quickly found there is no
controller to make these accessible from the tag definition context.
Without partials and without helpers like form_remote_tag and
link_to_remote, I found myself putting lots of raw html and javascript
(Prototype Ajax.request methods) in each tag block. Pretty nasty, so I
scrapped that.

Another solution would be the ability to render snippets from my
controller instead of the usual views. I’ve looked at Radiant on Rails
but support for processing Radiant tags while doing such output is still
a work in progress. Even if tag processing is finished soon, there is
still the issue of getting to Prototype/AJAX helper methods into those
snippets. I really like the idea of using snippets via my controller
though, if it were combined with an extension that exposes rails
Form/Prototype/Javascript helpers via Radiant Tags, that could maybe
work, without introducing that complexity into the core Radiant code.

For example, instead of my controller/view outputting an entire AJAX
sortable table, if I could define it as a snippet w/ the appropriate
AJAX helpers in place as Radiant tags, the end user could then tweak
considerably more, adding/dropping columns or changing class/id/css
information. I’ve already created radiant tags to access and iterate
over rows in my model so I think this would be a nice fit.

Which brings me back to my original approach, accessing rails
view/controller helpers from an extension tag block. I’ve read a few
posts where people were trying to do this, but they all have their
problems. Below are my experiences with various ways I’ve tried to get
around this.


Mental Tags - FormTagHelper?
http://www.ruby-forum.com/topic/104454#232664

This could allow tags to use rails helpers, thought it may be buggy. But
without the ability to render snippets from my controller, it is only a
half solution as my the AJAX responses from my controller will need to
output snippets, which RadiantOnRails does, but without processing
Radiant tags, so it would only work for the initial request served up by
Radiant. I could use a separate partial for the AJAX responses but I
don’t want my user making changes in the CMS and finding the AJAX
responses look different.


ActionView helpers in a behavior - I think I got it!
http://www.ruby-forum.com/topic/73437

Same issues as above, possible bugs, no controller access to output
snippets after tag expansion.


RJS/Tracking Controller through Radiant Extension?
http://www.ruby-forum.com/topic/107811#244757

Requires modifying a page’s process method to look for AJAX requests,
but I thought my extension’s controller can already receive requests
directed to it, so I don’t see the point.


[Radiant] Can you use controllers/views within a tag
http://lists.radiantcms.org/pipermail/radiant/2007-February/003407.html

Ahh, bring a controller instance into my Tag module/mixin. This may be
my best solution at this point. This would allow me to render entire
divs using single tags that output partials using my controller - the
same partials that my AJAX responses will continue to return. No need to
import rails view helpers into my tag module, and no need to render
snippets from the controller with tag processing. Not as as flexible as
I would like, but this approach doesn’t require me to duplicate the site
layout in app/views/layouts. The sample code in the above post doesn’t
work as is, it needed a few changes, which the the guys in #ruby-lang
help me come up with. Posted below, with asterisks by the added/changed
lines -

def activate

  • require ‘application’

  • SiteController.class_eval do
    def show_uncached_page(url)
    puts ‘!!!in show_uncached_page’
    @page = find_page(url)

unless @page.nil?
@page.process_controller(self)
@cache.cache_response(url, response) if live? and @page.cache?
@performed_render = true
else
render :template => ‘site/not_found’, :status => 404
end

  rescue Page::MissingRootPageError

redirect_to welcome_url
end
end

  • Page.class_eval do
    attr_accessor :controller

    def process_controller(controller)
    @controller = controller
    process(controller.request, controller.response)
    end
    end

Page.send :include, MemberDirectoryTags

end

So after that, from your tag definition, you just call -

tag.globals.page.controller.render_component_as_string :controller =>
“member_directory”, :action => “sortable_table”

Right? Nope. render_component_as_string is protected.


So I tried something different -

From my controller (MemberDirectoryController) -
def sortable_table_as_string
render_component_as_string :controller => “member_directory”,
:action => “sortable_table”, :params => { :person => “david” }
end

From my tag definition -
tag ‘member_test_tag’ do |tag|
@controller = MemberDirectoryController.new
puts @controller
@controller.sortable_table_as_string
end

well when I use <r:member_test_tag /> this displays in my browser -
can’t dup NilClass

Through using some output statements I found this happens during
render_component_as_string in my controller. I’m guessing the controller
needs more initialization than just calling new on it.


And finally I tried…
(asterisks on lines added over prior example)

def activate

require 'application'

SiteController.class_eval do

  def show_uncached_page(url)

puts ‘!!!in show_uncached_page’
@page = find_page(url)

unless @page.nil?
@page.process_controller(self)
@cache.cache_response(url, response) if live? and @page.cache?
@performed_render = true
else
render :template => ‘site/not_found’, :status => 404
end

  rescue Page::MissingRootPageError

redirect_to welcome_url
end

  • def unprotected_render_component_as_string(options)
    
  • render_component_as_string options
    
  • end
    

    end

    Page.class_eval do
    attr_accessor :controller

    def process_controller(controller)
    

    puts ‘!!!in process_controller’
    @controller = controller
    process(controller.request, controller.response)
    end
    end

    Page.send :include, MemberDirectoryTags

    end

Like my past attempts but I attempted to get around the protected
render_component_as_string and half-initialized controller by adding a
public instance method to SiteController called
unprotected_render_component_as_string(options) to expose the protected
method.

And in my tag definition file I put:
tag.globals.page.controller.unprotected_render_component_as_string
:controller => “member_directory”, :action => “sortable_table”

The <r:member_test_tag /> produced the error “interning empty string”

Below is the framework trace -
/usr/lib/ruby/gems/1.8/gems/radiant-0.6.1/lib/login_system.rb:16:in
intern' /usr/lib/ruby/gems/1.8/gems/radiant-0.6.1/lib/login_system.rb:16:inauthenticate’

Looks like an authentication issue? I don’t really know.

Well, that is it. I’ve outlined this quest to get things working with
AJAX so it will be available as a use case, and maybe help the devs
decide how to go forward with official changes to accommodate such apps.
I very much like Radiant for non-ajax use, I’ll be keeping an eye on it
for future projects as well.

If someone could point out how to access render_component_as_string from
my tag definition I’d be grateful. I’ve got to move on with this project
so I will be doubling up on my layouts for now, but if there is a way,
I’d like to know.


#2

On Jun 14, 2007, at 10:42 PM, Lindsay Pallickal wrote:

So I’ve got this project where I would like Radiant to render the
initial page, but do subsequent updates to various divs via AJAX.

Wow. That’s a long post, and I confess that I cannot read it fully at
the moment.

I would point you to the work I was doing during RailsConf: http://
dev.radiantcms.org/svn/radiant/trunk/extensions/forms. There is a
file in there, lib/forms/tags.rb, which shows how one can do anything
with Rails view helpers in Radiant tags. Be sure to also look at the
files lib/ext/page.rb and lib/ext/site_controller.rb, as some
modifications to Radiant were necessary, which I expect to push back
into core.

Fun stuff.

aiwilliams


#3

Adam W. wrote:

I would point you to the work I was doing during RailsConf: http://
dev.radiantcms.org/svn/radiant/trunk/extensions/forms. There is a
file in there, lib/forms/tags.rb, which shows how one can do anything
with Rails view helpers in Radiant tags. Be sure to also look at the
files lib/ext/page.rb and lib/ext/site_controller.rb, as some
modifications to Radiant were necessary, which I expect to push back
into core.

Very nice, this is a big step in the right direction. To make things
truly DRY for an AJAX application though, this should be combined with
the ability to render snippets from an extension controller. I mentioned
this in my post above, and Radiant on Rails allows this, but without tag
processing/expansion. AJAX responses are dynamic content, so without the
ability to use tags, controllers will still have to rely on regular
app/view partials for responses, instead of snippets that can be
customized by the CMS user.

For now I guess I’ll use these helpers in a tag to generate some
javascript that will load the dynamic content from my controller/view
with the page load. I suppose I could have done that without these
helpers, but they should make it cleaner and easier. This solution will
have to load some kind of default view if the user doesn’t have
javascript though, so it still won’t be totally DRY as non-javascript
users will have their content generated through tags and not the
controller/view.

But I won’t need two sets of layouts now, just the ones entered into
Radiant, so it it will be safe for the CMS user to tweak that without
controller/view portions of the site going out of sync.

Helpful, thanks.

-Lindsay


#4

Adam W. wrote:

On Jun 14, 2007, at 10:42 PM, Lindsay Pallickal wrote:

So I’ve got this project where I would like Radiant to render the
initial page, but do subsequent updates to various divs via AJAX.

Wow. That’s a long post, and I confess that I cannot read it fully at
the moment.

I would point you to the work I was doing during RailsConf: http://
dev.radiantcms.org/svn/radiant/trunk/extensions/forms. There is a
file in there, lib/forms/tags.rb, which shows how one can do anything
with Rails view helpers in Radiant tags. Be sure to also look at the
files lib/ext/page.rb and lib/ext/site_controller.rb, as some
modifications to Radiant were necessary, which I expect to push back
into core.

Fun stuff.

aiwilliams

I know this is a fairly old post, but this is exactly what I have been
looking for. Can someone point me to the examples? Or is there perhaps a
better way the solve this problem nowadays?