ONE DOES NOT SIMPLY
UPGRADE TO RAILS 3
Adventures in Upgrading
From Rails 2 to 3
Chris McCann
@testflyjets
github.com/testflyjets
The Task
Upgrade a Rails 2.3.5 app to Rails 3.1
Ultimately upgrade to Rails 4
Why?
Benefits to Upgrading
Security patches
Ruby 2.0
New gems and capabilities, Bundler
Asset pipeline + Sass
Framework improvements, speed
Improve test coverage (spoiler: easy!)
The App
First app I ever built
Started in 2007
Rails 1.2 initially
Upgraded to 2.3.5
2nd Edition
The App
!
Monkey-patched gems
(mysql, geokit, DJ)
Lots of plugins
Prototype helpers...lots
The App
CSS mess
Homebrewed
authentication
The App
163 models
167 controllers
> 1,000 view templates
12,500 users
Over $500,000 in non-
profit transactions
Testing to the rescue!
Not so much
Resources for Upgrading
Episodes 225, 226, 227
My Hybrid Approach*
Create a new Rails 3 app
Install Rails 3 versions of gems, replace plugins
Get the framework running with config from old app
The first goal is to get to:
* Thanks to Rob Kaufman @ Notch8
$ rails console !
$ irb(main):001:0>
MySQL
Originally MySQL 5.1 + mysql gem + monkey patch
Not compatible with Rails 3 >> MySQL 5.5 + mysql2
Broke original app on dev machine because it can’t use
MySQL 5.5 (bad handshake, disconnect errors)
Solution: live with it (have 2nd dev machine)
Plugin > gem conversion
Generally not too terrible
Most plugins available as Rails 3 gems
Can still use vendor/plugins if necessary
Spoiler alert: you won’t find all the problems initially
Framework configuration
Custom configuration code moved from
environment.rb to application.rb
Initializers used extensively in Rails 3
autoload paths (like /lib) need to be specified
parameter filtering (passwords, credit card numbers)
now in application.rb not in controllers
Solution: just gut it out until things work
UDT: Upgrade Driven Testing
Tests as sanity checks
Try to rapidly touch every model to trigger failures
Tools of choice: rspec, factory_girl, spork, faker
Script to generate specs and factories for all models:
rails generate rspec:model #{model_file} -s
“Canary in a coal mine” tests
I didn’t find a quick way to check my models
Specs you don’t write are all “pending”
Hidden land mine: attr_accessible
Model changes everywhere
named_scope became scope plus syntax changes
(deprecation)
validates_* to validates (*attributes)!
Had to update for rspec matchers to work
ActiveModel find syntax changes (deprecation)
ActionMailer failer
ActionMailer methods
Mailer methods changed:
create_* and deliver_* are gone
email = Mailer.confirm(user)
email.deliver!
@body[“instance_var”] to @instance_var
ActionMailer views
Mailer views renamed:
*.text.html.erb to *.html.erb
*.plain.text.erb to *.text.erb!
*.rhtml is completely deprecated, will fail
Solution: easy to test them, trigger failures, fix ‘em
Gotcha: links in your emails? How’s your routes.rb?
Routing changes
Joys of RESTful routing
Original app had mix of RESTful and controller/action
Bad, bad, bad: Catch-all route
map.connect ‘:controller/:action/:id.:format’
Routing syntax tedium
	
  	
  	
  	
  	
  
flight.resource	
  :roster_import,	
  :controller	
  =>	
  :import,	
  	
  
	
  	
  	
  	
  	
  	
  :only	
  =>	
  [:new,	
  :create],	
  
	
  	
  	
  	
  	
  	
  :member	
  =>	
  {	
  :import_csv	
  	
  =>	
  :get,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :import_setup	
  =>	
  :get,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :match_imports	
  =>	
  :get	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
!
	
  	
  	
  	
  resource	
  :roster_import,	
  controller:	
  'import',	
  	
  
only:	
  [:new,	
  :create]	
  do	
  
	
  	
  	
  	
  	
  	
  member	
  do	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  'import_csv'	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  'import_setup'	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  'match_imports'	
  
	
  	
  	
  	
  	
  	
  end	
  
	
  	
  	
  	
  end	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
Controller Testing
Hoped for a straightforward way to sanity check
request specs vs controller specs?
rspec let() vs @var : association issues
Bottom line: steep learning curve but necessary
Asset Pipeline
Asset Pipeline partially implemented already
Managed to double-include some files
Watch out for relative paths between JS and CSS
Overall: not terrible, probably easier without “poor
man’s pipeline”
View booby traps
Views can be a pain to test
Over 1,000 view templates in the app
ERB changes to look for:
<% form_for ...%> to <%= form_for ...%>!
Using raw to escape HTML built in strings (helpers)
No more Prototype helpers
Prototype helpers removed
remote_form_for!
form_remote_tag!
link_to_remote!
remote_function!
Ajax form/links now done with remote: true
Prototype callbacks
link_to_remote(dues.email_link, !
:url => send_notices_flight_dues_path(@flight, dues),!
:before => "Element.show('spinner')",!
:complete => "Element.hide('spinner')",!
:method! => :post,!
:confirm => confirm) !
!
!
link_to(dues.email_link, !
send_notices_flight_dues_path(@flight, dues),!
remote: true, !
method: :post, !
confirm: confirm) !
Use jQuery for callbacks
application.js!
!
$("*[data-spinner]")!
.on("ajax:before", function(){ !
$($(this).data('spinner')).show(); !
})!
.on("ajax:complete", function(xhr, status){ !
$($(this).data('spinner')).hide(); !
})!
Geocoding and GMap
Used geokit, monkey-patched for exception handling
during auto-geocoding
Google killed GMaps v2 last fall
New geokit-rails for Rails 3: used v2, no “distance” field
Solution: replace geokit-rails with geocoder
tl;dr
Upgrading between major versions is hard
Not having tests makes it MUCH harder
Incremental upgrade is way easier
For a large project, it simply takes time and effort
Retrofit tests as you go -- you’ll need them someday
ONE DOES NOT SIMPLY
UPGRADE TO RAILS 3
Adventures in Upgrading
From Rails 2 to 3
Chris McCann
@testflyjets
github.com/testflyjets

One does not simply "Upgrade to Rails 3"

  • 1.
    ONE DOES NOTSIMPLY UPGRADE TO RAILS 3
  • 2.
    Adventures in Upgrading FromRails 2 to 3 Chris McCann @testflyjets github.com/testflyjets
  • 3.
    The Task Upgrade aRails 2.3.5 app to Rails 3.1 Ultimately upgrade to Rails 4
  • 4.
  • 5.
    Benefits to Upgrading Securitypatches Ruby 2.0 New gems and capabilities, Bundler Asset pipeline + Sass Framework improvements, speed Improve test coverage (spoiler: easy!)
  • 6.
    The App First appI ever built Started in 2007 Rails 1.2 initially Upgraded to 2.3.5 2nd Edition
  • 7.
    The App ! Monkey-patched gems (mysql,geokit, DJ) Lots of plugins Prototype helpers...lots
  • 8.
  • 9.
    The App 163 models 167controllers > 1,000 view templates 12,500 users Over $500,000 in non- profit transactions
  • 10.
  • 11.
  • 12.
  • 13.
    My Hybrid Approach* Createa new Rails 3 app Install Rails 3 versions of gems, replace plugins Get the framework running with config from old app The first goal is to get to: * Thanks to Rob Kaufman @ Notch8 $ rails console ! $ irb(main):001:0>
  • 15.
    MySQL Originally MySQL 5.1+ mysql gem + monkey patch Not compatible with Rails 3 >> MySQL 5.5 + mysql2 Broke original app on dev machine because it can’t use MySQL 5.5 (bad handshake, disconnect errors) Solution: live with it (have 2nd dev machine)
  • 16.
    Plugin > gemconversion Generally not too terrible Most plugins available as Rails 3 gems Can still use vendor/plugins if necessary Spoiler alert: you won’t find all the problems initially
  • 17.
    Framework configuration Custom configurationcode moved from environment.rb to application.rb Initializers used extensively in Rails 3 autoload paths (like /lib) need to be specified parameter filtering (passwords, credit card numbers) now in application.rb not in controllers Solution: just gut it out until things work
  • 18.
  • 19.
    Tests as sanitychecks Try to rapidly touch every model to trigger failures Tools of choice: rspec, factory_girl, spork, faker Script to generate specs and factories for all models: rails generate rspec:model #{model_file} -s
  • 21.
    “Canary in acoal mine” tests I didn’t find a quick way to check my models Specs you don’t write are all “pending” Hidden land mine: attr_accessible
  • 22.
    Model changes everywhere named_scopebecame scope plus syntax changes (deprecation) validates_* to validates (*attributes)! Had to update for rspec matchers to work ActiveModel find syntax changes (deprecation)
  • 23.
  • 24.
    ActionMailer methods Mailer methodschanged: create_* and deliver_* are gone email = Mailer.confirm(user) email.deliver! @body[“instance_var”] to @instance_var
  • 25.
    ActionMailer views Mailer viewsrenamed: *.text.html.erb to *.html.erb *.plain.text.erb to *.text.erb! *.rhtml is completely deprecated, will fail Solution: easy to test them, trigger failures, fix ‘em Gotcha: links in your emails? How’s your routes.rb?
  • 26.
  • 27.
    Joys of RESTfulrouting Original app had mix of RESTful and controller/action Bad, bad, bad: Catch-all route map.connect ‘:controller/:action/:id.:format’
  • 28.
    Routing syntax tedium           flight.resource  :roster_import,  :controller  =>  :import,                :only  =>  [:new,  :create],              :member  =>  {  :import_csv    =>  :get,                                        :import_setup  =>  :get,                                        :match_imports  =>  :get  }                                           !        resource  :roster_import,  controller:  'import',     only:  [:new,  :create]  do              member  do                  get  'import_csv'                  get  'import_setup'                  get  'match_imports'              end          end                                        
  • 29.
    Controller Testing Hoped fora straightforward way to sanity check request specs vs controller specs? rspec let() vs @var : association issues Bottom line: steep learning curve but necessary
  • 30.
    Asset Pipeline Asset Pipelinepartially implemented already Managed to double-include some files Watch out for relative paths between JS and CSS Overall: not terrible, probably easier without “poor man’s pipeline”
  • 31.
    View booby traps Viewscan be a pain to test Over 1,000 view templates in the app ERB changes to look for: <% form_for ...%> to <%= form_for ...%>! Using raw to escape HTML built in strings (helpers)
  • 32.
  • 33.
  • 34.
    Prototype callbacks link_to_remote(dues.email_link, ! :url=> send_notices_flight_dues_path(@flight, dues),! :before => "Element.show('spinner')",! :complete => "Element.hide('spinner')",! :method! => :post,! :confirm => confirm) ! ! ! link_to(dues.email_link, ! send_notices_flight_dues_path(@flight, dues),! remote: true, ! method: :post, ! confirm: confirm) !
  • 35.
    Use jQuery forcallbacks application.js! ! $("*[data-spinner]")! .on("ajax:before", function(){ ! $($(this).data('spinner')).show(); ! })! .on("ajax:complete", function(xhr, status){ ! $($(this).data('spinner')).hide(); ! })!
  • 37.
    Geocoding and GMap Usedgeokit, monkey-patched for exception handling during auto-geocoding Google killed GMaps v2 last fall New geokit-rails for Rails 3: used v2, no “distance” field Solution: replace geokit-rails with geocoder
  • 38.
    tl;dr Upgrading between majorversions is hard Not having tests makes it MUCH harder Incremental upgrade is way easier For a large project, it simply takes time and effort Retrofit tests as you go -- you’ll need them someday
  • 39.
    ONE DOES NOTSIMPLY UPGRADE TO RAILS 3
  • 40.
    Adventures in Upgrading FromRails 2 to 3 Chris McCann @testflyjets github.com/testflyjets