Paginating with ActiveRecord scopes in Rails3.

I think that will_paginate is great and all, but slightly overkill for some things, so here's a simple method to give you pagination via scopes in ActiveRecord:

class Post < ActiveRecord::Base
  scope :paginate, lambda{ |page,per_page| limit(per_page.to_i).offset((page.to_i-1)*per_page.to_i) }
end

In my controller I have:

@posts = Post.paginate(params[:page]||1,25)

Pagination links can be handled by your view easily enough.

30 Jun 10

Beautiful multipart emails with ActionMailer in Rails 3

With Rails3 ActionMailer has been given a bit of a makeover. Your mailer classes now go in app/mailers and the API has been changed, take this example from Kimono's welcome_email action:

class Notifier < ActionMailer::Base
  def welcome_email(user)
    @user = user
    @url = cluster_url(user.personal_cluster)
    attachments.inline['kimono-small-notext.png'] = File.read File.join(Rails.root, 'public/images/kimono-small-notext.png')
    attachments.inline['sociable-logo.png'] = File.read File.join(Rails.root, 'public/images/sociable-logo.png')
    @css = File.read(File.join(Rails.root, 'public', 'stylesheets', 'notifier.css')).gsub(/\/images\/([a-zA-Z\-\_]+\.png)/) do |match|
      attachments.inline[$1] = File.read File.join(Rails.root, 'public', 'images', $1)
      attachments.inline[$1].url
    end
    mail(:to => user.email, :subject => "Welcome to Kimono.")
  end
end

Much like in your controllers, instance variables are passed into views, and also similarly to controllers ActionMailer will automatically pick up templates of 'html' and 'text' types and inline them as parts in the multipart message. In the example above, you see that I set up a couple of instance variables for use in the templates, then attach a couple of images used in the HTML version of the message (more on this later). Because in an HTML email I cannot use <link rel="stylesheet" /> to load the CSS for the message I need to inline it inside a <style> tag. I'm using sass to generate a file called notifier.css using sass's compressed style which squeezes the CSS up as much as possible. This stylesheet includes my applications base styles, which include relative links to images, so I use the gsub above to find all the image URIs and add them to the message as inline attachments and then replace the URL in the output with the inline MIME url that is used to referencing parts within the message itself. We end with a call to the mail method, on which you can call deliver to send the message, like so:

Notifier.welcome_email(current_user).deliver

I mentioned that ActionMailer will automatically inline templates of the html and text type, and these are done in the normal Rails view way. Here is the contents of my app/views/notifier/welcome_email.text.erb:

Welcome to Kimono
=================

Hi <%= @user.full_name %>,
   we're really glad that you've decided to join the 
social business revolution and open the Kimono.

You can log into your account on the site by going to <%= root_url %>.

Thanks for joining and have a great day!

And my app/views/notifier/welcome_email.html.haml:

!!! 5
%html
  %head
    %meta{'http-equiv' => 'Content-Type', :content => "text/html; charset=utf-8"}
    %style{'type' => 'text/css'}
      != @css
  %body
    #frame
      %div
        #sidebar
          %a#logo{:href => '/'}
            = image_tag attachments['kimono-small-notext.png'].url
        #content
          %h1
            Welcome to Kimono
          %p
            Hi
            = @user.full_name + ','
            %br
            we're really glad that you've decided to join the social business
            revolution and open the Kimono.
          %p
            You can log into your account on the site by going to
            = link_to(root_url, root_url)
          %p
            Thanks for joining and have a great day!
    #footer
      %div 
        = link_to(image_tag(attachments['sociable-logo.png'].url), 'http://www.sociable.co.nz/')

And, of course, the end product looks something like:

Multipart HTML email

In short with the refinements to ActionMailer it's much easier to make really nice looking HTML emails which help add to the professional look of your site. The Rails3 ActionMailer guide on edge Rails Guides is a great place to look for more information.

30 Jun 10

Two things that gotcha'd me with Rails 3 today.

config.load_paths is gone. You probably want config.autoload_paths.

The textilize, textilize_without_paragraph and markdown helpers are gone. I just added a markdown helper that calls RDiscount.new(content).to_html.

29 Jun 10

Inlining CSS with actionmailer 3.0

    @css = File.read(File.join(Rails.root, 'public', 'stylesheets', 'notifier.css')).gsub(/\/images\/([a-zA-Z\-\_]+\.png)/) do |match|
      attachments.inline[$1] = File.read File.join(Rails.root, 'public', 'images', $1)
      attachments.inline[$1].url
    end

28 Jun 10

Filterski

I got asked an interesting question on #RubyOnRails about building JSON and XML data structures for API services. From what I could tell the heavy lifting is around filtering the model down to a subset of attributes or associations that you want to render, so I threw together Filterski to allow you to easily create arbitrary filters on models which when called will create a nice data structure you can easily render into your format of choice. Filterski is a quick and dirty to do the job. I'm sure there are plenty of improvements which can be made. Please get forking!

15 Jun 10

Muninator covered by Ruby5

Recently, James Harton released Muninator, which allows you to monitor the the internals of your Rails application. Statistics can be gathered not just for the overall request, but also broken down by controller and even model. If you want to track and manage your own application statistics, this may be a good option for you.

Thanks guys!

13 May 10

Using Shorty as your URL shorter in Tweetie for iPhone

I just quickly hacked in compatibility with Tweetie's URL shortening expectations, so you can use Shorty when sending URLs via Tweetie. Under "Settings" > "Services" > "URL Shortening" select "Custom" and enter

http://s.mashd.cc/shorten?url=%@

That's it!

10 May 10

Easy extensible graphing of your Rails app using Munin

I have used Munin for years to monitor machines that I am looking after and recently, after migrating my sites to a new linode I found myself setting up munin-node again and reflecting on Munin's very simple protocol to talking to nodes over the network.

I had been lamenting the lack of visibility of what's going on inside my rails app without going overboard with products like New Relic's RPM, so I decided to write a rails plugin that turns your Rails application into a munin-node by implementing the protocol that Munin uses. This, like Munin's protocol itself, was trivially easy.

It's my pleasure to present Muninator. I have it running for this site, and you're free to take a look. As you can tell from my graphs, this site doesn't exactly get a lot of traffic, but I think you get the idea.

Controller post Hits

Getting Muninator running in your app is trivially easy, start by installing the plugin:

$ ./script/plugin install git://github.com/jamesotron/Muninator.git

Next create config/muninator.yml and tell it what environments, ports, etc you want it to use (most likely you only want to monitor your production environment, but just like config/database.yml you can specify configs for development and test also). Here I am instructing Muninator to listen on TCP port 4950 (Munin-node usually uses 4949, but I am incrementing from there) and the server_name attribute must match that configured in your munin.conf (more on that later). The restrict option can be a list of IP addresses or "localhost" which is an alias for ::1, fe80::1 and 127.0.0.1.

$ cat <<EOF > config/muninator.yml
> production:
>   server_name: myrailsapp.com
>   port: 4950
>   restrict: localhost
> EOF

Next we need to create an initialiser to instruct Rails to start up Muninator when it bootstraps the stack.

$ cat <<EOF > config/initializers/muninator.rb
> Muninator.from_config

By default Muninator will load plugins to monitor process memory usage (resident size) and thread state. I expect these don't give very accurate data however when running on systems like Passenger which will fork multiple instances of the application, although they do give you an idea of how much memory each instance is using.

You can also add automagic monitoring of your models (at the moment, just total row count plus additions and modifications) by calling the acts_as_munin_plugin class method within your models. You might also want to add an explicit call to the model class in your config/initializers/muninator.rb to make sure that the model is loaded at application start-up (by default models are lazy-loaded by Rails).

In addition to automagic monitoring of models you get detailed monitoring of controller actions for no extra cost by calling the monitor_with_munin class method within your controller. By default it will monitor all RESTful actions (ie :index, :new, :create, :show, :edit, :update and :destroy), or you can specify arbitrary actions by adding an argument like so: :actions => [ :action_name, :other_action_name ]. The default controller monitoring will give you per action monitoring of hits, response time and response size.

You can now restart your Rails app, and Muninator will be listening on the specified port waiting for a connection from your Munin collector:

$ sudo lsof -n | grep :4950
ruby       6514      jnh    5u     IPv4    4002312       0t0        TCP *:4950 (LISTEN)

Getting Munin itself installed is beyond the scope of this article, and there are some pretty good docs on Munin's site to help you. Debian/Ubuntu users can just apt-get install munin and it will take care of the bulk of the work for you. The relevant parts of my munin.conf for mashd.cc look like this:

$ cat /etc/munin/munin.conf
[Rails;]
  use_node_name yes

[Rails;mashd.cc]
  address 127.0.0.1
  port 4950

The only other potential gotcha is that platforms like Passenger will shut down your rails apps if they don't have any hits for a while, leaving large blank patches in your graphs. This might actually be desirable (especially on low memory environments), however I whipped up the following which I run in cron just before the munin-update job:

awk '/^\[Rails\;(.+)\]$/{print gensub(/.*;(.+)]/,"wget -O /dev/null -q http://\\1","")}' /etc/munin/munin.conf  | sh

If you wish to create your own Muninator plugin (or plugins) to monitor a specific part of your application then it's dead simple. All you have to do is add a class to Muninator::Commands that implements config() and fetch() class methods which return strings in the format expected by Munin. Take a look at the source for Muninator's memory plugin for about the simplest possible plugin. If you plugin is even vaguely useful to others then please feel free to contribute it either by forking on Github and sending me a pull request, or just hit me on twitter.

06 May 10

Radio NZ Pips

In response to this tweet by a colleague:

If there was a Radio NZ pips-at-the-top-of-the-hour Twitter account in the style of @big_ben_clock it'd be: "beep beep beep beep beep BEEP".

I had little choice but to gem install twurl as mentioned on last tuesday's Ruby5 podcast and give it a go. I followed the quick instructions on github and had Twurl set up to tweet every hour with a cron entry like so:

0 * * * * /opt/local/ree/bin/twurl -d "status=beep beep beep beep beeeeeep. it's `TZ=NZ date +\%l` o'clock." /1/statuses/update.xml 1> /dev/null

All up, about 5 minutes work.

05 May 10

The Never Call

The telephone was an aberation in human development. It was a 70 year or so period where for some reason humans decided it was socially acceptable to ring a loud bell in someone else’s life and they were expected to come running, like dogs.

I agree. Totally.

30 Apr 10