In this post, I have recorded my experiences when upgrading my hobby Web Application, a ProvisioningEngine front end from Rails 4.1.x to Rails 4.2.x and ruby from 2.1.x to 2.2.x. The upgrade went relatively smoothly, apart from an additional gem needed (‚test-unit‘) and a test failure due to changes in the Rails Url helpers.
v1: 2016-01-26: original post
v2: 2016-02-13: added a caveats section at the end (just before the summary) and added two rspec caveats with respect to the exception handling
Upgrading Rails and Ruby
Prerequisites
- In this post, I assume that rbenv is installed. If you need to install rbenv, find the instructions here. Other options are possible (e.g. RVM), but they are out of scope for this blog post.
Preparation steps:
- Better do not upgrade unless you have good coverage of automated tests of your application which all pass.
- In my case, I have a small app with about ~200 rspec tests and an in-built mock that allows me to perform the tests independently of the backend systems I integrate with. Perform the tests after each major step.
- Create a backup of your system.
Upgrade steps:
- (recommended) Update rails to latest minor version and perform the automated tests. In my case I have updated rails from 4.1.4 to 4.1.14, and check that the tests are still O.K.
- (optional, depending on the current ruby version and your target rails version) Upgrade ruby to the latest stable version and perform the automated tests. In my case, I have updated ruby from 2.1.3 to 2.2.4. Since our target rails version 4.2.x supports older versions of ruby, this step was optional. However, it will be mandatory later on, when we upgrade rails to 5.0, so I have performed it now.
- (mandatory) Now we can upgrade rails to 4.2.x (you can find all rails versions here). I have decided to specify version >~ 4.2.4 in the Gemfile and we will see that the latest available 4.2.x version is fetched (4.2.5.1). Also, it is not clear yet, whether this version is supported by travisCI.
- (mandatory) resolve issues and handle deprecation warnings
Cleanup steps:
- (mandatory) save and commit changes to the SW repository
- (mandatory) integration tests and merge to master
Step 0: take backup and perform automated tests:
Perform your automated tests and confirm that they are successful. In my case (I am using rspec), this means:
$ bundle exec rspec -f d spec/requests/provisioningobjects_spec.rb
...
Finished in 40.04 seconds
196 examples, 0 failures
I am a lazy guy, so I have created a VMware Snapshot in stead of performing a backup, which helps me to go back within two minutes. Once the upgrade has proven to be successful, I will remove the snapshot in order to retain the full performance. In addition, I have made sure the SW is committed and pushed to my GitHub repository.
Step 1: update rails to the latest stable minor version:
If you are using git, I recommend to create a new branch, e.g.:
$ git checkout -b upgrade_branch
You may need to save your work before with git add/git commit or save away current changes for later use with git stash.
In the application’s root folder, edit the Gemfile and change the rails version (in my case from 4.1.4 to 4.1.14):
# Gemfile gem 'rails', '4.1.14'
Now repeat the automated tests and check the results. If successful, commit and save your work e.g. with git:
git add .; git commit -am "updated rails 4.1.4 -> 4.1.14"; git push
Step 2: update ruby
Step 2.1 update rbenv, so you get the full list of ruby versions:
cd ~/.rbenv/ git pull rbenv install --list
Step 2.2: install new ruby version
Install an available ruby version:
rbenv install 2.2.4 ruby -v
The last command will still show the old version. We still need to tell rbenv to use the new version. Since I am running a productive rails server on the host, I do not want to change the global setting, so I am specifying an app-local version, which will be written into .ruby-version:
rbenv versions rbenv local 2.2.4 rbenv versions ruby -v
Now ruby is updated.
Step 2.3: re-install rails and other gems
For this version of ruby, rails and the other gems are not yet installed. Therefore the following error message is expected:
$ rails -v rbenv: rails: command not found The `rails' command exists in these Ruby versions: 2.1.3
Since I have a working project with a Gemfile, we can install bundle first and install rails by performing a bundle update
within the project’s root folder:
gem install bundle bundle update
The latter command will install all gems specified in the Gemfile, inclulding the rails gem. In my case, I had following output:
provisioningengine@ProvisioningEngine:~/ProvisioningEnginev0.5.15_testfolder$ gem install bundle Fetching: bundler-1.11.2.gem (100%) Successfully installed bundler-1.11.2 Fetching: bundle-0.0.1.gem (100%) Successfully installed bundle-0.0.1 Parsing documentation for bundler-1.11.2 Installing ri documentation for bundler-1.11.2 Parsing documentation for bundle-0.0.1 Installing ri documentation for bundle-0.0.1 Done installing documentation for bundler, bundle after 6 seconds 2 gems installed provisioningengine@ProvisioningEngine:~/ProvisioningEnginev0.5.15_testfolder$ bundle install Fetching gem metadata from https://rubygems.org/........... Fetching version metadata from https://rubygems.org/... Fetching dependency metadata from https://rubygems.org/.. Installing rake 10.5.0 Installing i18n 0.7.0 Installing json 1.8.3 with native extensions Installing minitest 5.8.4 Installing thread_safe 0.3.5 Installing builder 3.2.2 Installing erubis 2.7.0 Installing rack 1.5.5 Installing mime-types 2.99 Installing arel 5.0.1.20140414130214 Installing addressable 2.4.0 Installing execjs 2.6.0 Installing sass 3.2.19 Installing mini_portile2 2.0.0 Installing ffi 1.9.10 with native extensions Installing choice 0.2.0 Installing coffee-script-source 1.10.0 Installing thor 0.19.1 Installing cookiejar 0.3.0 Installing daemons 1.2.3 Installing tilt 1.4.1 Installing diff-lcs 1.2.5 Installing eventmachine 1.0.9.1 with native extensions Installing http_parser.rb 0.6.0 with native extensions Installing hike 1.2.3 Installing multi_json 1.11.2 Installing pg 0.15.1 with native extensions Using bundler 1.11.2 Installing ruby-graphviz 1.2.2 Installing rails_serve_static_assets 0.0.4 Installing rails_stdout_logging 0.0.4 Installing rspec-core 2.13.1 Installing rspec-mocks 2.13.1 Installing rubyzip 0.9.9 Installing websocket 1.0.7 Installing spork 1.0.0rc4 Installing sqlite3 1.3.11 with native extensions Installing rdoc 4.2.1 Installing tzinfo 1.2.2 Installing rack-test 0.6.3 Installing rack-protection 1.5.3 Installing mail 2.6.3 Installing autoprefixer-rails 6.3.1 Installing uglifier 2.7.2 Installing nokogiri 1.6.7.2 with native extensions Installing childprocess 0.5.9 Installing coffee-script 2.4.1 Installing figaro 1.1.1 Installing rspec-expectations 2.13.0 Installing em-socksify 0.3.1 Installing sprockets 2.12.4 Installing rails_12factor 0.0.2 Installing sdoc 0.4.1 Installing activesupport 4.1.14 Installing sinatra 1.4.7 Installing bootstrap-sass 3.3.5 Installing xpath 2.0.0 Installing selenium-webdriver 2.35.1 Installing em-http-request 1.1.3 Installing actionview 4.1.14 Installing activemodel 4.1.14 Installing delayed_job 4.1.1 Installing factory_girl 4.2.0 Installing jbuilder 2.4.0 Installing capybara 2.1.0 Installing actionpack 4.1.14 Installing activerecord 4.1.14 Installing actionmailer 4.1.14 Installing railties 4.1.14 Installing kaminari 0.16.3 Installing sprockets-rails 2.3.3 Installing delayed_job_active_record 4.1.0 Installing delayed_job_web 1.2.5 Installing rails-erd 1.4.5 Installing seed_dump 3.2.4 Installing coffee-rails 4.0.1 Installing factory_girl_rails 4.2.0 Installing jquery-rails 3.1.4 Installing respond-rails 1.0.1 Installing rspec-rails 2.13.1 Installing rails 4.1.14 Installing sass-rails 4.0.5 Installing turbolinks 2.5.3 Installing spork-rails 4.0.0 Installing jquery-turbolinks 2.1.0 Bundle complete! 30 Gemfile dependencies, 85 gems now installed. Use `bundle show [gemname]` to see where a bundled gem is installed. Post-install message from ruby-graphviz: Depending on your version of ruby, you may need to install ruby rdoc/ri data: <= 1.8.6 : unsupported = 1.8.7 : gem install rdoc-data; rdoc-data --install = 1.9.1 : gem install rdoc-data; rdoc-data --install >= 1.9.2 : nothing to do! Yay! Post-install message from capybara: IMPORTANT! Some of the defaults have changed in Capybara 2.1. If you're experiencing failures, please revert to the old behaviour by setting: Capybara.configure do |config| config.match = :one config.exact_options = true config.ignore_hidden_elements = true config.visible_text_only = true end If you're migrating from Capybara 1.x, try: Capybara.configure do |config| config.match = :prefer_exact config.ignore_hidden_elements = false end Details here: http://www.elabs.se/blog/60-introducing-capybara-2-1
Step 2.4: check ruby installation
The I tried to perform the automated tests, but there are some missing dependencies:
$ bundle exec rspec -f d spec/requests/provisioningobjects_spec.rb /home/provisioningengine/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/activesupport-4.1.14/lib/active_support/dependencies.rb:247:in `require': cannot load such file -- test/unit/assertions (LoadError) from /home/provisioningengine/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/activesupport-4.1.14/lib/active_support/dependencies.rb:247:in `block in require'
8-((
What the heck is going on here?
Following the hints in the issue descriptions here and here, I have added the gem ‚test-unit‘ to the test group of the Gemfile:
# Gemfile group :test do # ... gem 'minitest' gem 'test-unit' end
and performed
bundle install
This has done the job:
$ bundle exec rspec -f d spec/requests/provisioningobjects_spec.rb
...(lots of output and debugs omitted here)...
Finished in 40.04 seconds
196 examples, 0 failures
The procedure might also work without the gem ‚minitest‘. In my case it was already installed, so I have not tried without.
Now is a good time to commit the change in git:
$ git add.; git commit -a
And I have specified:
Updated ruby to 2.2.4, re-installed all gems; rails v4.1.14 all 196 tests successful in simulation mode
Step 3: upgrade rails to v 4.2.x
In the Gemfile, I have exchanged the rails line by:
# Gemfile gem 'rails', '~> 4.2.4'
Then:
$ bundle update Fetching gem metadata from https://rubygems.org/........... Fetching version metadata from https://rubygems.org/... Fetching dependency metadata from https://rubygems.org/.. Resolving dependencies............................................................................ Using rake 10.5.0 Using i18n 0.7.0 Using json 1.8.3 Using minitest 5.8.4 Using thread_safe 0.3.5 Using builder 3.2.2 Using erubis 2.7.0 Using mini_portile2 2.0.0 Installing rack 1.6.4 (was 1.5.5) Using mime-types 2.99 Installing arel 6.0.3 (was 5.0.1.20140414130214) Using addressable 2.4.0 Using execjs 2.6.0 Using sass 3.2.19 Using bundler 1.11.2 Using ffi 1.9.10 Using choice 0.2.0 Using coffee-script-source 1.10.0 Using thor 0.19.1 Using cookiejar 0.3.0 Using daemons 1.2.3 Using tilt 1.4.1 Using diff-lcs 1.2.5 Using eventmachine 1.0.9.1 Using http_parser.rb 0.6.0 Using hike 1.2.3 Using multi_json 1.11.2 Using pg 0.15.1 Installing power_assert 0.2.7 (was 0.2.2) Using ruby-graphviz 1.2.2 Using rails_serve_static_assets 0.0.4 Using rails_stdout_logging 0.0.4 Using rspec-core 2.13.1 Using rspec-mocks 2.13.1 Using rubyzip 0.9.9 Using websocket 1.0.7 Using spork 1.0.0rc4 Using sqlite3 1.3.11 Using rdoc 4.2.1 Using tzinfo 1.2.2 Using nokogiri 1.6.7.2 Using rack-test 0.6.3 Using rack-protection 1.5.3 Using mail 2.6.3 Using autoprefixer-rails 6.3.1 Using uglifier 2.7.2 Using childprocess 0.5.9 Using coffee-script 2.4.1 Using figaro 1.1.1 Using rspec-expectations 2.13.0 Using em-socksify 0.3.1 Using sprockets 2.12.4 Installing test-unit 3.1.7 (was 3.0.8) Using rails_12factor 0.0.2 Using sdoc 0.4.1 Installing activesupport 4.2.5.1 (was 4.1.14) Installing loofah 2.0.3 Using xpath 2.0.0 Using sinatra 1.4.7 Using bootstrap-sass 3.3.5 Using selenium-webdriver 2.35.1 Using em-http-request 1.1.3 Installing rails-deprecated_sanitizer 1.0.3 Installing globalid 0.3.6 Installing activemodel 4.2.5.1 (was 4.1.14) Using delayed_job 4.1.1 Using factory_girl 4.2.0 Using jbuilder 2.4.0 Installing rails-html-sanitizer 1.0.3 Using capybara 2.1.0 Installing rails-dom-testing 1.0.7 Installing activejob 4.2.5.1 Installing activerecord 4.2.5.1 (was 4.1.14) Installing actionview 4.2.5.1 (was 4.1.14) Using delayed_job_active_record 4.1.0 Using delayed_job_web 1.2.5 Using rails-erd 1.4.5 Using seed_dump 3.2.4 Installing actionpack 4.2.5.1 (was 4.1.14) Installing actionmailer 4.2.5.1 (was 4.1.14) Installing railties 4.2.5.1 (was 4.1.14) Using kaminari 0.16.3 Using sprockets-rails 2.3.3 Using coffee-rails 4.0.1 Using factory_girl_rails 4.2.0 Installing jquery-rails 4.1.0 (was 3.1.4) Using respond-rails 1.0.1 Using rspec-rails 2.13.1 Installing rails 4.2.5.1 (was 4.1.14) Using sass-rails 4.0.5 Using turbolinks 2.5.3 Using spork-rails 4.0.0 Using jquery-turbolinks 2.1.0 Bundle updated!
We can see in the log that rails has been upgraded to the latest available 4.2 version at time of writing (4.2.5.1). Now let us test again. This time we get lots of DEPRECATION WARNINGs and an error:
bundle exec rspec -f d spec/requests/provisioningobjects_spec.rb ... DEPRECATION WARNING: Calling URL helpers with string keys controller, action is deprecated. Use symbols instead. (called from _app_views_customers__sidebar_html_erb__545434719134816655_70018526941280 at /home/provisioningengine/ProvisioningEnginev0.5.15_testfolder/app/views/customers/_sidebar.html.erb:8) should have link to 'New Customer' (FAILED - 1) Failures: 1) On target solution 'Environment2_V7R1' index should have link to 'New Customer' Failure/Error: expect(page).to have_link( "New #{obj}", href: new_provisioningobject_path(obj) + '?per_page=all') #unless obj == "Site" expected #has_link?("New Customer", {:href=>"/customers/new?per_page=all"}) to return true, got false # ./spec/requests/provisioningobjects_spec.rb:1246:in `block (5 levels) in <top (required)>' Finished in 17.13 seconds 68 examples, 1 failure Failed examples: rspec ./spec/requests/provisioningobjects_spec.rb:1241 # On target solution 'Environment2_V7R1' index should have link to 'New Customer'
Step 4.1: resolving the introduced Url-Helper issue
Okay, there is more work to be done. Maybe I should have tried with ‚bundle install‘ first, not with ‚bundle update‘, which was updating many gems at once? We could go back with a git stash and try again with bundle install. However, let us quickly see, if there is a quick solution:
The DEPRECATION WARNING is related with the problem. It points to
/home/provisioningengine/ProvisioningEnginev0.5.15_testfolder/app/views/customers/_sidebar.html.erb:8
and there we find:
<li><%= link_to 'New Customer', new_customer_path(@params) %></li>
That is causing the errored test:
expected #has_link?("New Customer" ...
So let us try to understand the DEPRECATION WARNING. By guessing from the error message, I have replaced the @params
assignment in
class ProvisioningobjectsController < ApplicationController def index @params = params
by
@params = {"per_page"=>"all", "controller"=>"customers", "action"=>"index", "target_id"=>nil}
after I had seen that a puts @params.inspect
had returned {
„per_page“=>...}
. Surprisingly, after this change the test was successful with the the DEPRECATION WARNING still present. The reasons seems to be that params
is an ActionController::Parameters
while the literal {...}
is a Hash.
The remaining warning was removed by replacing the keys by symbols:
@params = {:per_page=>"all", :controller=>"customers", :action=>"index", :target_id=>nil}
Since I do not want to care about the actual parameters, I replaced this by the more general code:
class ProvisioningobjectsController < ApplicationController def index @params = {} params.each do |key,value| @params[key.parameterize.underscore.to_sym] = value end
This will convert any ActionController::Parameters parameters
by a Hash with symbol keys.
Even more surprisingly, now all 196 test cases are successful:
$ bundle exec rspec -f d spec/requests/provisioningobjects_spec.rb ... Finished in 42.56 seconds 196 examples, 0 failures
Step 4.2: resolving DEPRECATION WARNING: You are passing an instance of ActiveRecord::Base to `find`
During the test, we have seen other deprecation warnings like follows:
DEPRECATION WARNING: You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`. (called from validate at /home/provisioningengine/ProvisioningEnginev0.5.15_testfolder/app/models/user.rb:15)
Let us resolve this as well. The waring is quite explicit and points to a line containing:
@site = Site.find(record.site)
This can easily be resolved by exchanging it by:
@site = Site.find(record.site.id)
Done and works.
Step 4.3: resolving DEPRECATION WARNING: renamed configuration option
Now let us tackle the last remaining deprecation warning:
DEPRECATION WARNING: The configuration option `config.serve_static_assets` has been renamed to `config.serve_static_files` to clarify its role (it merely enables serving everything in the `public` folder and is unrelated to the asset pipeline). The `serve_static_assets` alias will be removed in Rails 5.0. Please migrate your configuration files accordingly. (called from block in <top (required)> at /home/provisioningengine/ProvisioningEnginev0.5.15_testfolder/config/environments/test.rb:16)
We just need to exchange the line 16 in config/environments/test.rb
config.serve_static_assets = true
by
config.serve_static_files = true
and we are done with all deprecation warnings.
Step 5: cleanup: save changes, additional test with travisCI and remove snapshot
Finally, this is a good time to commit the change to git and to merge the branch to the original branch („development“ branch) and push it to GitHub. In my case, this has triggered an additional test on travisCI (if you have no access, try travisCI home).
Success.
Once real live tests with real provisioned legacy end systems have proven to be successful as well, we will merge the development branch to the master branch and remove the VMware snapshot we had taken before we started the upgrade.
Caveats
- In the new rspec version (rspec-core: 3.4.2), the exception handling seems to have changed: if there is an exception/abort in the rspec script or in the called code, the rspec just stops, but it does not indicate an error. A short google session did not lead to any results. I need to find a solution or a workaround for this.
- rspec: begin/rescue block does not work as (I had) expected: if you write a begin/rescue block like
begin something_raising_an_exception rescue some_other_code end
then the rescue is ignored, the abort message is printed and the rspec test suite is stopped.
Summary
For my hobby Web Application, a ProvisioningEngine front end, I could upgrade Rails and Ruby relatively smoothly within an afternoon+evening session (including the documentation on my blog). There only was a missing dependency ‚test-unit‘ and a changed behavior of the url helpers, which has forced me to convert the Url helper parameter from ActionController::Parameters
class ) to Hash {...}
class with the same content. In addition, the Hash keys had to be converted from "String"
to :symbol
in order to get rid of the corresponding DEPRECATION WARNING. After that, all ~200 rspec tests were successful.
I don’t think the title of your article matches the content lol. Just kidding, mainly because I had some doubts after reading the article.
Your point of view caught my eye and was very interesting. Thanks. I have a question for you.