Migrating PriceChirp to Rails 3.0
Steven Evatt
Blog: http://www.evatt.com/blog
Web: http://PriceChirp.com
Twitter: @sevatt
Houston-RoR Nov 2010
The pain points
2
Today We'll Cover:

Why Upgrade?

Pain points / Issues

Tips

Take Aways
3
Full Disclosure

PriceChirp is my main side project designed to
help people (myself included) save money on
Amazon

My background is in Perl
4
Why???

Gem ecosystem is starting to require Rails 3.0

Too many deprecation notices
5
How much work was it?

3 weeks of nights / weekends

According to svn

Paths added 67

Paths modified 112

Paths removed 45

Replaced Restful_Authentication with Devise

Converted from Prototype to UJS jQuery
6
Where to start?

Upgrade to Rails 2.3.9

Upgrade Gems

Fix code to remove deprecation message

Bundler

Have test for critical functionality

Install Rails_upgrade plugin

Make a new branch in your source control
7
Source Control

Svn copy http://svn...com/pricechirp/trunk/
http://svn...com/pricechirp/branches/migrate_to_rails
3

Svn checkout
http://svn...com/pricechirp/branches/migrate_to_rails
3 migrate
8
Plugin → Rails_upgrade

Documentation out of order and gives wrong
syntax for rails command

Mostly run from rails 2.3

Useful rake tasks

Rake rails:upgrade:check

Rake rails:upgrade:backup

Rake rails:upgrade:routes

Rake rails:upgrade:gems

Rake rails:upgrade:configuration
9
Installing Rails 3

Rails new .

Creates a rails3 project
with the name of the
current directory

If directory name is
migrate:

Application name is
Migrate::Application

Must update in 12 places
to change
10
Bundler

Bundler manages an application's dependencies
through its entire life across many machines
systematically and repeatably.

Gemfile

gem “rack”, “~>1.1”

gem “rspec, :requires => “spec”

It doesn’t take long before you want to use
bundler on all projects
11
Bundler II

Passenger is not ignoring my Gemfile :test,
:development block in production

To get bundler to work with capistrano:

Add to deploy.rb:
require "bundler/capistrano"
12
Upgrade Gems

gem ‘will_paginate’, ‘~> 3.0.pre2’, :require =>
‘will_paginate’

factory_girl_rails => compatibility mode
13
Passenger

Apache configuration changed

from:
RailsEnv development

to:
RackEnv development
14
Upgrade Plugins

Restful Authentication → Devise

copy crypted_password => encrypted_password

copy salt => encrypted_salt

set confirmed date

nil is a valid salt with Restful Auth, not in Devise

Change helper methods
− :login_required → :authenticate_user!
− logged_in? → user_signed_in?

Exception_notification
15
RAILS_ENV and RAILS_ROOT
 RAILS_ENV → Rails.env
 Rails.env.production?
− If RAILS_ENV == “production”
− If Rails.env.production?
 RAILS_ROOT → Rails.root
 RAILS_CACHE → Rails.cache
16
XSS protection is everywhere

Learn to love .html_safe.

All strings are not safe until flagged as safe.
17
Views

forms helpers changed from <% to <%=

removed error_messages_for

link_to_remote

Use :remote => true

link_to
18
UJS / jQuery

Converted rjs to erb

Converted from Prototype to jQuery

Get jQuery
− http://docs.jquery.com/Downloading_jQuery
− /public/javascripts/jquery-1.4.2.min.js

Copy rails.js from
github.com/rails/jquery-ujs/raw/master/src/rails.js

:remote => true
19
Active Record Changes

passing options hash containing :conditions,
:include, :joins, :limit, :offset, :order,
:select, :readonly, :group, :having, :from,
:lock to any of the ActiveRecord provided
class methods, is now deprecated.

Find → where

find_by_x('xxx') => where(:x => ‘xxx’).first

where(‘b like ?’, value)
20
Active Record “Bug”

select('distinct(x)') is ignored when passed to
.count
Example
Item.where('updated_at < ?',
1.days.ago ).select('distinct(asin)').all
Produces the following query:
Item Load (6.3ms) SELECT distinct(asin) FROM `items`
WHERE (updated_at < '2010-10-16 01:06:05')
21
Active Record “Bug” II
Now, when we use .count instead of .all, the .select call
is ignored:
Item.where('updated_at < ?',
1.days.ago ).select('distinct(asin)').count
Yields:
SQL (5.4ms) SELECT COUNT(*) AS count_id FROM
(SELECT 1 FROM `items` WHERE (updated_at <
'2010-10-16 01:09:55')) AS subquery
22
Active Record “Bug” III

Work around
Put a .all before the .count
Item.where('updated_at < ?',
1.days.ago ).select('distinct(asin)').all.count
Issue
If you attempt to use :group ie .count(:group => :asin) you
get an error

More Info on “bug”

https://rails.lighthouseapp.com/projects/8994/
tickets/5698-select-ignored-in-subquery-
when-querying-with-count
23
Custom to_s formatting for dates
Example: Time.now.to_s(:js)

From:
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FOR
MATS.merge!(:js => ‘%m %d, %Y %H:%M:%S’)

To:
Time::DATE_FORMATS[:js] = ‘%m %d, %Y %H:%M:%S’
24
Logging Rails.cache Activity

By default, Rails3 does not log Rails.cache activity
Cache read: user.default
Cache fetch_hit: user.default
Cache read: user.count ({:expires_in=>300 seconds})
Cache generate: user.count ({:expires_in=>300 seconds})
Cache write: user.count ({:expires_in=>300 seconds})

To enable logging
In environment.rb
Rails.cache.logger ||= Rails.logger
25
Action Mailer

Action_mailer defaults to :smtp although
everything says it defaults to :sendmail
config.action_mailer.delivery_method = :sendmail

Action_mailer requires
default_url_options[:host] to be defined
config.action_mailer.default_url_options[:host] = {
:host => ‘pricechirp.com’ }
26
script/* replaced by script/rails

script/rails replaces all the scripts that used to
be in the script directory
Safe to remove: about, console, dbconsole, destroy
generate, plugin, runner, and server
27
RSS

Had to wrap my
“xml.description” output
in a CDATA block for it
to display
<![CDATA[
…
]]>
28
Validate-on-callback methods

validate_on_create
validate :x, :on => :create
29
Remove Debugging Statements

logger.info request.inspect
Rails 2.3.9 Rails 3.0
request.inspect 5.9k 330k
30
Mime::Type
Error:
A ActionView::MissingTemplate occurred in pages#index:
Missing template pages/index with {:locale=>[:en, :en],
:handlers=>[:builder, :erb, :rjs, :rhtml, :rxml], :formats=>[:"text/*"]} in
view paths ….
actionpack (3.0.1) lib/action_view/paths.rb:15:in `find'
Solution:
In /config/initializers/mime_types.rb, add:
Mime::Type.register “text/*”, :html
31
Current Issues I
A ActionController::UnknownHttpMethod occurred in #:
PROPFIND, accepted HTTP methods are get, head, put,
post, delete, and options actionpack (3.0.1)
lib/action_dispatch/http/request.rb:59:in `request_method'
32
Current Issues II
Bundler – During a “cap deploy”
...
[pricechirp.com] executing command
** [out :: pricechirp.com] (in /home/pricechirp)
** [out :: pricechirp.com] Could not find gem 'rspec-rails (>= 2.0.1, runtime)' in any of
the gem sources.
** [out :: pricechirp.com] Try running `bundle install`.
command finished
*** [deploy:update_code] rolling back
...
33
Take Aways

Upgrade / fix deprecated code as
best you can before you start

Use Rails_upgrade plugin

XSS Protection is everywhere

Syntax changes:

Views form helpers

Link_to_remote replaced with :remote => true

Active Record changes: .find → .where

Validate-on-callback

Configurations

There is a lot of deprecated
advice out there, remember
to check the dates
34
Thanks for Coming!
Steven Evatt
Email: steven@evatt.com
Site: http://PriceChirp.com
Blog: http://www.evatt.com/blog
Twitter: @sevatt

Migrating PriceChirp to Rails 3.0: The Pain Points

  • 1.
    Migrating PriceChirp toRails 3.0 Steven Evatt Blog: http://www.evatt.com/blog Web: http://PriceChirp.com Twitter: @sevatt Houston-RoR Nov 2010 The pain points
  • 2.
    2 Today We'll Cover:  WhyUpgrade?  Pain points / Issues  Tips  Take Aways
  • 3.
    3 Full Disclosure  PriceChirp ismy main side project designed to help people (myself included) save money on Amazon  My background is in Perl
  • 4.
    4 Why???  Gem ecosystem isstarting to require Rails 3.0  Too many deprecation notices
  • 5.
    5 How much workwas it?  3 weeks of nights / weekends  According to svn  Paths added 67  Paths modified 112  Paths removed 45  Replaced Restful_Authentication with Devise  Converted from Prototype to UJS jQuery
  • 6.
    6 Where to start?  Upgradeto Rails 2.3.9  Upgrade Gems  Fix code to remove deprecation message  Bundler  Have test for critical functionality  Install Rails_upgrade plugin  Make a new branch in your source control
  • 7.
    7 Source Control  Svn copyhttp://svn...com/pricechirp/trunk/ http://svn...com/pricechirp/branches/migrate_to_rails 3  Svn checkout http://svn...com/pricechirp/branches/migrate_to_rails 3 migrate
  • 8.
    8 Plugin → Rails_upgrade  Documentationout of order and gives wrong syntax for rails command  Mostly run from rails 2.3  Useful rake tasks  Rake rails:upgrade:check  Rake rails:upgrade:backup  Rake rails:upgrade:routes  Rake rails:upgrade:gems  Rake rails:upgrade:configuration
  • 9.
    9 Installing Rails 3  Railsnew .  Creates a rails3 project with the name of the current directory  If directory name is migrate:  Application name is Migrate::Application  Must update in 12 places to change
  • 10.
    10 Bundler  Bundler manages anapplication's dependencies through its entire life across many machines systematically and repeatably.  Gemfile  gem “rack”, “~>1.1”  gem “rspec, :requires => “spec”  It doesn’t take long before you want to use bundler on all projects
  • 11.
    11 Bundler II  Passenger isnot ignoring my Gemfile :test, :development block in production  To get bundler to work with capistrano:  Add to deploy.rb: require "bundler/capistrano"
  • 12.
    12 Upgrade Gems  gem ‘will_paginate’,‘~> 3.0.pre2’, :require => ‘will_paginate’  factory_girl_rails => compatibility mode
  • 13.
  • 14.
    14 Upgrade Plugins  Restful Authentication→ Devise  copy crypted_password => encrypted_password  copy salt => encrypted_salt  set confirmed date  nil is a valid salt with Restful Auth, not in Devise  Change helper methods − :login_required → :authenticate_user! − logged_in? → user_signed_in?  Exception_notification
  • 15.
    15 RAILS_ENV and RAILS_ROOT RAILS_ENV → Rails.env  Rails.env.production? − If RAILS_ENV == “production” − If Rails.env.production?  RAILS_ROOT → Rails.root  RAILS_CACHE → Rails.cache
  • 16.
    16 XSS protection iseverywhere  Learn to love .html_safe.  All strings are not safe until flagged as safe.
  • 17.
    17 Views  forms helpers changedfrom <% to <%=  removed error_messages_for  link_to_remote  Use :remote => true  link_to
  • 18.
    18 UJS / jQuery  Convertedrjs to erb  Converted from Prototype to jQuery  Get jQuery − http://docs.jquery.com/Downloading_jQuery − /public/javascripts/jquery-1.4.2.min.js  Copy rails.js from github.com/rails/jquery-ujs/raw/master/src/rails.js  :remote => true
  • 19.
    19 Active Record Changes  passingoptions hash containing :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :having, :from, :lock to any of the ActiveRecord provided class methods, is now deprecated.  Find → where  find_by_x('xxx') => where(:x => ‘xxx’).first  where(‘b like ?’, value)
  • 20.
    20 Active Record “Bug”  select('distinct(x)')is ignored when passed to .count Example Item.where('updated_at < ?', 1.days.ago ).select('distinct(asin)').all Produces the following query: Item Load (6.3ms) SELECT distinct(asin) FROM `items` WHERE (updated_at < '2010-10-16 01:06:05')
  • 21.
    21 Active Record “Bug”II Now, when we use .count instead of .all, the .select call is ignored: Item.where('updated_at < ?', 1.days.ago ).select('distinct(asin)').count Yields: SQL (5.4ms) SELECT COUNT(*) AS count_id FROM (SELECT 1 FROM `items` WHERE (updated_at < '2010-10-16 01:09:55')) AS subquery
  • 22.
    22 Active Record “Bug”III  Work around Put a .all before the .count Item.where('updated_at < ?', 1.days.ago ).select('distinct(asin)').all.count Issue If you attempt to use :group ie .count(:group => :asin) you get an error  More Info on “bug”  https://rails.lighthouseapp.com/projects/8994/ tickets/5698-select-ignored-in-subquery- when-querying-with-count
  • 23.
    23 Custom to_s formattingfor dates Example: Time.now.to_s(:js)  From: ActiveSupport::CoreExtensions::Time::Conversions::DATE_FOR MATS.merge!(:js => ‘%m %d, %Y %H:%M:%S’)  To: Time::DATE_FORMATS[:js] = ‘%m %d, %Y %H:%M:%S’
  • 24.
    24 Logging Rails.cache Activity  Bydefault, Rails3 does not log Rails.cache activity Cache read: user.default Cache fetch_hit: user.default Cache read: user.count ({:expires_in=>300 seconds}) Cache generate: user.count ({:expires_in=>300 seconds}) Cache write: user.count ({:expires_in=>300 seconds})  To enable logging In environment.rb Rails.cache.logger ||= Rails.logger
  • 25.
    25 Action Mailer  Action_mailer defaultsto :smtp although everything says it defaults to :sendmail config.action_mailer.delivery_method = :sendmail  Action_mailer requires default_url_options[:host] to be defined config.action_mailer.default_url_options[:host] = { :host => ‘pricechirp.com’ }
  • 26.
    26 script/* replaced byscript/rails  script/rails replaces all the scripts that used to be in the script directory Safe to remove: about, console, dbconsole, destroy generate, plugin, runner, and server
  • 27.
    27 RSS  Had to wrapmy “xml.description” output in a CDATA block for it to display <![CDATA[ … ]]>
  • 28.
  • 29.
    29 Remove Debugging Statements  logger.inforequest.inspect Rails 2.3.9 Rails 3.0 request.inspect 5.9k 330k
  • 30.
    30 Mime::Type Error: A ActionView::MissingTemplate occurredin pages#index: Missing template pages/index with {:locale=>[:en, :en], :handlers=>[:builder, :erb, :rjs, :rhtml, :rxml], :formats=>[:"text/*"]} in view paths …. actionpack (3.0.1) lib/action_view/paths.rb:15:in `find' Solution: In /config/initializers/mime_types.rb, add: Mime::Type.register “text/*”, :html
  • 31.
    31 Current Issues I AActionController::UnknownHttpMethod occurred in #: PROPFIND, accepted HTTP methods are get, head, put, post, delete, and options actionpack (3.0.1) lib/action_dispatch/http/request.rb:59:in `request_method'
  • 32.
    32 Current Issues II Bundler– During a “cap deploy” ... [pricechirp.com] executing command ** [out :: pricechirp.com] (in /home/pricechirp) ** [out :: pricechirp.com] Could not find gem 'rspec-rails (>= 2.0.1, runtime)' in any of the gem sources. ** [out :: pricechirp.com] Try running `bundle install`. command finished *** [deploy:update_code] rolling back ...
  • 33.
    33 Take Aways  Upgrade /fix deprecated code as best you can before you start  Use Rails_upgrade plugin  XSS Protection is everywhere  Syntax changes:  Views form helpers  Link_to_remote replaced with :remote => true  Active Record changes: .find → .where  Validate-on-callback  Configurations  There is a lot of deprecated advice out there, remember to check the dates
  • 34.
    34 Thanks for Coming! StevenEvatt Email: steven@evatt.com Site: http://PriceChirp.com Blog: http://www.evatt.com/blog Twitter: @sevatt