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

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"