The Next Evolution in Mongrel is Thin

Update: Thin, Mongrel and all of the variants have been replaced by a much better solution.

The Quick Background
First there was Zed’s excellent (and much appreciated) baseline work that establish Mongrel as the front runner in ruby hosting. Something I’ve been using for well over a year in production behind nginx. From that baseline evolved a few hacks: Evented Mongrel and Swiftiply Proxy.

The problem is that they were just that — Hacks that monkey patched the source code. Bye-bye needed updates. As much as I liked the perf gains from running them, it was not a great production solution.
Continue reading “The Next Evolution in Mongrel is Thin”

Compile errors installing ruby-xslt and libxml-ruby gems

I was setting up a new server for a client yesterday and was beating my head getting a few gems install.If you are having issues installing the ruby-xslt and ruby-xml gems on linux (I was on CentOS 5.1), make sure the developer libraries of libxml and xslt are installed. I had the rpms installed, but not the developer versions.
Continue reading “Compile errors installing ruby-xslt and libxml-ruby gems”

Mongrel Security Alert

Make sure you install the latest 1.0.x or 1.1.x release of Mongrel. There is a security hole in the DirHandler that allows read access to the file system.

sudo gem install mongrel

You should be running at least 1.05 or 1.1.3.

Per Zed and others on the mailing list, here are the details:

1) If you use nginx or apache (and maybe other full web servers with aproxy module) then you can wait to upgrade, but probably not verylong. This is because these servers do their own checking as well, andare handling your files. That means a request for the file will bedropped, and blocked.

2) If you use a pure TCP/IP based proxy balancer (balance, pen,swiftiply?) then you must upgrade as these do no checks on the incomingTCP packets.

3) If you use mongrel directly to serve content then you must upgrade.

Outputting custom model attributes with to_json

I’m working on a project where I need to convert a model attribute on the fly from XML to XHTML. However, when I tried to drop it out using to_json on the object array, it didn’t display! Here’s how you can output an attr_reader method.

Here is the info from the model:

class Section < ActiveRecord::Base

after_find #this forces it to run anytime a find is called (pre-building it)attr_reader :content

def content=(value)

@content = value

end

def content

@content

end

def after_find

xml = self.content_xml

xslt = XML::XSLT.new()
xslt.xml = REXML::Document.new( “<?xml version=\”1.0\”?><book>\r\n#{xml}</book>” )
xslt.xsl = REXML::Document.new( File.new( “#{RAILS_ROOT}/db/xslt/to_xhtml.xsl” ) )

out = xslt.serve()
xhtml = Hpricot.XML(out)

self.content = xhtml.at(“body”).innerHTML

end

end

If you put the returned section array into yaml – y(@section), you’ll see it doesn’t show up in the attributes, but in the methods. So in reality meth_reader would be a more accurate name — but I digress. 🙂

Now here is how you make it work. Note the :methods => :content option. That’s what I couldn’t figure out.

@section = Section.find(:all, :select => “section_number, content_xml”, :conditions => “translation_id = #{translation_id} and section_number > #{section_number}”, :order => ‘section_number’, :limit => 15)

respond_to { |format|

format.html {}
format.xml { render :xml => @section.to_xml(:only => :section_number,:methods => :content) }
format.js { render :json => @section.to_json(:only => :section_number, :methods => :content) }

}

Disclaimer: This is based on Rails 2.0 RC1.

Enabling PHP5 on OS X 10.5 Leopard

Leopard (OS X 10.5) ships with PHP 5.2.4 (cli) installed along side Apache 2.2.6. However, it’s disabled by default. Here’s how to enable it.

Open Terminal and choose your editor (vi, pico or other) and edit the following (sudo required!):

/etc/apache2/httpd.conf

Then search for the word php.It will find the line

LoadModule php5_module libexec/apache2/libphp5.so.

Uncomment this and save.At the command prompt site

sudo apachectl restart

That’s it!

A full rails stack with RM-Install on Ubuntu

Updated: I’ve added info to modify the /etc/profile script to load the path vars for all users.

Five Runs has just released an installer for automating the installation process of a full rails stack based on either a development or production environment. Included in the install are:

  • Ruby 1.8.6
  • Rails 1.2.3
  • MySQL 5.0
  • SQLite 3.3
  • Subversion 1.4
  • Apache HTTP Server 2.2 (Production mode)
  • OpenSSL
  • ImageMagick 6.3
  • Mongrel/Mongrel Cluster
  • Capistrano
  • Gruff
  • Rake
  • RMagick

The install is a binary executable that will run command line or as a gui application. There are currently two versions: linux and OS X 10.4.x.

To get started, I’ve setup Ubuntu 6.06 LTS server in Parallels on my Mac Book Pro. Note that I used that alternate install disc instead of the server or desktop discs. There is a known issue with the server disc that causes it to hang on reboot. You can grab the desktop(live cd), server or alternate disc here: http://osmirrors.cerias.purdue.edu/pub/ubuntu-releases/6.06.1/

If you choose the alternate disc, simply enter server at the command prompt to install it. Using the defaults you’ll be in great shape.

Once you are setup, login to you ubuntu server via the terminal or SSH. You’ll need to download rm install now. Skipping registration, the download page is here: http://www.fiveruns.com/products/rm/install/download. Be sure to choose the linux version. Here is the version as of me writing this:

wget http://www.fiveruns.com/downloads/rminstall-1.0.1-linux.bin
chmod 755 rminstall-1.0.1-linux.bin
sudo ./rminstall-1.0.1-linux.bin

Be sure to sudo when running the script or it will drive you crazy why you can’t setup Apache on port 80.

Here’s where the boring part comes in. With the command line install you have to page (via the enter key) WAY TOO MUCH to read all of the EULAs! I just held down the enter key until I saw a yes/no accept prompt, and hit yes.

Depending on your install you’ll choose the developer or server install. I’ve used the server install here.

From there, I chose to install to /opt/rminstall/ rather than the default ~/rminstall. It makes more sense to me to have it in a convenient shared place that is common to every server or desktop install.

The server install includes mongrel cluster and apache with the load balancing option all setup, where the developer path does not. Other than that, they are identical.

Once installed, they recommend that you setup a .proflie to auto load the paths correctly. Ubuntu (Debian as well) uses a .bash_profile instead, so it’s slightly different. You’ll need to run this for all users who need access.

echo "source /opt/rminstall/scripts/setenv.sh" >> ~/.bash_profile

Update: You can also add this to the /etc/profile file to make it load for all users!

sudo vi /etc/profile

At the top of the file, put the following 3 lines:

if [ -f /opt/rminstall/scripts/setenv.sh ]; then
. /opt/rminstall/scripts/setenv.sh
fi

You can now logout and log back in and you’ll be able to type ruby -v, rails -v or other commands such as mysql and rake.

This really is a blazingly fast install (< 10 minutes in a VM!). Configuring ImageMagick has always been a pain in the ass and this just makes life easy. Check it out. It works great!

Using Google Apps Mail with Rails for Great Deliverability

If you have your domain email hosted with Google, it’s easy to get much better delivery rates using thier servers than those on your webserver. Here’s how to do it using Ruby on Rails.

One of the rails projects I’m working on recently has been having issues sending email from our web servers. Some 80% of mail was getting marked as SPAM, despite having the reverse DNS entries, SPF record and SMTP port open to verify.

The domain we were using is hosted by Google using thier Google Apps for small business service. On a whim, we decided to try sending directly through Google to see what happened.

We used the convenient write up at Ruby Inside for sending via TLS encryption, which is required by Google. It worked like a charm and the deliverability issues went a way.

However, there is one issue with doing so. Regardless of the from address you specify, it always sends as the email address you use to login to the server. Google deliberately modifies the from address. It’s not all bad though. It protects their deliverability rates with other ISPs and keeps spam way down.

The only way around this is to run overrides in each of the notifiers you want to use a different account for auth. Had they allowed it to come from any valid account in the domain that word be much, much easier, but they don’t. Sorry kids.

One more thing I couldn’t find online, but found by trial and error was how to specify the from name when sending using ActionMailer. Most spam filters knock for not having this information. Here’s how to do it:

[source:ruby]
def some_notification(message)

recipients message.email
from “Your Name
subject “Welcome to YourDomain.com”
body “This is where the body of the email goes.”
end
[/source]

Auto-generated sitemaps.xml file for Mephisto

Make your Mephisto powered site easily spidered by Google, Yahoo and MSN. Enter the sitemap.xml feed.

Mephisto (using 7.3 edge) has some nice built in atom feeds. I could not however figure out how to get a full site feed. Everything was by section. Specifically, I wanted a feed that followed the sitemaps.org specification that Google, MSN and Yahoo use to spider the site.

Kudos to Joseph Moore for doing 90% of the work. His original version used a Google version of the spec (0.84) and put everything to http://mydomain.com/sitemap/ but the spiders look for sitemap.xml unless told otherwise.

Here are the changes I made to /app/views/sitemap/index.rxml

[source:ruby]
# see https://www.google.com/webmasters/tools/docs/en/protocol.html
# http://www.sitemaps.org/protocol.php
xml.instruct! :xml, :version=>”1.0″, :encoding=>”UTF-8″
xml.urlset(:xmlns => “http://www.sitemaps.org/schemas/sitemap/0.9”) do
time_zone = TimeZone.new(@site.timezone.current_period.utc_offset)

# Priority is a relative weighting, the default is 0.5 if not specified. 0.0 – 1.0
# give priority to the homepage (daily, 1.0)
# give priority to the subdirectories (daily, 0.8)
# else priority to the articles based on age
# >= 1 day = 0.9 and weekly
# >= 1 week = 0.8 and weekly
# >= 1 month = 0.5 and monthly
# >= 6 month = 0.3 and yearly

# give priority to the homepage (daily, 1.0)
xml.url do
xml.loc(“http://#{request.host_with_port}#{request.relative_url_root}/”)
xml.lastmod(Date.today.strftime(“%Y-%m-%dT%H:%M:%S#{time_zone.formatted_offset}”))
xml.changefreq(“daily”)
xml.priority(“1.0”)
end

@sections.each do |section|
if section.name.downcase != “home” then #exclude the home page!
xml.url do
xml.loc(“http://#{request.host_with_port}#{request.relative_url_root}/#{Section.permalink_for(section.name.to_s)}”)
#fudge the date to be recent.
xml.lastmod((Date.today-1).strftime(“%Y-%m-%dT%H:%M:%S#{time_zone.formatted_offset}”))
xml.changefreq(“daily”)
xml.priority(“0.8”)
end
end
end

@articles.each do |article|
xml.url do
xml.loc(“http://#{request.host_with_port}#{request.relative_url_root}#{site.permalink_for(article)}”)
xml.lastmod(article.updated_at.strftime(“%Y-%m-%dT%H:%M:%S#{time_zone.formatted_offset}”))

age = (Date.today – Date.parse(article.updated_at.to_s)) % (60*60*24)
if age >= 180 then
xml.changefreq(“yearly”)
xml.priority(“0.3”)
elsif age >= 30 then
xml.changefreq(“monthly”)
xml.priority(“0.5”)
elsif age >= 7 then
xml.changefreq(“weekly”)
xml.priority(“0.8”)
else
xml.changefreq(“daily”)
xml.priority(“0.9”)
end
end
end
end
[/source]

Here are the changes I made to /app/controllers/sitemap_controller.rb to support the section lookup.

[source:ruby]
class SitemapController < ApplicationController
layout nil
session :off

def index
@sections = site.sections.find(:all)
@articles = Article.find(:all, :conditions => “published_at is not null”)
end

end
[/source]

I’ve also added an additional route to lib/mephisto/routing.rb to recognize http://mydomain.com/sitemap.xml.

[source:ruby]
def self.connect_with(map)

# Allows access to the sitemap!
map.connect ‘sitemap’, :controller => ‘sitemap’
map.connect ‘sitemap.xml’, :controller => ‘sitemap’


[/source]

When I get the chance I’ll make this a plugin. Very handy.

Update:

I forgot the instruct line for the charset in the sitemap.rxml file. I’ve added it above.

This site is iPhone optimized!

The great little iWPhone WordPress Plugin and Theme automatically reformats your blog’s content for optimized viewing on Apple’s iPhone, and it works great.

I came across a great little plugin from Content.Robot that will optimize your WordPress blog for the iPhone when it detects the user agent string.

This site is iPhone optimized!

WARNING: When you activate the plugin it sets your theme to the default WordPress theme. You’ll simply need to go to Presentation and click to activate the theme you want.

Also, I have not tested this with wp-cache. I have it disabled so I can get cleaner stats (no admins).

iPhone Tip – Using Safari Bookmarks

You can utilize a Safari bookmarks folder as your main iSafari bookmarks. This allows you to easily keep things differentiated between your computer and iPhone.

I picked up my iPhone about 5 days ago and have already been compiling a listing of helpful resources.

For starters, I’ve created an iPhone folder in my Safari bookmarks that I keep iPhone sites in. iSafari always goes back to the last bookmark folder making it easy to find them quickly. Here are some of the links I have.

I also have a bunch of internal resources here so I can quickly hit our monitoring software. They are full blown web apps and not iPhone specific. They were unusable on my T-Mobile MDA (Windows Mobile 5), but work great on the iPhone.