2010-02-01 02:27:32 +00:00
require 'open3'
module Rails
module Upgrading
class ApplicationChecker
def initialize
@issues = [ ]
raise NotInRailsAppError unless in_rails_app?
end
def in_rails_app?
File . exist? ( " config/environment.rb " )
end
# Run all the check methods
def run
the_methods = ( self . public_methods - Object . methods ) - [ " run " , " initialize " ]
the_methods . each { | m | send m }
end
# Check for deprecated ActiveRecord calls
def check_ar_methods
files = [ ]
[ " find(:all " , " find(:first " , " :conditions => " , " :joins => " ] . each do | v |
lines = grep_for ( v , " app/ " )
files += extract_filenames ( lines ) || [ ]
end
unless files . empty?
alert (
" Soon-to-be-deprecated ActiveRecord calls " ,
" Methods such as find(:all), find(:first), finds with conditions, and the :joins option will soon be deprecated. " ,
" http://m.onkey.org/2010/1/22/active-record-query-interface " ,
files
)
end
lines = grep_for ( " named_scope " , " app/models/ " )
files = extract_filenames ( lines )
if files
alert (
" named_scope is now just scope " ,
" The named_scope method has been renamed to just scope. " ,
" http://github.com/rails/rails/commit/d60bb0a9e4be2ac0a9de9a69041a4ddc2e0cc914 " ,
files
)
end
end
# Check for deprecated router syntax
def check_routes
lines = [ " map. " , " ActionController::Routing::Routes " , " .resources " ] . map do | v |
grep_for ( v , " config/routes.rb " ) . empty? ? nil : true
end . compact
unless lines . empty?
alert (
" Old router API " ,
" The router API has totally changed. " ,
" http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/ " ,
" config/routes.rb "
)
end
end
# Check for old (pre-application.rb) environment.rb file
def check_environment
unless File . exist? ( " config/application.rb " )
alert (
" New file needed: config/application.rb " ,
" You need to add a config/application.rb. " ,
" http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade " ,
" config/application.rb "
)
end
lines = grep_for ( " config. " , " config/environment.rb " )
unless lines . empty?
alert (
" Old environment.rb " ,
" environment.rb doesn't do what it used to; you'll need to move some of that into application.rb. " ,
" http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade " ,
" config/environment.rb "
)
end
end
# Check for old-style config.gem calls
def check_gems
lines = grep_for ( " config.gem " , " config/*.rb " )
files = extract_filenames ( lines )
if files
alert (
" Old gem bundling (config.gems) " ,
" The old way of bundling is gone now. You need a Gemfile for bundler. " ,
" http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade " ,
files
)
end
end
# Checks for old mailer syntax in both mailer classes and those
# classes utilizing the mailers
def check_mailers
lines = grep_for ( " deliver_ " , " app/models/ #{ base_path } app/controllers/ #{ base_path } app/observers/ " )
files = extract_filenames ( lines )
if files
alert (
" Deprecated ActionMailer API " ,
" You're using the old ActionMailer API to send e-mails in a controller, model, or observer. " ,
" http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3 " ,
files
)
end
files = [ ]
[ " recipients " , " attachment " , " subject " , " from " ] . each do | v |
lines = grep_for ( v , " app/models/ " )
files += extract_filenames ( lines ) || [ ]
end
unless files . empty?
alert (
" Old ActionMailer class API " ,
" You're using the old API in a mailer class. " ,
" http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3 " ,
files
)
end
end
# Checks for old-style generators
def check_generators
generators = Dir . glob ( base_path + " vendor/plugins/**/generators/**/ " )
unless generators . empty?
files = generators . map do | g |
grep_for ( " def manifest " , g ) . empty? ? g : nil
end . compact
if files
alert (
" Old Rails generator API " ,
" A plugin in the app is using the old generator API (a new one may be available at http://github.com/trydionel/rails3-generators). " ,
" http://blog.plataformatec.com.br/2010/01/discovering-rails-3-generators/ " ,
files
)
end
end
end
# Checks a list of known broken plugins and gems
def check_plugins
# This list is off the wiki; will need to be updated often, esp. since RSpec is working on it
bad_plugins = [ " rspec " , " rspec-rails " , " hoptoad " , " authlogic " , " nifty-generators " ,
" restful_authentication " , " searchlogic " , " cucumber " , " cucumber-rails " , " devise " ,
" inherited_resources " ]
bad_plugins = bad_plugins . map do | p |
p if File . exist? ( " #{ base_path } vendor/plugins/ #{ p } " ) || ! Dir . glob ( " #{ base_path } vendor/gems/ #{ p } -* " ) . empty?
end . compact
unless bad_plugins . empty?
alert (
" Known broken plugins " ,
" At least one plugin in your app is broken (according to the wiki). Most of project maintainers are rapidly working towards compatability, but do be aware you may encounter issues. " ,
" http://wiki.rubyonrails.org/rails/version3/plugins_and_gems " ,
bad_plugins
)
end
end
private
# Find a string in a set of files; calls +find_with_grep+ and +find_with_rak+
# depending on platform.
#
# TODO: Figure out if this works on Windows.
def grep_for ( text , where = " ./ " )
# If they're on Windows, they probably don't have grep.
@probably_has_grep || = ( Config :: CONFIG [ 'host_os' ] . downcase =~ / mswin|windows|mingw / ) . nil?
2010-02-05 03:21:58 +00:00
lines = if @probably_has_grep
2010-02-01 02:27:32 +00:00
find_with_grep ( text , base_path + where )
else
find_with_rak ( text , base_path + where )
end
2010-02-05 03:21:58 +00:00
# ignore comments
lines . gsub! / ^ \ s* # .+$ /m , " "
lines
2010-02-01 02:27:32 +00:00
end
# Sets a base path for finding files; mostly for testing
def base_path
Dir . pwd + " / "
end
# Use the grep utility to find a string in a set of files
def find_with_grep ( text , where )
value = " "
Open3 . popen3 ( " grep -r ' #{ text } ' #{ where } " ) do | stdin , stdout , stderr |
value = stdout . read
end
value
end
# Use the rak gem to grep the files (not yet implemented)
def find_with_rak ( text , where )
value = " "
Open3 . popen3 ( " rak --nogroup -l ' #{ Regexp . escape ( text ) } ' #{ where } " ) do | stdin , stdout , stderr |
value = stdout . read
end
value
end
# Extract the filenames from the grep output
def extract_filenames ( output )
if @probably_has_grep
extract_filenames_from_grep ( output )
else
extract_filenames_from_rak ( output )
end
end
def extract_filenames_from_grep ( output )
return nil if output . empty?
fnames = output . split ( " \n " ) . map do | fn |
2010-02-02 21:10:53 +00:00
if m = fn . match ( / ^(.+?): / )
m [ 1 ]
end
2010-02-01 02:27:32 +00:00
end . compact
fnames . uniq
end
def extract_filenames_from_rak ( output )
return nil if output . empty?
output . split ( " \n " ) . uniq
end
# Terminal colors, borrowed from Thor
CLEAR = " \e [0m "
BOLD = " \e [1m "
RED = " \e [31m "
YELLOW = " \e [33m "
CYAN = " \e [36m "
WHITE = " \e [37m "
# Show an upgrade alert to the user
def alert ( title , text , more_info_url , culprits )
if Config :: CONFIG [ 'host_os' ] . downcase =~ / mswin|windows|mingw /
basic_alert ( title , text , more_info_url , culprits )
else
color_alert ( title , text , more_info_url , culprits )
end
end
# Show an upgrade alert to the user. If we're on Windows, we can't
# use terminal colors, hence this method.
def basic_alert ( title , text , more_info_url , culprits )
puts " ** " + title
puts text
puts " More information: #{ more_info_url } "
puts
puts " The culprits: "
culprits . each do | c |
puts " \t - #{ c } "
end
puts
end
# Show a colorful alert to the user
def color_alert ( title , text , more_info_url , culprits )
puts " #{ RED } #{ BOLD } #{ title } #{ CLEAR } "
puts " #{ WHITE } #{ text } "
puts " #{ BOLD } More information: #{ CLEAR } #{ CYAN } #{ more_info_url } "
puts
puts " #{ WHITE } The culprits: "
culprits . each do | c |
puts " #{ YELLOW } \t - #{ c } "
end
ensure
puts " #{ CLEAR } "
end
end
end
end