#2 May 13th, 2008

My 5 Rails Tips blog post has won second place in the RailsCast contest (Actually it was third, but for some reason the numbering is different there).

This is a great boost for me, to know I’m doing a good job posting this stuff. I also won a few things:

All great prices, and I’ll certainly be doing some reviews of them here! I’m allready excited to start using the 16Bugs plan, because good bugtracking was something I was missing in some of my projects..

A big thanks goes out to Ryan Bates for organizing and judging this contest (and picking me as a winner)!

tags: , , , l 0 comments »

As seen in the Commit History: script/dbconsole May 8th, 2008

While going to the recent Rails Git logs, I stumbled across a cool new feature: dbconsole.

We all know and love the Rails console, which gives us an easy interface to our Rails environment, but sometimes we have to be in the database itself.. When this happened, most of the time we had to look up how our database was called, what kind of database it was, basically look at our database.yml file, but no more!

jan:railsproject jan$ script/dbconsole 
SQLite version 3.5.6
Enter ".help" for instructions
sqlite> select * from products;
...

and with easy access to each environment:

jan:railsproject jan$ script/dbconsole production
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 5.0.41 MySQL Community Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

How cool is that!

tags: , , , l 3 comments »

Don't be too DRY - Filter Abuse April 29th, 2008

Controller Filters are a great way to add behaviour to your controller which isn’t directly connected to your actions. The Hitchhiker’s Guide to the GalaxyRails Documentation has this to say on the subject of Filters

Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do authentication, caching, or auditing before the intended action is performed. Or to do localization or output compression after the action has been performed. Filters have access to the request, response, and all the instance variables set by other filters in the chain or by the action (in the case of after filters). source

A good example to this is authentication. A controller action’s role isn’t authentication, but you should be sure that whoever reaches that action is authenticated. This can be solved with a simple before_filter

class PostsController < ApplicationController
  before_filter :check_auth, :except => [:show, :index]

  def index
    @posts = Post.all
  end

  def new
    @post = Post.new
  end 

  ...
  private
  def check_auth
    User.authenticate(session[:user]) || redirect_to login_path
  end
end

Lately, however, I’ve seen some very bad use of this functionality. We all like the Dont Repeat Yourself principle, but this shouldn’t interfere with code readability. Actions should be descriptive. When reading an actions code, you should know what it does.

This is bad:

class PostsController < ApplicationController
  before_filter :find_post, :only => [:show, :edit, :update]
  before_filter :all_posts, :only => [:index]
  before_filter :find_comments, :only => [:show]

  def index
  end

  def show
  end

  def edit
  end

  def update
    @post.update_attributes(params[:post)
    redirect_to @post
  end
  ...
  private
  def find_post
    @post = Post.find(params[:id])
  end

  def all_posts
    @posts = Post.find(:all)
  end

  def find_comments
    @comments = Comment.find_by_post_id(params[:id])
  end
end

The reason people use filters this way is to be ultra DRY. You don’t need to do Post.find(params[:id]) 3 times in your controller, and it works this way.

I personally hate this pattern, and everyone who uses it. This code may be DRY, but it’s not readable at all.

When I see an action with nothing in it, and I look at the view template and see @post being used, I have to look back at the controller, look at which before_filters apply, find the methods called, interpret what is in there.

Other than that, it also removes the ability to do effective Action Caching, because action caching will interpret before_filters, thus running the SQL queries the find-methods trigger.

If you have too much code you’d have to replicate, there are other solutions. For example if you had

class PostController
  def show
    @post = Post.find(params[:id], :conditions => {:site_id => current_site.id, :deleted_at => nil}, :include => :comments)
  end

  def edit
    @post = Post.find(params[:id], :conditions => {:site_id => current_site.id, :deleted_at => nil}, :include => :comments)
  end

You should refactor this with a model-action instead of going for a before_filter. This could become

class Post
  named_scope :active, :conditions => {:deleted_at => nil}, :include => :comments
end

class PostController
  def show
    @post = current_site.posts.active.find(params[:id])
  end

  def edit
    @post = current_site.posts.active.find(params[:id])
  end
end

Believe me, readability is way more important then being DRY

tags: , , , l 0 comments »

5 Rails Tips April 25th, 2008

Ryan Bates at railscasts.com has written out a contest where he asks to publish your own Ruby on Rails tips, 5 of them, and let him know about it. I’m keen on the idea of sharing some Rails wisdom with the world, so here are my humble 5 Rails Tips.

using alias_method_chain to improve your models

In Ruby on Rails projects I like to use the Fat Models, Skinny Controllers principle. Sometimes I have models with associations that are quite evident, and where I want it to be very transparent how to set them. For example, I have a Post, which belongs to a category, but I want to be able to create a post like this:

Post.new :title => "Lorem Ipsum", :content => "Lorem Ipsum dolor...", :category => 'Lorem'

And this should create the Lorem category, or use the one which exists. To reach that goal, I use alias_method_chain

class Post < ActiveRecord::Base
  belongs_to :category

  def category_with_name_recognition=(name)
    if name.is_a? Category
      self.category_without_name_recognition = name
    else
      self.category_without_name_recognition = Category.find_or_initialize_by_name(name)
    end
  end

  alias_method_chain :category=, :name_recognition
end

The alias_method_chain function magically aliases category= to category_without_name_recognition, and then replaces category= to link to category_with_name_recognition. That way I can do a call to the old function. For more info on aliasmethodchain, take a look at your local active_support/core_ext/module/aliasing.rb file.

This comes in very handy when category is an autocomplete_field!

everything you always wanted to know about rake tasks, but were too afraid to ask

Everybody knows the default rake tasks (rake db:migrate, rake test), but do you know the next ones:

  • rake db:create:all –> This will create databases for all defined environments in your database.yml file.
  • rake routes –> This will print out all routes your application knows
  • rake db:fixtures:load –> This will load your test fixtures in your current environments database, I use this all the time

To see all rake tasks, you can just run rake --tasks for a list of all tasks with a short description. For a full description of all tasks, run rake --describe

use read_attribute and write_attribute

If you’re overriding an attribute setter or getter in your models, it’s not possible setting or getting these attributes like you normally would, because that would cause an infinite loop. Luckily, there are methods you can use to get and set the attribute. Lets take a User object for example:

class User < ActiveRecord::Base
  ...

  def password=(pass)
    write_attribute(:password, Security.encode(pass))
  end

  def password
    Security.decode(read_attribute(:password))
  end
end

using your own date and time formats

I personally don’t want to be bothered with time formatting, I can read about any format the internet throws at me. Sadly, some of my clients have different, strong opinions on the way time should be formatted on their website. You really don’t want to be bothered with using strftime everywhere. Luckily, Rails has a solution to this problem.

In your environment.rb, you can define your own time formats:

ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(
  :client_readable => "%A %B %d at %H:%M", # => Friday April 25 at 11:10
  :client_reports => "%d/%m/%Y at %H:%M"   # => 25/04/2008 at 11:10
)

And then you can use this formatting in your views really easy:

Last updated on <%= @post.updated_at.to_s(:client_readable) %>
# => "Last updated on Friday April 25 at 11:20"

you need plugins!!!

Rails is a great framework, but it doesn’t implement every need of every project. Most of the time however, you’re not the first person to need feature A, or something B, most of the time the feature you want is already in a plugin. These are 5 plugins I can’t live without:

cool Rails 2.1 features

I couldn’t constrain myself to 5 rails tips. Today, and today only, you get a free 6th tip!

If you’re running on Rails Edge (which I do for most projects), or you’re reading this when Rails 2.1 is already out, there are a bunch of cool new features that aren’t documented yet.

render :partial => form
# Before Rails 2.1
<% form_for @client do |form| %>
  <%= render :partial => 'form', :object => form %>
  <%= form.submit "Save" %>
<% end %>

# Now, with sparkly new Rails
<% form_for @client do |form| %>
  <%= render :partial => form %> # Automaticly renders _form.erb 
  <%= form.submit "Save %>
find(:last)
# Before Rails 2.1
@last_post = Post.find(:first, :order => 'created_at DESC')

# with Rails 2.1
@last_post = Post.find(:last, :order => 'created_at')
Easy finding
# Before Rails 2.1
Post.find :all
Post.find :first

# with Rails 2.1, thanks to scoping
Post.all
Post.first
Post.last

These are all small changes that I find particularly handy in my day-to-day work. For the “big” changes, you should read Ryan’s Scraps, he keeps track of all big changes in Rails.

And with that, I’m all out of tips, at least for now. Comments are always appreciated!

tags: , , , , l 0 comments »

Testing your Rails Plugins April 24th, 2008

A great feature in Ruby is the integrated testing. Ruby on Rails also implements and extends the Ruby testing framework, and if you’re not testing your Ruby code you should start doing it now!

Normally, when I write tests for a class, I mock out everything not related directly to the class (I do this with mocha). For example, when writing tests for the Mollom gem, I mocked out the whole XMLRPC API, because I can’t rely on them to give me all test cases.

# from http://github.com/DefV/ruby-mollom/tree/master/test/mollom_test.rb
def test_server_list
  xml_rpc = mock
  xml_rpc.expects(:call).
          with('mollom.getServerList', is_a(Hash)).
          returns(['http://172.16.0.1', 'http://172.16.0.2', 'https://172.16.0.2'])

  XMLRPC::Client.stubs(:new).with('xmlrpc.mollom.com', '/1.0').returns(xml_rpc)

  assert_equal [
          {:ip => '172.16.0.1', :proto => 'http'}, 
          {:ip => '172.16.0.2', :proto => 'http'}, 
          {:ip => '172.16.0.2', :proto => 'https'}
         ], @mollom.server_list
end

This way, I’m only testing the behaviour of that specific class, so if something fails somewhere, I can pinpoint the problem, while being sure it’s not a problem in the Mollom API.

While writing a test for some view plugin the other day, I was doing the same thing. I stubbed out everything from ActionView/ActionController which I used (content_tag and url_for and such). Suddenly I realised that wasn’t a good idea.

Rails plugins tests should use the Rails Framework, because the plugins themselves rely on it deeply. To be sure the plugin keeps working while the framework changes, I just have to run my tests, which is exactly what I want.

So I do it this way now:

# from http://github.com/DefV/title_helper/tree/master/test/title_helper_test.rb
require 'test/unit'
require File.dirname(__FILE__) + '/../../../../config/boot.rb'
require File.dirname(__FILE__) + '/../../../../config/environment.rb'

class TitleHelperTest < Test::Unit::TestCase
  def setup
    @helper = ActionView::Base.new
  end

  # Replace this with your real tests.
  def test_title_method_with_no_title_set
    assert_equal "foobar", @helper.title(:site_name => 'foobar')
  end
  ...
end

How do you test your Rails Plugins?

tags: , , , l 0 comments »

Symbols vs Strings April 22nd, 2008

When to use Symbols and when to use Strings in Ruby is really a personal preference. Most of the time I program without taking the difference in performance in consideration.

I just use a few simple rules

in a hash it’s always :key => ‘value’

redirect_to :controller => 'pages', :action => 'index'

special keywords are symbols

Page.find(:all)
Page.find(:first)
redirect_to(:back)

attributes and status’es are symbols

update_attribute(:name, 'Jan')
comment.state = :approved

Except for those rules it’s really what feels right while coding it.

tags: , , , l 1 comments »

HWYDI: String Hex to Decimals April 22nd, 2008

How would You do it?!

I want to make a returning item out of these kinds of posts. I present a problem I had, and the way I solved it, and then you can tell how you did it (In the comments, in your own blog, ..).

I think this can be really interesting, since everyone has their own solution to problems, and nobody knows every possible way to solve something.

the Problem

At Openminds we generate a Virtual Server’s MAC Address by the IP the Virtual Server will get. So for example, host 88.151.243.8 will have MAC address 02:00:58:97:f3:08, where the last 4 parts are the hexadecimal representation of the IP address.

Calculating the IP to the MAC address is easy, but sometimes we want to know what IP address is linked to a certain mac. Most of the time we’ll get our mac addresses in the form of 0200.5897.f308, so we take this as our default input.

my Solution

#!/usr/bin/ruby
mac = ARGV[0] # => "0200.5897.f308"

if mac =~ /(\w{4})\.(\w{4})\.(\w{4})/
  ip = ($1 + $2 + $3).scan(/../)[-4..-1].collect do |hex|
    sprintf('%d', "0x#{hex}")
  end.join('.')
end

puts ip

How would You do it?

tags: , , , l 2 comments »

All things Ruby April 21st, 2008

I used to have 1 blog where I talked about everything I cared about, which was a mix of personal messages (“I’ve seen a movie” and “I’ve bought Guitar Hero III”) and technical messages (Rails Plugins, Akismet Bugs, …). I also mixed English and Dutch in those articles.

It came to a point where my friends and family (not-techies) started complaining that I did too much technical blogging, whilst the people who read my blog for the technical posts got annoyed by my personal blabbering.

No more! From now on all my technical blogging will move here. All in English, nothing dumbed down. My personal life and everything around it will stay on my original blog. That way I hope to keep my “fanbase” happy.

It’s a hard job to maintain 1 blog with useful posts.. Let’s see what 2 blogs give ;-)

tags: , , l 1 comments »