my.urgas.eu

RSS

Required keyword arguments

If you don’t know about keyword arguments then please read Keyword arguments in Ruby 2.0 first.

Ruby 2.1 improves keyword arguments even more by adding an option to make them required.


An example of using required keyword arguments:

def foo(api_key:, api_secret:, **opts)
  [api_key, api_secret, opts]
end

Now you can use it like this:

foo(api_secret: 'abc', api_key: 'xyz', debug: false)
  # ['xyz', 'abc', {debug: false}]

If you forget to add api_secret or api_key option then ArgumentError will be raised

foo(api_key: 'xyz', debug: false)
  # ArgumentError: missing keyword: api_secret

This is really great feature for extracting data from a hash. No more Hash#fetch and Hash#delete calls. Instead of doing

  def process(message)
    type = message.fetch(:type)
    # decide which processor to use
    processor.process(message.except(:type))
  end

you can now just do:

  def process(type:, **message)
    # decide which processor to use
    processor.process(message)
  end

Organising Rails controllers

I’ve seen many projects that have over 50 controllers in their main controllers directory. This makes finding related code very tedious. In this article I’ll show you a few tips how to organise your code into separate directories.

Namespaces

Namespaces are usually most common method to structure controllers. I’m sure you have seen or used /admin/* namespaces before. Its idea is to separate bigger parts of the application.

# config/routes.rb
  namespace :admin do
    resources :posts
    resources :categories
  end

Namespaces should be short and simple (e.g admin, advertising). Abbreviations are also allowed if they are understandable for the site visitors (e.g instead of writing human_resources_management, just use hrm).

The reason why we want namespaces to be short is to keep rails url helpers short. Here are few examples with the admin namespace:

  form_for [:admin, Post.new]
  link_to "post link", [:admin, @post]
  link_to "post link", admin_post_path(@post)

Modules

Modules are meant for putting related code into separate directories without changing URLs nor url helpers. Lets say we have two different kinds of posts: drafts and published posts. They are related so we might want to put them in the same directory. We can put them into app/controllers/posts/ directory and add following routes:

# config/routes.rb
  scope :module => :posts do
    resources :drafts
    resources :published_posts
  end

Drafts controller itself might look something like this:

# app/controllers/posts/drafts_controller.rb
  module Posts
    class DraftsController < ApplicationController
    end
  end

Another benefit of using modules is that you can have two controllers with the same name. Lets say we have controllers ongoing_surveys and closed_surveys. Both of them have responders page. In the ongoing survey page you can add and remove responders, but in the closed survey page you can only see statuses of responders. Instead of making one responders controller and adding many conditionals we can separate these into two.

# config/routes.rb
  resources :ongoing_surveys do
    scope :module => :ongoing_surveys do
      resources :responders, :only => [:index, :create, :destroy]
    end
  end
  resources :closed_surveys do
    scope :module => :closed_surveys do
      resources :responders, :only => [:index]
    end
  end
end

Now we have:

app/controllers/ongoing_surveys/responders_controller.rb
app/controllers/closed_surveys/responders_controller.rb
app/views/ongoing_surveys/responders/index.html.erb
app/views/closed_surveys/responders/index.html.erb
...

Rails url helpers can be still used like there are no modules present. e.g

  ongoing_survey_responders_path(@survey)
  
  polymorphic_path([@closed_survey, :responders])
  # In the next post I'll show how to separate Survey into
  # ClosedSurvey and OngoingSurvey without inheritance.

Next time

Next time I’ll show you how to organise Rails models and how to avoid conflicts with Rails polymorphic url helpers.

Having fun with Mac OS X: Hidden files

Bought a Mac

I’ve been using Linux for last 5+ years. Mostly Ubuntu and Debian. But this month I decided to try something new and bought myself a new MacBook Pro with Retina display. Took over three weeks to arrive. I’ll be writing few posts about annoying and also interesting issues I’ve found.

Hidden files

First thing I did with my MacBook was generating a new ssh key pair for my laptop. ssh-keygen worked as expected, so no problem there. I also needed to add this key to a few servers. I thought that the easiest way to do this is to copy the public file to my dropbox and add it to the server using my main computer.

I fired up finder and there was immediately a problem: where the hell is my home directory. I finally found it in the Go menu. I was trying to find it from left side of the finder (Favorites section).

Now I needed to get to the .ssh directory. In Debian I’m used to press Ctrl+L and append .ssh to the address bar. Didn’t work here. So maybe Ctrl+H to see hidden files. Nope, didn’t work. I browsed the finder menu but nothing was even close to show hidden files. Finally I fired up Google and did some searching. Turns out that viewing hidden files is simple as:

defaults write com.apple.finder AppleShowAllFiles -bool true

Wait, what? I though we didn’t need a terminal for basic operations like that.

During writing this post I found that when I was in the home directory I could have clicked on Go->Go to Folder instead

.
May 6

Getting rid of the AR callbacks

Problem

When writing a small rails application, it is really easy to use ActiveRecord callbacks. You want to send a welcome message after user signs up? Add after_create callback and voila.

Your model might look something like this:

class User < ActiveRecord::Base
  validates :name, :presence => true

  after_create :send_welcome_email

  private

  def send_welcome_email
    UserNotifier.welcome(self).deliver
  end
end

It seems nice so far. Lets also add some tests to ensure it works:

require "spec_helper"

describe User do
  it "should send a welcome email after the user is created" do
    user = User.new(:name => "John")

    notifier = stub
    notifier.should_receive(:deliver)
    UserNotifier.stub(:welcome).with(user) { notifier }

    user.save
  end
end

This works, but there are some issues:

  1. It’s slow. Saving records to the database is something we don’t want to do.
  2. We have to stub out welcome message part in every test that creates an user.
  3. We also know that the site administrator later wants to add users manually using admin panel where welcome messages shouldn’t be sent.

There are some solutions for these issues:

  1. Instead of saving the user we can use user.run_callbacks(:create) { false }.
  2. We can build a helper method that stubs out sending welcome message.
  3. We can add an if condition to the callback that checks some attr_accessor variable.

There’s something common with each of these points: they suck! The reason why they suck is because they shouldn’t be in the model at all.

Solution

Instead of adding after_create callback just create a separate class that handles this logic. Lets create a new directory called services and add a new class:

class UserRegistrationService
  def initialize(name)
    @user = User.new(:name => name)                                            
  end
                                                                               
  def register                                                                 
    if @user.save                                                              
      send_welcome_email                                                       
    end
  
    @user                                                                      
  end

  private

  def send_welcome_email
    UserNotifier.welcome(@user).deliver
  end
end

Testing this class is much easier. We don’t even have to load rails.

require_relative "../../app/services/user_registration_service"

class User; end
class UserNotifier; end
  
describe UserRegistrationService do                                            
  let(:user) { stub(:save => true) }                                           
  let(:notifier) { stub(:deliver => true) }                                    
      
  before do
    User.stub(:new).with(:name => "John") { user }
    UserNotifier.stub(:welcome).with(user) { notifier }                        
  end

  it "returns the user" do
    UserRegistrationService.new("John").register.should == user
  end 
    
  it "sends a welcome message after the user is created" do
    notifier.should_receive(:deliver)

    UserRegistrationService.new("John").register
  end
  
  it "does not send a welcome message when saving the user fails" do               
    user.stub(:save) { false }
    notifier.should_not_receive(:deliver)
    
    UserRegistrationService.new("John").register
  end
end

There are many testing benefits for this approach:

  • We don’t have to load rails. (no spec_helper loaded). This means the testing time is really short.
  • There is no need to save anything to the database. Again, fast tests.
  • We don’t have to worry about stubbing out the welcome message part in the other tests. We only have to worry about it in one place.
  • We can use the same class in the integration testing if we need to populate the database.

Other benefits:

  • The User model is clean. It is easier to maintain shorter classes.
  • If we don’t want to send emails, we can just use some other class or the AR model directly.

When is it okay to use callbacks?

In my opinion - never.

I really like José Valim tweet: If you want to skip an Active Record callback, it probably shouldn’t be a callback.. He later added: Scratch that. Most of your Active Record callbacks probably shouldn’t be a callback..

Where to now?

I suggest reading a free book called Object on Rails.

If you already haven’t, then take a look at Destroy All Software screencasts.

There are also a few older blog posts that I found interesting regarding this topic: ActiveRecord’s Callbacks Ruined My Life and Crazy, Heretical, and Awesome: The Way I Write Rails Apps

An idea about asset pipeline

Rails 3 way

Usually we structure our Rails 3+ assets like this:

app
  assets
    stylesheets
      global.css
      comments.css
      posts.css
    javascripts
      global.js
      comments.js
      posts.js
  views
    comments
      ...
    posts
      ...

comments.js and comments.css are specific for comments controller and its views.

Keeping controller specific assets in the views directory

app
  assets
    stylesheets
      global.css
    javascripts
      global.js
  views
    comments
      comments.css
      comments.js
      ...
    posts
      posts.css
      posts.js
      ...

What’s the point?

  • Keeping assets closer to views that actually use it.
  • Suggesting users to use more structured assets. I sometimes find a project that has only one huge global.js and one huge global.css

What do you think about this?

Asset pipeline

Please feel free to comment and suggest your ideas.

Speeding up RSpec test suite: Tip #3

Assuming you are using ruby 1.9.3-p0 at the moment, then I suggest applying funny-falcon patch (https://gist.github.com/1688857). This made my rails start up faster and also the overall runtime.

This is also included in the RVM. To install it, just run:

rvm get head
rvm install 1.9.3-falcon
rvm use 1.9.3-falcon

I also suggest adding following to your shell rc file (bonus section from the gist). This basically calms down the garbage collector. It has almost same effect as disabling GC completely in the specs (tip #2).

export RUBY_HEAP_MIN_SLOTS=1000000
export RUBY_HEAP_SLOTS_INCREMENT=1000000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
export RUBY_GC_MALLOC_LIMIT=1000000000
export RUBY_HEAP_FREE_MIN=500000

Speeding up RSpec test suite: Tip #2

Got lots of RAM? Disable GC.

I have this snippet in my spec_helper.rb

  if ENV["RUBY_DISABLE_GC_FOR_SPECS"]
    config.before(:suite) do
      puts "GC disabled"
      GC.disable
    end
  end

And in my shell rc file I have:

export RUBY_DISABLE_GC_FOR_SPECS="true"

Warning: If you have many selenium tests, then do not disable it completely. You’ll just run out of RAM.

Speeding up RSpec test suite: Tip #1

If your test suite is database heavy and you are using postgresql, then turn off fsync and synchronous_commit.

fsync - PostgreSQL server will try to make sure that updates are physically written to disk (link). In the development machine we usually don’t care about power failures and data loss, especially with the test data.

synchronous_commit - Specifies whether transaction commit will wait for WAL records to be written to disk before the command returns a "success" indication to the client (link).

You can change these settings in the postgresql.conf. Just set:

fsync = off
synchronous_commit = off

My test suite time with default options (using ssd): 105 seconds.

My test suite time with fsync and synchronous_commit turned off: 30 seconds.