Sucking in the header and footer from a remote site

Eric PughAugust 30, 2007

How do you share a common header and footer across multiple apps? For example, you might be prototyping a new app, and want to borrow the main sites header and footer. Obviously you can do crazy stuff like have JavaScript pull something via the client, or maybe have iframes or frames. But what if you have to massage the content as it comes in, maybe only keeping part of it?

Well fortunately Ruby on Rails makes that very easy. We simply created a Model object that uses HPricot to pull in remote content and grab the portion we want:

require 'hpricot&';
require 'open-uri'

class CatalogContent
cattr_accessor :remote_url

def self.footer
url = remote_url
raise "Remote URL was not set" if url.nil?

puts "\nPulling remote content footer from #{url}\n"

begin
doc = Hpricot(open(url))
footer_content = doc.at("//div[@id=footer]")
raise "Didn't find footer content in site #{url}" if footer_content.nil?

footer_content

rescue Errno::EHOSTUNREACH
footer_content = "Can't connect to #{url}"
rescue Errno::ETIMEDOUT
footer_content = "Can't connect to #{url}"
rescue SocketError
footer_content = "Can't connect to #{url}"
end
end
end

In this case we are loading up a website and pulling out the footer section in the HTML. We have the option here while manipulating the HPricot XML DOM to do any other types of changes we want such as:

  • Remapping URLS
  • Snipping out bits of content
  • Changing colors or stylesheet classes
  • Conditional inclusion of content

And then, we have a ApplicationHelper.rb method that creates the model object, populates the remote url and displays it:

def remote_footer
CatalogContent.remote_url = AppConfig.catalog_remote_url
CatalogContent.footer
end

Of course, people are going to jump on the fact that this means that every page is making an extra URL call right? Fortunately caching comes to the rescue in our layout:

<div class="footer">
  < % cache(:controller=> 'remote_content', :action => 'remote_footer') do %><br /> < %= render(:partial=>'/partial/bottom_panel') %><br /> < % end %>
</div>

Note, however, we don’t actually have a controller called remote_content with an action called remote_footer, you just have to have those bits so the caching support knows what keys to store the cached content under! Could have been:

< % cache(:controller=> 'foo', :action => 'bar') do %>
< %= render(:partial=>'/partial/bottom_panel') %>
< % end %>

And in our partial we just have:

< %= remote_footer %>

So, the only downside is that in development we are constantly pulling in the content because the caching is turned off, which slows things down a bit, and that the only way (out of the box) to update the cache is to “sweep” out the contents of the /tmp/cache directory.

Also, be aware that if what you are importing has lots of relative paths or references relative JavaScripts and CSS that you are going to have to make those parts work with your app nicely!




More blog articles:


Let's do a project together!

We provide tailored search, discovery and analytics solutions using Solr and Elasticsearch. Learn more about our service offerings