posts

nested transactions

After an hour of head banging, well….banging our head trying to test that raising any error inside an ActiveRecord::Base#transaction block. It would appear that you can’t test a transaction in a test straight out the box. ActiveSupport::TestCase turns on transactional fixtures by default, in order that fixtures don’t get reloaded in every time a test is run. This makes sense and for this particular project there are silly amount of fixtures so this speeds up tests no end. However, this also means you can’t test a transaction inside a transaction because ActiveRecord doesn’t support nested transactions.

However, all is not lost you can turn off transactional fixtures on a test class by using the following.

self.use_transactional_fixtures = false

It’s a trade off on speed, so if you have a lot of tests it might be a better idea to move tests that rely on transactions working as they should into separate test classes.

Solution was found here: http://railspikes.com/2007/3/28/testing-transactions

Rendering different views depending on the hostname of your app

Quite an interesting problem. How to render different views depending on the hostname of your application. Well actually only certain views have to be overridden. The ideal behaviour would to be to set a search path based on the hostname of the application which then falls back to the default if no views exists.

Initially I looked at how I could force searching different view path and using that file if it already exists else use the default by overriding existing ActionView methods.

http://rpheath.com/posts/122-needing-to-extend-rails-view-path

However, this isn’t necessary as it appears Rails supports a method of adding another search path for views by pushing the custom view dir onto the view_paths array. This could be placed in config/initializers/action_controller_extender.rb and loaded as the application is started.

ActionController::Base.view_paths.unshift(File.join(RAILS_ROOT, 'app', 'views', 'custom_views'))

In order to fix this based on the hostname, its actually more convenient to move this into a before filter and on every request we can check the host header.

class ApplicationController < ActionController::Base

  before_filter :custom_view_path

  private

  def custom_view_path
    if ['www.zombification.net'].include?(request.host)
      self.prepend_view_path(File.join(RAILS_ROOT, 'app', 'views', 'zombification'))
    end
  end

end

Passenger 2.0.1 - installed

Just got around to installing Passenger 2.0.1 and I am just horrified at how easy it is, surely there should be some kind of rule about this. Its the little complexities and awkwardness that keep me in work.

gem install passenger; passenger-install-apache2-module

Running a basic install of LovdByLess at Zombie Crawl as a test for now. It seems rather slow to initialise, about 5 to 10 seconds on the first request. But then its relatively speedy.

I’m interested in following the progress on this one as I’ve heard about its potential application for shared hosting environments, although I’m not immediately convinced since it took a few reloads of apache to get all of the models loaded without `undefined constant` errors running a single site. So I can forsee the support for this with multiple sites on a single host being problematic.

Installing HAML plugin into your rails app

Just a quick one if you want to utilise HAML inside your rails app. Personally I find the HAML syntax far more readable than Erb

gem install haml; cd path/to/app/; haml --rails ./

dos2unix

I used this utility a few times many years ago and when I’ve come to use it again it does appear to be anywhere I can find it. I’ve since switched from Fedora to Ubuntu so perhaps that’s why. Its a nice little command line tool that strips out all the windows characters

apt-get install sysutils
.....
Setting up tofrodos (1.7.6-2) ...
.....

Not immediately obvious but this includes dos2unix.

Fixing Exim retry errors

We’ve been experiencing a problem sending mails between application servers, one server’s mail server has been unavailalbe for a number of days and as such all new mails to that box bounce. Even now that the server is available the same behaviour is being experienced. We need to clear the exim retry database for this particular server. The first problem is actually seeing the retry rules exim_dumpdb is used here.

root@server1:/# exim_dumpdb /var/spool/exim4 retry
  T:www.zombification.net:83.244.152.145 111 65 Connection refused
01-Aug-2008 12:26:13  06-Aug-2008 09:22:37  06-Aug-2008 15:22:37 *
  T:backup.zombification.net:83.244.152.146 113 77 No route to host
31-Jul-2008 17:20:22  05-Aug-2008 17:43:21  05-Aug-2008 23:43:21 *

Here we can see that retry times need to be cleared. Here I’ve used exim_tidydb to clear any rules that have been in place for more than one hour.

root@server1:/# exim_tidydb -t 1h /var/spool/exim4 retry
Tidying Exim hints database /var/spool/exim4/db/retry
deleted T:backup.zombification.net:83.244.152.146 (too old)
deleted T:www.zombification.net:83.244.152.145 (too old)

SEO urls on the cheap

RESTful rails apps don’t necessarily make your urls that friendly if you end up with

http://www.zombification.net/posts/1

An id isn’t very descriptive of the content of the post. The quick fix for was is to add the method Post#to_param

class Post < ActiveRecord::Base

  validates_presence_of :title
  validates_presence_of :content

  def to_param
    return "#{self.id}-#{self.slug}" 
  end

  def slug
    return self.title.clone.downcase.gsub(' ','-')
  end

end

to_param is defined in ActionController::Routing and is used when generating urls with link_to, etc, but only when you specify the object and not the id itself.

link_to :controller => 'posts', :action => :show, id => @post

If you use name routes this is all taken care of for you. At this point all the links for @post will be the more meaningful URL

http://www.zombification.net/posts/1-nested-transactions

This is not the whole story however, this works with minimal effort because when we get our post object back Post#find(params[:id]), params[:id] will be ‘1-nested-transactions’. However, ActiveRecord::Base#find calls #to_i, on the single argument. So as long as the id of the post in params[:id] appears first the result from ActiveRecord will be the same.

rob@rob-laptop:~/src/zombification$ ./script/console 
Loading development environment (Rails 2.1.0)
>> Post.find('1-random-meaningless-string')
=> #<Post id: 1, title: "nested transactions", content: "After an hour of head banging, well....banging our ...", published_at: nil, created_at: "2008-07-29 16:40:35", updated_at: "2008-08-16 07:51:57">

Hey presto slightly more SEO urls. Ok you still have the id floating about which is a little ugly but its certainly better.

Sharing git on a private network

If you want to share your git repos using the git protocol you can use the following

git-daemon --syslog --base-path=/home/rob/src --export-all --detach

This starts the git daemon in the background

rob@rob-laptop:~/src/hosting_admin$ netstat -ltanup | grep git
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:9418            0.0.0.0:*               LISTEN      19583/git-daemon
tcp6       0      0 :::9418                 :::*                    LISTEN      19583/git-daemo

It should then be a question of pulling from the remote location like so.

git pull git://192.168.1.120/repository

This doesn’t provide any security out of the box, so I wouldn’t recommend using this out in the wild but on a private network amongst your team this is an easy way to share your repo.