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.
Gemfile
gem 'rails', :github => 'rails/rails', :branch => '2-3-stable' gem 'rake' gem 'json' gem 'iconv' group :test do gem 'test-unit', '1.2.3' end
And then:
$ bundle update rails rake json iconv test-unit
Rakefile
Remove the following line:
require 'rake/rdoctask'
config/boot.rb
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 else raise end
config/deploy.rb
Update your Ruby version used for Capistrano deployments. Only if you’re using Capistrano with RVM.
set :rvm_ruby_string, '2.1.8'
config/environment.rb
Change hardcoded Rails version:
RAILS_GEM_VERSION = '2.3.18' unless defined? RAILS_GEM_VERSION
Before Rails::Initializer.run
block add the following:
# Rails 2.3 and Ruby 2.0+ compatibility hack # http://blog.lucascaton.com.br/index.php/2014/02/28/have-a-rails-2-app-you-can-run-it-on-the-newest-ruby/ if RUBY_VERSION >= '2.0.0' module Gem def self.source_index sources end def self.cache sources end 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 end end end
Additional initializers
config/initializers/active_record_callbacks_ruby2.rb
:
# ActiveRecord::Callbacks compatibility fix # http://blog.lucascaton.com.br/index.php/2014/02/28/have-a-rails-2-app-you-can-run-it-on-the-newest-ruby/ module ActiveRecord module Callbacks private 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) end notify(method) return result end end end
config/initializers/backport_3937.rb
:
# Encoding issues with Ruby 2+ # backport #3937 to Rails 2.3.8 # https://github.com/rails/rails/pull/3937/files class ERB module Util def html_escape(s) s = s.to_s if s.html_safe? s else silence_warnings { s.gsub(/[&"'><]/n) { |special| HTML_ESCAPE[special] }.html_safe } end end end end
config/initializers/i18n_ruby2.rb
:
# Patch to make i18n work with Ruby 2+ # http://blog.lucascaton.com.br/index.php/2014/02/28/have-a-rails-2-app-you-can-run-it-on-the-newest-ruby/ module I18n module Backend module Base def load_file(filename) type = File.extname(filename).tr('.', '').downcase raise UnknownFileType.new(type, 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) } end end end end
config/initializers/rails_generators.rb
:
# 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. # http://blog.lucascaton.com.br/index.php/2014/02/28/have-a-rails-2-app-you-can-run-it-on-the-newest-ruby/ 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 ERB.new(file.read, nil, '-').result(b) end end end end
config/initializers/ruby2.rb
:
# 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. # http://blog.lucascaton.com.br/index.php/2014/02/28/have-a-rails-2-app-you-can-run-it-on-the-newest-ruby/ 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) super else load_target @target.send(method, *args) end end end end end end
config/initializers/string_encodings.rb
:
# 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
config/initializers/utf8_params.rb
# convert all params into UTF-8 (from ASCII-8BIT) # http://jasoncodes.com/posts/ruby19-rails2-encodings 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| traverse.call(o, block) } elsif object.kind_of?(Array) object.each { |o| traverse.call(o, block) } else block.call(object) end object end force_encoding = lambda do |o| o.force_encoding(Encoding::UTF_8) if o.respond_to?(:force_encoding) end traverse.call(params, force_encoding) end before_filter :force_utf8_params end
References: