Run Rails 2.3 application using Ruby 2.1

There is a way to have your old Rails 2.3.something application running using latest Ruby from the 2.1 branch. However, it is moderately complex and requires quite a few hacks. Not everything works perfect with this setup, though. Common exceptions are performance tests and some of the generators, but I regard those as minor annoyances.

I’m using 2-3-stable branch of Rails, which means version 2.3.18 plus some additional unreleased patches on top of it and a recently released Ruby 2.1.8.

This guide assumes that you are using Bundler to manage your gems. If not, please follow this guide first.


gem 'rails', :github => 'rails/rails', :branch => '2-3-stable'
gem 'rake'
gem 'json'
gem 'iconv'

group :test do
  gem 'test-unit', '1.2.3'

And then:

$ bundle update rails rake json iconv test-unit


Remove the following line:

require 'rake/rdoctask'


Change the following block after rescue Gem::LoadError => load_error to look like this:

if load_error.message =~ /Could not find RubyGem rails/
  STDERR.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
  exit 1


Update your Ruby version used for Capistrano deployments. Only if you’re using Capistrano with RVM.

set :rvm_ruby_string, '2.1.8'


Change hardcoded Rails version:

RAILS_GEM_VERSION = '2.3.18' unless defined? RAILS_GEM_VERSION

Before block add the following:

# Rails 2.3 and Ruby 2.0+ compatibility hack
if RUBY_VERSION >= '2.0.0'
  module Gem
    def self.source_index

    def self.cache

    SourceIndex = Specification

    class SourceList
      # If you want vendor gems, this is where to start writing code.
      def search(*args); []; end
      def each(&block); end
      include Enumerable

Additional initializers


# ActiveRecord::Callbacks compatibility fix

module ActiveRecord
  module Callbacks


    def callback(method)
      result = run_callbacks(method) { |result, object| false == result }

      # The difference is here, the respond_to must check protected methods.
      if result != false && respond_to_without_attributes?(method, true)
 result = send(method)


      return result


# Encoding issues with Ruby 2+
# backport #3937 to Rails 2.3.8

class ERB
  module Util
    def html_escape(s)
      s = s.to_s
      if s.html_safe?
        silence_warnings { s.gsub(/[&"'><]/n) { |special| HTML_ESCAPE[special] }.html_safe }


# Patch to make i18n work with Ruby 2+

module I18n
  module Backend
    module Base
      def load_file(filename)
        type = File.extname(filename).tr('.', '').downcase

        raise, filename) unless respond_to?(:"load_#{type}", true)

        data = send(:"load_#{type}", filename) # TODO raise a meaningful exception if this does not yield a Hash

        data.each { |locale, d| store_translations(locale, d) }


# This is a very important monkey patch to make Rails 2.3.18 to work with Ruby 2+
# If you're thinking to remove it, really, don't, unless you know what you're doing.

if Rails::VERSION::MAJOR == 2 && RUBY_VERSION >= '2.0.0'
  require 'rails_generator'
  require 'rails_generator/scripts/generate'

  Rails::Generator::Commands::Create.class_eval do
    def template(relative_source, relative_destination, template_options = {})
      file(relative_source, relative_destination, template_options) do |file|
        # Evaluate any assignments in a temporary, throwaway binding
        vars = template_options[:assigns] || {}
        b = template_options[:binding] || binding
        # this no longer works, eval throws "undefined local variable or method `vars'"
        # vars.each { |k, v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
        vars.each { |k, v| b.local_variable_set(:"#{k}", v) }

        # Render the source file with the temporary binding, nil, '-').result(b)


# This is a very important monkey patch to make Rails 2.3.18 to work with Ruby 2+
# If you're thinking to remove it, really, don't, unless you know what you're doing.

if Rails::VERSION::MAJOR == 2 && RUBY_VERSION >= '2.0.0'
  module ActiveRecord
    module Associations
      class AssociationProxy
        def send(method, *args)
          if proxy_respond_to?(method, true)
            @target.send(method, *args)


# Set default encoding for everything coming in and out of the app
# TODO this could/should be removed when upgrading to Rails 3+
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8


# convert all params into UTF-8 (from ASCII-8BIT)

raise "Check if this is still needed on " + Rails.version unless Rails.version == '2.3.18'

class ActionController::Base
  def force_utf8_params
    traverse = lambda do |object, block|
      if object.kind_of?(Hash)
        object.each_value { |o|, block) }
      elsif object.kind_of?(Array)
        object.each { |o|, block) }
    force_encoding = lambda do |o|
      o.force_encoding(Encoding::UTF_8) if o.respond_to?(:force_encoding)
    end, force_encoding)

  before_filter :force_utf8_params


Installing Ruby 1.8.7-head for chruby using ruby-build on OS X 10.11 (El Capitan)

Unfortunately ruby-install won’t let you install Ruby version 1.8.7 any more. Here’s how to install it using ruby-build so that it could still be used by chruby:

brew install ruby-build
brew install openssl libyaml libffi
brew install apple-gcc42
brew install openssl098
  # dependencies

mkdir -p ~/.rubies
  # if doesn't exist

brew link openssl098 --force
  # 1.8.7 requires OpenSSL 0.9.8 (or lower)

ruby-build 1.8.7-p375 ~/.rubies/ruby-1.8.7
  # compile 1.8.7-head (p375 is the same as HEAD)

brew unlink openssl098
  # revert symlinking

chruby 1.8.7
  # switch to 1.8.7

Chruby with Phusion Passenger

This guide will tell you how to use chruby together with Phusion Passenger.

What we want to achieve is automatic Ruby version switching in Passenger based on the Ruby used by the project.

I’m using zsh, but with small modifications it should also work with bash or any other shell type.


  • latest chruby
  • latest Passenger
  • .ruby-version file present in each app/project

The gist of it is that you need a chruby wrapper script, which will be executed separately for each project by Passenger. Place it in ~/bin or at any other place where you keep your local binaries:


# Wrapper for chruby to work with Phusion Passenger
# Based on:

source /usr/local/share/chruby/
source /usr/local/share/chruby/


# original call
exec "ruby" "$@"

After that you need to configure Passenger to make use of this script. I’m on OS X, so the file I’m editing is at /etc/apache2/other/passenger.conf:

# chruby wrapper for Passenger use
PassengerDefaultRuby /Users/<your-username>/bin/chruby-wrapper

# Passenger must read ENV variables for the chruby-wrapper script to work
PassengerLoadShellEnvvars on

# Run Passenger application instance as your current user
PassengerUserSwitching on

That’s it! Good luck!


Testing database transactions explicitly with RSpec

TL;DR; you cannot do it reliably with RSpec.

The long story goes like this. Lets say you have a code executing an AR rollback when something fails:

def call
  Model.transaction do

    unless send_notification
      raise ActiveRecord::Rollback

This update_reason is a block of code, which does some database operation, like an INSERT or UPDATE:

def update_reason
  object.update reason: reason

And send_notification is just some external API call.

So when you write a spec for this code, you might want to write something like this:

describe '#call' do
  it 'does not update the reason when sending the notification fails' do
    allow(object).to receive(:send_notification).and_return false
    expect {
    }.not_to change(object, :reason)  

And, surprise, surprise, the above spec will fail! The reason will change on the object, even though the logic says it should not.

Why is that? This is because normally you have your whole example spec wrapped in a transaction and rolled back after the example has been run. Since your code opens up a new, nested transaction internally (with the #call method: Model.transaction do). This messes things up and now the rollback in the nested transaction does not really roll back anything. Adding require_new: true doesn’t help. Disabling transaction just for this one spec does not work either. Unfortunately.

Something like this works, but it’s not ideal:

expect {
}.to raise_exception ActiveRecord::Rollback 

Additional reading:

* How to test that a certain function uses a transaction in Rails

A deep_reject method for hashes in Ruby

Recently, while adding missing functionality for the i18n-js gem I’ve stumbled into a problem. I needed to have a method to “deep reject” keys in the hash. There are some examples in the wild doing that, but they all solve this problem by adding a new method to the Hash class. I wanted a generic method, which would take the hash as an argument.

After some head scratching, tinkering and tweaking, I’ve come up with a correct solution (at least I think it’s correct). Here it is:

def self.deep_reject(hash, &block)                                                   
  hash.each_with_object({}) do |(k, v), memo|                                        
    unless, v)                                                          
      if v.is_a?(Hash)                                                               
        memo[k] = deep_reject(v, &block)                                             
        memo[k] = v                                                                  

You use it like this:

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

result = deep_reject(hash) { |k, v| k == :b }

result # => {:a => {:c => 2}}

Enabling I18n locale fallbacks in Rails

This guide is valid for i18n version 0.7.0+ and Rails 4.1+

Strangely enough enabling custom locale fallbacks is harder than it should be. Here’s what you need to enable custom locale fallbacks with i18n gem.

First, you need to set config.i18n.fallbacks = true for all environments in a Rails application (config/environments/*.rb).

Then you need to have this in your config/application.rb:

# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
config.i18n.default_locale = :en

# Enforce available locales
config.i18n.enforce_available_locales = true

# Custom I18n fallbacks
config.after_initialize do
  I18n.fallbacks = :"de-DE", ch: :"de-DE", gb: :"en-US")

The above will enable custom fallbacks from at and ch locales to the German language and from gb locale to English. The enforce_available_locales bit is optional.

If you also use i18n-js to have your translated phrases available in javascript, here’s the exemplary fallbacks snippet you need to put inside your javascript code:

I18n.locales["at"] = ["de", "en"]
I18n.locales["ch"] = ["de", "en"]
I18n.locales["gb"] = ["en"]

VI mode indicator in ZSH prompt

Here is my take on VI mode indicator in zsh’s prompt. This is useful only for people who use the vi mode (bindkey -v) in ZSH.


function zle-keymap-select {
  zle reset-prompt
zle -N zle-keymap-select

function zle-line-finish {
zle -N zle-line-finish

# Fix a bug when you C-c in CMD mode and you'd be prompted with CMD mode indicator, while in fact you would be in INS mode
# Fixed by catching SIGINT (C-c), set vim_mode to INS and then repropagate the SIGINT, so if anything else depends on it, we will not break it
# Thanks Ron! (see comments)
function TRAPINT() {
  return $(( 128 + $1 ))

And then it’s a matter of adding ${vim_mode} somewhere in your prompt. For example like this:


Other examples on the web use zle reset-prompt in the zle-line-init, which has a very nasty side effect of deleting last couple of lines on mode change (when going from ins to cmd mode) when using multi-line prompt. Using zle-line-finish works around that.

Also see my current ~/.zshrc, which includes those tweaks (and many others!).

ZSH vi mode with emacs keybindings

This is my attempt at bringing emacs-style keybindings to vi mode in ZSH:

# VI MODE KEYBINDINGS (ins mode)                                      
bindkey -M viins '^a'    beginning-of-line                            
bindkey -M viins '^e'    end-of-line                                  
bindkey -M viins '^k'    kill-line                                    
bindkey -M viins '^r'    history-incremental-pattern-search-backward  
bindkey -M viins '^s'    history-incremental-pattern-search-forward   
bindkey -M viins '^p'    up-line-or-history                           
bindkey -M viins '^n'    down-line-or-history                         
bindkey -M viins '^y'    yank                                         
bindkey -M viins '^w'    backward-kill-word                           
bindkey -M viins '^u'    backward-kill-line                           
bindkey -M viins '^h'    backward-delete-char                         
bindkey -M viins '^?'    backward-delete-char                         
bindkey -M viins '^_'    undo                                         
bindkey -M viins '^x^r'  redisplay                                    
bindkey -M viins '\eOH'  beginning-of-line # Home                     
bindkey -M viins '\eOF'  end-of-line       # End                      
bindkey -M viins '\e[2~' overwrite-mode    # Insert                   
bindkey -M viins '\ef'   forward-word      # Alt-f                    
bindkey -M viins '\eb'   backward-word     # Alt-b                    
bindkey -M viins '\ed'   kill-word         # Alt-d                    
# VI MODE KEYBINDINGS (cmd mode)                                      
bindkey -M vicmd '^a'    beginning-of-line                            
bindkey -M vicmd '^e'    end-of-line                                  
bindkey -M vicmd '^k'    kill-line                                    
bindkey -M vicmd '^r'    history-incremental-pattern-search-backward  
bindkey -M vicmd '^s'    history-incremental-pattern-search-forward   
bindkey -M vicmd '^p'    up-line-or-history                           
bindkey -M vicmd '^n'    down-line-or-history                         
bindkey -M vicmd '^y'    yank                                         
bindkey -M vicmd '^w'    backward-kill-word                           
bindkey -M vicmd '^u'    backward-kill-line                           
bindkey -M vicmd '/'     vi-history-search-forward                    
bindkey -M vicmd '?'     vi-history-search-backward                   
bindkey -M vicmd '^_'    undo                                         
bindkey -M vicmd '\ef'   forward-word                      # Alt-f    
bindkey -M vicmd '\eb'   backward-word                     # Alt-b    
bindkey -M vicmd '\ed'   kill-word                         # Alt-d    
bindkey -M vicmd '\e[5~' history-beginning-search-backward # PageUp   
bindkey -M vicmd '\e[6~' history-beginning-search-forward  # PageDown

You know, so that your muscle memory can rest in peace. Also see the commit adding the above emacs style keybindings to my dotfiles.

Testing CSRF protection in Rails

Ever wanted to test your CSRF protection in a Rails app? For example, in a situation when you have a custom “remember me” cookie set and you need to overwrite Rails’ handle_unverified_request to clear it so it does not open a big security hole in your app? I know I did and it took me a while to find out how to do that, so I figured it would be good to write about it.

Here’s how to do it (in Test::Unit, but it’s the same for RSpec):

setup do                                                           
  # Enable CSRF protection in this test                            
  ActionController::Base.allow_forgery_protection = true           
teardown do                                                        
  # Disable CSRF protection for all other tests                    
  ActionController::Base.allow_forgery_protection = false          

Adding the above will make it so that the authenticity_token is added to each generated <form> element and will be required to be sent with each non GET request.