Running passenger with multiple different ruby versions apache nginx rvm

It’s becoming more and more of a requirement to run different apps under different versions of ruby
One one project I have needed to take the plunge with Ruby 1.9 for unicode support.

This is no big deal really because Passenger and Rails 3 are pretty stable on Ruby 1.9, however, I still have apps that need 1.8 for various reasons I wont go into.

As it stands, Passenger does not support running apps under different interpreters using the apache or nginx modules, I came across this article

I’m going to try and simplify by just giving installation instructions, but if you want a bit more in-depth info as to the reason for this process head on over to Phusion’s blog.

First of all your going to need RVM and passenger module installed on your apache/nginx instance, I’m going to assume you’ve already got this working and the reason your here is to use multiple different versions of the ruby interpreter with passenger.
NOTE: You should be using the interpreter used for the majority of your apps as the base apache or nginx passenger ruby

Lets assume we already have a working app using passenger with ruby 1.8.7 and we want to get another app running but using ruby 1.9.2

So here’s our basic configuration:

1
2
3
4
5
6
7
8
9
10
11
12
# Basic Apache configuration
PassengerRuby /home/user/.rvm/wrappers/ruby-1.8.7-p330/ruby

<VirtualHost *:80>
  ServerName foo.com
  DocumentRoot /home/user/apps/foo.com/public
</VirtualHost>

<VirtualHost *:80>
  ServerName bar.com
  DocumentRoot /home/user/apps/bar.com/public
</VirtualHost>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Basic Nginx configuration
passenger_ruby /home/user/.rvm/wrappers/ruby-1.8.7-p330/ruby

server {
    listen 80;
    server_name www.foo.com;
    root /home/user/apps/foo.com/public;
    passenger_enabled on;
}

server {
    listen 80;
    server_name www.bar.com;
    root /home/user/apps/bar.com/public;
    passenger_enabled on;
}

Now lets setup bar.com so that it’s using the ruby 1.9.2 interpreter

# setup 1.9.2 and run passenger standalone
$ rvm install ruby-1.9.2 # if you don't already have it
$ rvm use 1.9.2
$ cd /home/user/apps/bar.com
$ passenger start -a 127.0.0.1 -p 3000 -d

Update our web server config to use proxy pass

1
2
3
4
5
6
7
8
# Updated Apache configuration
<VirtualHost *:80>
    ServerName www.bar.com
    DocumentRoot /home/user/webapps/bar.com/public
    PassengerEnabled off
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>
1
2
3
4
5
6
7
8
9
10
# Updated Nginx configuration
server {
    listen 80;
    server_name www.bar.com;
    root /home/user/webapps/bar.com/public;
    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
    }
}

Deploying your app, bundle and reload your apache or nginx instance.
Then point your browser to `bar.com` If you’ve followed all the steps, you’ll have foo.com using 1.8.7 and bar.com using 1.9.2

Override ActionMailer recipient in Rails 2 and 3 for development or testing

If your like me, you might want enable delivering of mail in development, mostly because testing email layout is a pain without actually sending the email to a real client.

In doing this you’ll probably want to avoid the possibility of emails making their way to your customers from your development machine, this can be achieved with a simple initializer to override the destination.

Rails 2.x

1
2
3
4
5
6
7
8
9
10
11
12
if Rails.env.development?
  class ActionMailer::Base
    def create_mail_with_overriding_recipients
      mail = create_mail_without_overriding_recipients
      mail.to = "mail@robaldred.co.uk"
      #mail.cc =
      #mail.bcc =
      mail
    end
    alias_method_chain :create_mail, :overriding_recipients
  end
end

Rails 3.x

1
2
3
4
5
6
7
8
if Rails.env.development?
  class OverrideMailReciptient
    def self.delivering_email(mail)
      mail.to = "mail@robaldred.co.uk"
    end
  end
  ActionMailer::Base.register_interceptor(OverrideMailReciptient)
end

If you prefer to use a plugin, there is one called mail_safe personally, i think it’s a little overkill.

MySQL server has gone away with Rails, Sunspot and Cucumber

When using Rails, sunspot and cucumber with a mysql test database, I received the following the MySQL server has gone away error.
After some googling a came across the `reconnect` option for database config

1
2
3
4
Using the default profile...
Sunspot server is starting...
Sunspot server took 2.72 sec. to get up and running. Let's cuke!
Mysql::Error: MySQL server has gone away: SHOW TABLES (ActiveRecord::StatementInvalid)

Solution is to add

1
reconnect: true

to cucumber section of your database.yml

Thanks to: http://rywalker.com/

Adding additional configuration args to selenium rc using cucumber and webrat

I use cucumber, webrat and selenium RC a lot day to day, all are great pieces of kit, which make my life so much easier, but I’m always trying to find ways to make these ever easier.
Selenium RC has alot of options which are settable when booting the selenium-server java application.
Many of these options are not available when using selenium with cucumber and webrat.

I have raised an issue for webrat and produced a patch that enables you to pass additional arguments during the setup of your webrat instance, these are then passed on to the Selenium RC startup command.

Until this is prioritised and added to the webrat core, I have an on-the-fly solution that enables the additional args, the following code should be added to your `env.rb`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module Webrat
  class Configuration
    attr_accessor :selenium_additional_args
  end
end

module AdditionalArgs  
  def self.included(base)
    base.alias_method_chain :start, :additional_args
  end
   
  def start_with_additional_args
    remote_control.additional_args = Webrat.configuration.selenium_additional_args
    start_without_additional_args
  end
end

Webrat.configure do |config|  
  ...  
  config.selenium_additional_args = ["-firefoxProfileTemplate '../../Library/Application Support/Firefox/Profiles/selenium'", "-singleWindow"]
  ...
end

Webrat::Selenium::SeleniumRCServer.send(:include, AdditionalArgs)

Testing ActionMailer models with Rspec

You might think testing ActionMailer sounds like a challenge, actually it’s just as easy as testing other classes, you just have to know the methods to use to change the state/content of the deliveries.

Here we have a simple example of how you might test a mailer for your order receipt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
require File.dirname(__FILE__) + '/../spec_helper'

describe OrderMailer do
 
  describe "receipt" do
 
    before(:each) do
      @order = mock_model(Order)
      @order.customer = mock_model(Customer)
    end
 
    it "should render successfully" do
      lambda { OrderMailer.create_receipt(@order) }.should_not raise_error
    end
 
    describe "rendered without error" do
   
      before(:each) do
        @mailer = OrderMailer.create_receipt(@order)
      end
   
      it "should have an order number" do
        @mailer.body.should have_tag('.order_number', :text => @order.id)
      end
   
      it "should have order details" do
        @mailer.body.should have_tag(".order_details")
      end
 
      it "should have a billing address" do
        @mailer.body.should have_tag(".billing.address")
      end
   
      it "should have a delivery address" do
        @mailer.body.should have_tag(".delivery.address")
      end
 
      it "should have customer contact details" do
        @mailer.body.should have_tag("#contact_details") do
          with_tag('.landline', :text => @order.customer.landline)
          with_tag('.mobile', :text => @order.customer.mobile)
          with_tag('.email', :text => @order.customer.email)
        end
      end
 
      it "should have a list of items" do
        @mailer.body.should have_tag(".line_items")
      end
 
      it "should have totals" do
        @mailer.body.should have_tag(".totals") do
          with_tag('.sub_total', :text => format_price(@order.sub_total))
          with_tag('.vat', :text => format_price(@order.vat))
          with_tag('.postage', :text => format_price(@order.postage))
          with_tag('.total', :text => format_price(@order.total))
        end
      end
     
      it "should deliver successfully" do
        lambda { OrderMailer.deliver(@mailer) }.should_not raise_error
      end
     
      describe "and delivered" do
       
        it "should be added to the delivery queue" do
          lambda { OrderMailer.deliver(@mailer) }.should change(ActionMailer::Base.deliveries,:size).by(1)
        end
       
      end
   
    end

  end
 
end

rendering the following output:

ActionMailer test output from textmate rspec bundle
ActionMailer test output from textmate rspec bundle

Add https (ssl) support to your osx mac development machine with signed certificate

It became apparent that getting mod_ssl working correctly without browser warnings when developing sites that take payments is a bit of pain. Mainly because there is no free way to have a root authority sign your Certificate Signing Request (CSR).

There is how ever a short cut, given that you are using Apache, mod_ssl, openssl and Firefox.

We’re going to generate our own Certificate Authority (CA), this is CA is only going to work for us so if your generating a certificate for production, you’ll need to send your CSR to a proper CA such as VeriSign

Step1, Make a temporary folder we can work in.

cd ~/Desktop/ssltemp

Step2, generate our private key

openssl genrsa -des3 -out server.key 1024

You will be asked for a passphrase in the creation of this key. (just use 12345) or anything butdo not forget this passphrase! You’ll have to do this all over if you forget the passphrase. You will need this passphrase later on in the process.

Step3, generate a CSR from our private key

openssl req -new -key server.key -out server.csr

you’ll be asked for the following information:

Country Name (2 letter code) [AU]: (enter your country code here)
State or Province Name (full name) [Some-State]: (Enter your state here)
Locality Name (eg, city) []: (enter your city here)
Organization Name (eg, company) [Internet Widgits Pty Ltd]: (enter something here)
Organizational Unit Name (eg, section) []: (enter something here)
Common Name (eg, YOUR name) []: (this is the important one)
Email Address []: (your e-mail address)

Make sure you fill in `Common Name` with your domain you want this certificate for, this should match your apache vhost `ServerName` setting

Now, looking at the directory we’re working in, you should have the following:

[rob:~/Desktop/ssltemp] ls -la
total 12
drwxr-xr-x    5 rob      staff         126 Nov 14 17:01 .
drwx------   38 rob      staff       1248 Nov 14 16:57 ..
-rw-r--r--    1 rob      staff         729 Nov 14 17:01 server.csr
-rw-r--r--    1 rob      staff         963 Nov 14 16:59 server.key

Step4, create the private key for our CA

openssl genrsa -des3 -out ca.key 1024

Again, you’ll be asked for a passphrase, which, again, you should not forget.

Step5, create CA certificate using the key we just made

openssl req -new -x509 -days 365 -key ca.key -out ca.crt

You will be asked for similar information you were asked for when we make the web server certificate earlier; this information should be about you, enter something like the following

Country Name (2 letter code) [AU]:GB
State or Province Name (full name) [Some-State]:Cheshire
Locality Name (eg, city) []:Stockport
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My CA
Organizational Unit Name (eg, section) []:My CA for Dev
Common Name (eg, YOUR name) []:Rob Aldred
Email Address []:raldred@gmail.com

Now you will have 4 files your directory; server.key, server.csr, ca.key, ca.crt
Next is the important park, signing our certificate request.

The easiest way to do this is to use the sign.sh script contained in the mod_ssl source,
or you can get it here: sign.sh
copy the script to the working directory

Step6, make sign.sh executable and sign our CSR

chmod +x sign.sh
./sign.sh server.csr

you should get something like the following:

CA signing: server.csr -&gt; server.crt:
Using configuration from ca.config
Enter PEM pass phrase:
Check that the request matches the signature
Signature ok
The Subjects Distinguished Name is as follows
countryName           :PRINTABLE:'GB'
stateOrProvinceName   :PRINTABLE:'Cheshire'
localityName          :PRINTABLE:'Stockport'
organizationName      :PRINTABLE:'Testing'
organizationalUnitName:PRINTABLE:'Testing'
commonName            :PRINTABLE:'localhost'
emailAddress          :IA5STRING:'raldred@gmail.com'
Certificate is to be certified until Nov 14 23:09:20 2010 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
CA verifying: server.crt &lt;-&gt; CA cert
server.crt: OK

Answer ‘y’ to the question asking to Sign the certificate [y/n]

Step7, remove password requirement from server key

cp server.key server.key.original
openssl rsa -in server.key.original -out server.key

you be asked for the passphase

Step8, copy files to our webserver

sudo mkdir /etc/apache2/certs
sudo cp -r * /etc/apache2/certs/

Step9, add the configuration to your VirtualHost block listening on the SSL port 443

SSLEngine on
SSLCertificateFile "/etc/apache2/certs/server.crt"
SSLCertificateKeyFile "/etc/apache2/certs/server.key"
SSLCACertificateFile "/etc/apache2/certs/ca.crt"

Step10, Tell apache to listen on 443
By default there is a file in /etc/apache2/extras called httpd-ssl.conf
this needs to edited and included in /etc/apache2/httpd.conf its commented out initially.

Depending where you are defining your VirtualHost blocks
Comment out or remove the _default_ virtualHost block in httpd-ssl.conf, this will cause errors when starting apache because we have no configured certificate for the example apache provites

Edit your httpd.conf to include the etc/httpd-ssl.conf file, scroll to the bottom the file, you’ll notice its commented out at around line #476

# Secure (SSL/TLS) connections
# Include /private/etc/apache2/extra/httpd-ssl.conf

Just remove the # and move onto the next step

I use a seperate vhosts folder in extra, containing individual conf files for each virtualhost, they are included in the extra/httpd-vhosts.conf files using the following:

Include /private/etc/apache2/extra/vhosts/*.conf

Step10, restart apache

sudo apachectl restart

Step11, (a few steps in itself) Add our CA to Firefox so it think its a trusted authority
Go to Preferences (Cmd + ,)
Go to Advanced
Go to Encryption
Click ‘View Certificates’
Choose the ‘Authorities’ tab
Click ‘Import’
Hit Shift + Cmd + g to open the go to folder window
Enter ‘/etc/apache2/certs’ (You might be asked to authenticate with your system password)
Select the ca.crt file we generated earlier and click ‘Open’
Firefox will ask you:
Do you want to trust “My CA” for the following purposes?
Just select Trust this CA to identify websites
Click ‘OK’
Restart your browser

If you’ve followed everything correctly when you go to https://localhost (or whatever CommonName you specified)
You will get a ssl encrypted site and no warnings about the certificate not being trusted.

Example showing a local vhost with a verified cert
Example showing a local vhost with a verified cert

If apache doesn’t come backup then apache’s config checks program is your best friend.

/usr/sbin/httpd -S

Cucumber pretty html formatter for textmate

I’ve been using cucumber a lot recently along with the textmate bundle for it.
The HTML output cucumber creates is fine, but not all that pretty.

I’ve created a new formatter for cucumber to generate HTML enhanced with some javascript.
My inspiration came from the textmate bundle for rspec.

You can grap it off my github.
Here’s some screenshots:

UPDATE: I replaced the original html formatter in cucumber; as of 19th November my fork has been merged into Aslak’s cucumber/master upgrade to the cucumber 0.4.5 release.
View the commit history

Custom log files for your ruby on rails applications

Sometimes logging is required but putting the messages in the Rails log isn’t the answer.For example you need to see the progress of customers through your order placement cycle, you ideally need this seperate to any other in a custom log file?

There is a solution and it’s easy… To create an order progress log, simply create a new instance of Logger and pass it a File instance for your own logfile.
Create a new model which inherits from Logger

class OrderProgressLogger < Logger
  def format_message(severity, timestamp, progname, msg)
  "#{timestamp.to_formatted_s(:db)} #{severity} #{msg}\n"
  end
end

Create initializer called logs.rb in RAILS_ROOT/config/initializers with the following content:

order_progress_logfile = File.open("#{RAILS_ROOT}/log/order_progress.log", 'a')
order_progress_logfile.sync = true
ORDER_PROGRESS_LOG = OrderProcessLogger.new(order_progress_logfile)

After a restart of your mongrel or passenger ORDER_PROGRESS_LOG will be available through out your application.
You log to it just as you would DEFAULT_RAILS_LOGGER like so…

ORDER_PROGRESS_LOG.debug "Starting order placement method"
ORDER_PROGRESS_LOG.error "Could not create order record"