Displaying articles with tag

New Hosting / Server OS

Posted by Matt White, Thu Dec 13 20:10:00 UTC 2007

After running nearly two years on a Rimuhosting VPS with Debian Sarge, we decided to switch over to a Rimuhosting Miro4 on CentOS 5 with Plesk 8.2 and do some hosting. Since it seems that since we’re the primary tech support contact for our clients regardless of the fact that most of them have direct relationships with their web hosts, we’ve decided to start hosting client sites ourselves (when appropriate), on our VPS. VPS hosting for Rails sites is usually a better solution anyway, since nearly every shared host I’ve attempted to host a Rails app on until now has either been very slow, or very unreliable.

Overall, it’s been a lot of fun getting things up and running, and it’s been quite painless. Plesk automates a lot of the tasks that I had to do manually on Sarge, a lot of which I really didn’t want to know about anyway. (Such as editing dovecot config files manually… ugh)

I was a bit concerned about how to host Rails apps with Plesk, and I’m pleasantly surprised at how easy it is. CentOS5 ships with Apache 2.2, with it’s awesome mod_proxy_balancer. It’s definitely not the highest-performance solution available, but it’s easy to set up, and it integrates well with the Plesk workflow. This blog is running on two mongrels with mongrel_cluster, and balanced with mod_proxy_balancer. I simply had to set up a vhost.conf file based on the setup provided by Coda Hale for my subdomain, get mongrel_cluster configured to match, and I was done. There’s a couple other steps in there, which I’ll detail in a future post.

IMAP Problems

The only real problem I’ve experienced is Apple Mail’s rebellion against the IMAP namespace structure of Courier IMAP, which is the default mail server with Plesk. Apparently under the IMAP spec mail servers are free to choose a namespace separator as well as a namespace prefix. This can be autodiscovered during the HELO process with the server, but Mail ignores this. Mail DOES allow you to set an IMAP prefix under the Advanced account settings tab, which on Courier servers is INBOX. Setting that is the first step, which will get Mail to correctly pick up the Sent and Trash folders on the server. However, there is still difficulty when you wish to create new “Mailboxes” on the account, which is Mail’s term for subfolders. In this case, if you wish to create a first-tier subfolder under the account (i.e. Account Name/New Folder) you must specify the mailbox name as “INBOX.New Folder” to correctly set it up. However, for subfolders underneath this new folder (i.e. Account Name/New Folder/New Subfolder), you simply add a new Mailbox underneath New Folder as you would expect, with no need to specify any prefix.

0 comments | Filed Under: | Tags:

MediaTemple (gs) Automated Mongrel Restart

Posted by Matt White, Fri Dec 07 10:45:00 UTC 2007

I’ve got a couple of small Rails sites hosted on MediaTemple’s (gs) product, and I’m happy to say that it’s been a positive experience. A lot of people complained about the product early on, but MediaTemple has made a lot of reliability improvements, and I’ve had nothing but a good experience so far with it.

However, the one problem is that the base Rails container, which runs Mongrel, is pretty limited on memory, and it seems that the mongrel process will just get killed if it goes over it’s limit. I’ve had this happen a couple of times where the process gets killed without being restarted, and then the site is down until I call “cap restart” or restart it via SSH. This obviously isn’t an acceptable solution, so I hacked together a little Ruby script that will check your site to see if it’s alive, and if not it will restart it. Schedule it via cron, and you’re ready to go!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#!/usr/bin/ruby
require 'net/http'
require 'uri'

site = "your_site_id"
application = "your_application_name"
friendly_name = "Your Site's Actual Name (used only for notification)"
webpath = "http://www.yourdomainname.com/"
user = "serveradmin%yourdomainname.com"
password = "yourpassword"

url = URI.parse(webpath)
req = Net::HTTP::Head.new(url.path)

res = Net::HTTP.start(url.host, url.port) do |http|
  http.request(req)
end

unless res.is_a? Net::HTTPOK
  puts "Detected Site Down: Restarting #{friendly_name}..."
  `mtr restart #{application} --username #{user} --password #{password}`
end

One More Thing

To actually call the script, you have to initialize your PATH variables, since cron doesn’t run your .bash_profile script before it runs your job. I created a .sh file to call my ruby script that sets the correct paths before calling the script:

1
2
3
4
5
6

#!/bin/sh
export PATH=$PATH:/usr/local/bin/

cd $HOME/data/
./babysitter.rb

I called the sh file babysitter.sh, and the ruby script babysitter.rb, chmod +x on both of them, added babysitter.sh as a 5 minute cron job, and forgot about it. Periodically I’ll get a cron email letting me know that it’s restarted the app, though it doesn’t happen very often.

Hopefully this will help someone!

0 comments | Filed Under: | Tags:

XML.load and Content-Encoding

Posted by Matt White, Tue Mar 06 09:22:00 UTC 2007

I was having a rather perplexing problem with Flash in IE6 the other day, and I found very little to help me until I stumbled upon this gem. We have some flash elements on a site that we built that make use of XML loaded out of a Rails app via ActiveRecord::Base.to_xml. I was using the basic Nginx config file put together by Ezra for proxying to a mongrel_cluster, which works great overall. However, as mentioned in that blog entry, IE6 doesn’t like compression or no-cache headers on XML data.

Fortunately Nginx allows you to set the header types that will have gzip compression applied, using the “gzip_types” directive in the config file. So, just make sure that your actions that generate xml have the “Content-Type: text/xml” header applied, and then remove the “text/xml” directive from the list of gzip_types in your Nginx config file.

Unfortunately you can’t use the handy-dandy Rails respond_to function, because Flash fails to correctly set the Request-Type header to text/xml. So, my only solution was to come up with xml actions to parallel with my view actions so that any request to that action would receive xml regardless of the Request-Type.

So, make sure that any actions that emit xml for Flash consumption set the following:

1
2

@headers['Content-Type'] = "text/xml"

And you should be good to go!

1 comment | Filed Under: | Tags:

Rails AJAX_OPTIONS :with

Posted by Matt White, Wed Feb 21 15:05:00 UTC 2007

The Problem

I’m writing an admin backend for a client, and I wanted to trigger an AJAX call based on the value of a SELECT field. The problem this presents compared to the normal Rails AJAX usage is that the value you want to send via AJAX isn’t specified at the time the page is rendered, and I didn’t want to serialize the entire form to get the correct value of one field.

The obvious candidate for a link to fire an AJAX call is link_to_remote, but it’s not entirely obvious how you can specify a request parameter that will be evaluated at the time of the click. Most of the time you set the parameters you wish to send back to the server in the URL, but that won’t work in this case because I don’t want to use a static URL.

.h2 The solution: ”:with” So, there’s a handy option to most of the PrototypeHelper functions that’s a bit poorly documented (in my opinion, anyway). :with allows you to specify the parameters part of the Ajax.Request call. This will be evaluated on the fly when the onclick is fired, and is submitted via POST. (Unless you are setting the postBody parameter as well.) The best part is that Prototype will serialize standard JavaScript object notation (read: JSON) into a URL-encoded POST body. Not too clear? Try this:


link_to_remote("Set Position", 
           :url => {:action => "load_map", :id => @listing},
           :with => "{map_id:$F('listing_map_id')}")

This will yield the following HTML/JavaScript:


<a href="#" onclick="new Ajax.Request('/property/show_map/1', 
           {asynchronous:true, evalScripts:true, 
           parameters:{map_id:$F('listing_map_id')}}); 
           return false;">Set Position</a>

Which will, when clicked, generate an AJAX call with the following POST body:

map_id=1

(assuming the value of my dropdown is 1.) Also, note that $F is a Prototype shortcut that allows you to grab the value of a form field based on the ID you pass in. An extremely handy function…

So, this is a really great way to add an arbitrary number of parameters to an AJAX call using any Javascript variable, which could be the position of an element, value of a form field, the list goes on!

0 comments | Filed Under: | Tags: