Mongrel and simple-rss, makes great duct tape
Posted by Jason, Thu Jan 11 14:13:00 UTC 2007
Lately it seems like I have been building a lot of applications for various clients which build upon open source projects. When a client comes to me and says that they need an e-commerce solution, being a ruby fan I of course launch into my sales pitch on how Substruct can save the world. When they bring up how they also would like to have a collection of images available for browsing I tend to steer them in the direction of Snaps. Of course RForum and Typo are solutions not to be forgotten. It seems that with a little CSS hackery you can pretty easily mask the front-end of all these applications to look the same, and using webrick its easy to build them in parallel on different ports. When all of the individual applications are finished, you now want to wrap everything together so that it appears to be one application, without the nightmare of actually trying to merge these code bases together. This is how I went about doing so..Lets start with Mongrel. The day I discovered Mongel was the last time I ever said the "f" word. That being fastcgi of course. With an all http environment, its really easy to get used to not having to deal with that mysterious black box that fastcgi really is. Mongrel makes things easy. You can fire up each application, running in production mode with something like:
/usr/bin/ruby1.8 /usr/bin/mongrel_rails start \
/var/www/myapp/blog -d false -e production -P \
/var/www/myapp/blog/tmp/pid.txt -p 4245
This will start three Mongrel processes (default) running on port 4245 for the myapp application. You would want to do this for every application that you are trying to wrap together. If you don't want to run three processes, you can also tell Mongrel to fire up one, or two, or ten.. Then, you would set up the following in your Apache (2.2) virtual container, with the appropriate rewrite rules to forward things accordingly:
ServerName blog.myapp.com
ServerAlias blogs.myapp.com
UseCanonicalName On
ServerAdmin example@example.com
DocumentRoot /var/www/myapp.com/blog/public
SetEnv RAILS_ENV production
Options ExecCGI FollowSymLinks
AllowOverride all
Order allow,deny
Allow from all
ErrorLog /var/www/myapp.com/blog/log/production.log
ServerSignature On
RewriteEngine On
RewriteRule ^/$ /index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ http://localhost:4245%{REQUEST_URI} \
[P,QSA,L]
In the above example, I am wrapping the Typo blog, these rewrites will set the URL to be something like http://blog.myapp.com. You would do the same thing for all of the other applications that you are wrapping, so you could also end up with a http://forum.myapp.com and maybe a http://gallery.myapp.com, with in my case the Substruct store being http://www.myapp.com. Of course you would set all of these applications to run on separate ports, the above example shows the blog running on port 4245. You could set the gallery on 4240, and maybe the forum on 4242.. You get the idea..
Now that you have all of your pieces "Mongreled" together, you may run into a situation where one of these applications needs to get data from another. What if your client wants to have the last two Typo posts appear on the front page of the store application? I am sure that there are a few ways to approach this problem, but the solution I went with was RSS. Rails makes RSS easy, and the simple-rss plugin makes it even easier. You can install this as a gem, and it will set up everything in the plugins directory for you. So if you wanted to display the last two Typo posts on the Substruct home page, inside the Substruct application type:
gem install simple-rss
This will install the simple-rss plugin, which will allow you to make something like the following in your view to capture the XML stream from Typo and display it on Substructs home page. Your view code or partial may look something like:
show_by_name.rhtml
<% if @rss %>
<%= link_to "Latest articles from The Blog",
@rss.channel.link %>
<% if @rss.items[0] %>
<%= link_to
@rss.items[0].title, @rss.items[0].link %>
<%= @rss.items[0].description %>
Posted on: <%= @rss.items[0].updated %>
<% end %>
<% if @rss.items[1] %>
<%= link_to
@rss.items[1].title, @rss.items[1].link %>
<%= @rss.items[1].description %>
Posted on:
<%= @rss.items[1].updated %>
<% end %>
<% end %>
Inside the controller, this would get the job done:
content_nodes_controller.rb
# Shows an entire page of content by name
def show_by_name
### RSS
@blog_rss_url = Setting.find(:first, :conditions =>
["title = ?","blog_rss_url"])
begin
@value_url = open(@blog_rss_url.value)
@rss = SimpleRSS.parse @value_url
rescue
@rss = nil
end
if @content_node == nil then
render :file => "#{RAILS_ROOT}/public/404.html",
:layout => false, :status => 404
return
end
# Set a title
if @content_node.title.blank? then
@title = @content_node.name.capitalize
else
@title = @content_node.title
end
# Render special template for blog posts
if @content_node.is_blog_post? then
render(:template => 'content_nodes/blog_post')
else # Render basic template for regular pages
render(:layout => 'main')
end
end
You must also require the plugin in your environment.rb file. In Substruct, open up your vendor/plugins/substruct/config_stubs/environment.rb file and add:
# For RSS feed reader in main page
require 'simple-rss'
require 'open-uri'
On the Typo side of things, you need to define a method in your xml_controller (Typo already conveniently has lots of various XML support) to export the XML for Substruct to read. Something like this should work:
def latest_feeds
@articles = Article.find(:all, :limit => 2,
:order => "published_at DESC")
end
We only want the latest two feeds, so we have our controller conditions like so. This will generate a latest_feeds.xml file inside Typo's app/views/xml directory there for the reading.. Bling! That wasn't so hard.. You could expand upon this a lot, as XML can be used for all sorts of stuff, check out REXML if you want to broaden your horizons a little..
Cool stuff ha?