Full URL in Rails’ logs

I find myself needing to have the full URLs in Rails’ logs. Normally you get something like:

Started GET "/" for 127.0.0.1 at 2011-08-27 13:13:10 +0200

but I needed

Started GET "http://foo.bar:3000/" for 127.0.0.1 at 2011-08-27 13:13:10 +0200

because the app does different things depending on the domain and when it fails, I have to know which URL was hit. The solution I ended up with was adding this in an initializer:

class Rails::Rack::Logger << ActiveSupport::LogSubscriber
  protected

  def before_dispatch(env)
    request = ActionDispatch::Request.new(env)
    info "\n\nStarted #{request.request_method} \"#{request.url}\" for #{request.ip} at #{Time.now.to_default_s}"
  end
end

That’s monkey-patching Rails’ own logger. Credit for the solution goes to numbers1311407.

My question for the people using Rails, do you think having a configurable logger in Rails would be useful or nice? If so, I could make a patch for Rails but I have made patches before that failed to gather the needed popularity and thus were ignored. I’m not wasting my time like that again.

Getting rid of RubyGems deprecation warnings

A recent update to RubyGems is causing a lot of deprecation warnings like these:

NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /usr/lib/ruby/gems/1.8/specifications/rubygems-update-1.4.1.gemspec:11.
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /usr/lib/ruby/gems/1.8/specifications/bundler-1.0.7.gemspec:10.
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /usr/lib/ruby/gems/1.8/specifications/file-tail-1.0.5.gemspec:10.

I generally like software to move forward and the way to do that is deprecate and then after a while, make backwards incompatible changes. It’s painful but there’s no other way.

I do have a problem with all the cron jobs of my web apps like Keep on Posting or DNSk9 flooding my inbox with those warnings. Thankfully, that’s not hard to fix. Where I was doing:

rake pre_calculate_thingies > /dev/null

now I’ll be doing:

rake pre_calculate_thingies 2>&1 >/dev/null | grep -v default_executable

Careful with that email

When you are building systems like my Keep on Posting or my DNSk9 that send emails there’s always the danger that you’ll accidentally fire emails from your development machine to real users. You really don’t want to do that because it’s annoying and extremely unprofessional.

It happened to me a couple of times. Thankfully, nothing serious. But I learned the lesson. That’s why in my user models now I have a safe_email method which I use instead of accessing email whenever I’m about to actually deliver a message.

The method safe_email ensures that nobody will receive a message unless I’m in production and at the same time it’s good for testing. Obviously most of the time in development and testing mode I don’t deliver emails at all, but sometimes, I make an exception:

def safe_email
  if Rails.env.production? || email.blank? # If the email is blank (or nil), let it be.
    email
  else
    "pupeno+#{email.gsub("@", "_AT_")}@pupeno.com"
  end
end

Rake tasks for production

When I need to run something periodically on production, I always implement it as a rake tasks and install it as a cron job. Nevertheless there’s some setup to do in the task to have proper logging and error reporting.

This is the template I use for creating those tasks:

namespace :projectx do
  desc "Do something"
  task :something => :environment do
    if Rails.env.development?
      # Log to stdout.
      logger = Logger.new(STDOUT)
      logger.level = Logger::INFO # DEBUG to see queries
      ActiveRecord::Base.logger = logger
      ActionMailer::Base.logger = logger
      ActionController::Base.logger = logger
    else
      logger = ActiveRecord::Base.logger
    end

    begin
      logger.info "Doing something"
    rescue Exception => e
      HoptoadNotifier.notify(e)
      raise e
    end
  end
end

While in development mode, it outputs to the console for convenience.

Another useful collection method? Enumerable#select_first

For a personal project I’m working on, I need to find out the smallest time period with more than 5 records. I essentially wrote this code:

period = [1.week, 1.month, 1.year].select_first do |period|
  Record.where("published_at >= ?", period.ago).count >= 5
end

only to find out that the select_first method doesn’t exist. So I wrote it:

module Enumerable
  def select_first(&predicate)
    self.each do |item|
      if yield(item)
        return item
      end
    end
    return nil
  end
end

and then of course, I tested it:

require "test_helper"

require "enumerable_extensions"

class EnumerableTest  2 }
  end

  should "select_first the first one" do
    assert_equal 1, [1, 2, 3, 4].select_first { |i| i >= 1 }
  end

  should "select_first the last one" do
    assert_equal 4, [1, 2, 3, 4].select_first { |i| i >= 4 }
  end

  should "select_first none" do
    assert_equal nil, [1, 2, 3, 4].select_first { |i| i >= 100 }
  end
end

A hash map method that returns a hash

I’ve just released another gem, this one extends Hash to contain another method called hmap. This solves a problem I face ofter: how to run a map in a hash that returns another hash, for example:

{:a => 1, :b => 2, :c => 3}

being converted into

{:a => 2, :b => 3, :c => 4}

With hmap it’s easy:

hash.hmap { |a,b| {a => b + 1} }

It also works with arrays, but you must make sure the array you return always contains two and only two elements:

hash.hmap { |a,b| [a, b + 1] }

And that’s all, quite a simple piece of code, but now it’s re-usable and well tested.

Have a sense of humor

I love it when company have a sense of humor that shows they are not enterprise drones but people like their users, with normally the same goals and intentions. Way to go JetBrains with your new install screen for RubyMine.

Faking it: enums in Ruby

Ruby doesn’t have enums and in Rails I sometimes need it. I’ve come out with my own way of doing after some trial and error. First I want to be able to access these enums as constants in a class, like:

Pizza::RAW
Pizza::COOKED
Pizza::BOXED
Pizza::DELIVERED
Pizza::EATEN

That’s actually quite easy:

class Pizza
  RAW = 1
  COOKED = 2
  BOXED = 3
  DELIVERED = 4
  EATEN = 5
end

If I’m storing those values on the database, I’d like to have my database be more readable, so, I just store strings:

class Pizza
  RAW = "raw"
  COOKED = "cooked"
  BOXED = "boxed"
  DELIVERED = "delivered"
  EATEN = "eaten"
end

That’s not very efficient and there are better ways these days. But it’s simple and premature optimization is the root of all evil. Anyway, back to Ruby, I’d like to be able to get a list of all possible enum values, to be able to populate selections for example:

class Pizza
  RAW = "raw"
  COOKED = "cooked"
  BOXED = "boxed"
  DELIVERED = "delivered"
  EATEN = "eaten"

  STATES = [RAW, COOKED, BOXED, DELIVERED, EATEN]
end

Simple enough except that it doesn’t follow DRY style. In Ruby we can do better:

class Pizza
  STATES = [
    RAW = "raw",
    COOKED = "cooked",
    BOXED = "boxed",
    DELIVERED = "delivered",
    EATEN = "eaten"
  ]
end

That defines the constant and since the result of RAW = "raw" is "raw" we can also add it to an array at the same time.

John, the manager of the Ruby Pizza Shop was concerned that some pizzas were devoured immediately and others took as long as 15 minutes. Decided to improve his business he started investigating the whole procedure and he noticed that as soon as the pizza left the kitchen, some cooks considered delivered and marked them as such. That was wrong so instead of doing a pizza-state training, he decided we should improve the UI. Let’s describe the state.

class Pizza
  STATES = [
    RAW = "raw",
    COOKED = "cooked",
    BOXED = "boxed",
    DELIVERED = "delivered",
    EATEN = "eaten"
  ]

  STATE_EXPLANATIONS {
    RAW => "The pizza is not a pizza yet, just a bunch of ingredients.",
    COOKED => "OMG! That smells good!",
    BOXED => "It's ready to go.",
    DELIVERED => "The pizza has been snatched out of the hands of a delivery boy.",
    EATEN => "The pizza is no more."
  }
end

Of course we can do better than that and merge it in one:

class Pizza
  STATE {
    (RAW = "raw") => "The pizza is not a pizza yet, just a bunch of ingredients.",
    (COOKED = "cooked") => "OMG! That smells good!",
    (BOXED = "boxed") => "It's ready to go.",
    (DELIVERED = "delivery") => "The pizza has been snatched out of the hands of a delivery boy.",
    (EATEN = "eaten") => "The pizza is no more."
  }
end

Stylistically I’m not a fan, but semantically, that’s dryer. Now, we can get the list of states like this:

Pizza::STATE.keys

Metaprogramming Ruby

There are thousands of books that will take you from illiterate to novice in any programming language. But finding those that will take you from novice or intermediate to expert is hard. I remember reading Effective Java some years ago and wishing I had something like that for Python. I’ve never found one.

Metaprogramming Ruby is a great book full of very interesting knowledge, full of those things that separate a Ruby programmer and an export Ruby programmer. Before finishing the book I’ve already put to use some of the lessons and it saved me a lot of time. The book payed for itself before I’ve finished reading and I really recommend it to anyone who is serious about coding in Ruby.

The magic of Bundler

Recently I reported a bug for Formtastic. Justin French, the author of Formtastic, created a branch and made a fix. He then asked me for my feedback.

I look at the code and then decided to give it a try. In a pre-Bundler world that would have required this:

  1. Find a directory to play with this.
  2. Clone the Formtastic repository with Git from http://github.com/justinfrench/formtastic.git
  3. Create a local branch tracking the remote branch with the fix, GH-264. This is something I don’t do often enough with Git and every time I have to look it up.
  4. Figure out how to build a gem out of it. Is it rake? is it rake build? is it rake gem? This might also fail and need fixing some stuff.
  5. Install said gem, which is not that trivial. Should I install as my user or as root? Should I remove the currently installed version of the gem? If the branch didn’t have an increase in version number it could be problematic.
  6. Test my application. Make sure it’s picking up the new gem.
  7. Uninstall the gem, maybe re-install the stock gem.
  8. Delete the temporary directories I’ve created to hold the cloned repository (this is something I always forget to do and a month later I’m wondering: what’s this? is there any important changes I’ve did in this repo?).
  9. The tasks are not that big, but are very inconvenient to do and uncomfortable for a perfectionist like me. Thankfully I’m using Bundler, so the above was like this:

  1. Add :git => "http://github.com/justinfrench/formtastic.git", :branch => "GH-264" to the Formtastic line in Gemfile.
  2. Run bundle install.
  3. Test app.
  4. Revert the Gemfile change.
  5. Run bundle install.
  6. I really love Bundler.