one does not simply "upgrade to rails 3"
TRANSCRIPT
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 viewsMailer 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