Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
# I found this code I wrote in an old project of mine (written around Sept 2008). The project upgraded to a new Rails version and I had to find the routes with errors.
# core extensions
class Array
def bsearch_lower_boundary(key)
range = 0 ... self.length
lower = range.first - 1
upper = range.last
while lower + 1 != upper
mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
if (self[mid] <=> key) < 0
lower = mid
else
upper = mid
end
end
return upper
end
def bsearch_first(key)
boundary = bsearch_lower_boundary(key)
if boundary >= self.length || (self[boundary] <=> key) != 0
return nil
else
return boundary
end
end
def permutation(n=size)
if size < n or n < 0
elsif n == 0
yield([])
else
self[1..-1].permutation(n - 1) do |x|
(0...n).each do |i|
yield(x[0...i] + [first] + x[i..-1])
end
end
self[1..-1].permutation(n) do |x|
yield(x)
end
end
end
end
# Reports invalid routes due to Rails upgrade.
# Warning: This generator currently forces user to clear routes_report.txt to make sure the generator
# won't overwrite a previous report.
class RoutesReportGenerator
ROUTES_REPORT_FILENAME = File.join(RAILS_ROOT, 'routes_report.txt')
HASH_FOR_PREFIX = 'hash_for_'
def initialize
@dirs_to_scan = ["vendor/plugins/app_core/app/views",
"vendor/plugins/app_core/app/controllers",
"vendor/plugins/app_core/app/helpers"]
@exclude_list = ["polymorphic_path", # Rails helper
"poly_tell_a_friend_url", "poly_comment_url", "full_url", "embed_url", "image_url", # variables
"infer_page_from_url", "render_path", "document_download_path", # method
"upload_url", "flash_url", # javascript properties
"new_url"] # fark.com URL parameter
@valid_named_routes = ActionController::Routing::Routes.named_routes.names.collect {|x| x.to_s}.sort
@found_named_routes = []
@error_routes = []
@fix_suggestions = []
prepare_file(ROUTES_REPORT_FILENAME)
end
def run
@dirs_to_scan.each do |dir_path|
real_dir_path = File.join(RAILS_ROOT, dir_path)
scan_directory_for_routes(real_dir_path)
end
generate_routes_report
end
private
def scan_directory_for_routes(dir)
all_files = File.join(dir, '**', '*')
Dir[all_files].each do |filename|
if File.directory?(filename)
@dirs_to_scan << filename
elsif File.file?(filename)
if File.readable?(filename)
File.open(filename) do |f|
f.each do |line|
# Note: cannot use the grouping parenthesis as it will mess up the scan
matches = line.scan(/[^a-z.:_@]([a-z_]+_path|[a-z_]+_url)[^a-z_]/).flatten
matches.each do |match|
check_named_route(match)
end
end
end
else
# display warning: an unreadable file cannot be processed
end
end
end
end
def check_named_route(named_route)
unless @found_named_routes.include?(named_route) || @exclude_list.include?(named_route)
@found_named_routes << named_route
unless valid_named_route?(named_route)
@error_routes << named_route
@fix_suggestions << suggest_valid_names(named_route)
end
end
end
def valid_named_route?(named_route)
base_named_route = named_route.chomp("_path").chomp("_url")
if @valid_named_routes.bsearch_first(base_named_route).nil?
if base_named_route.starts_with?(HASH_FOR_PREFIX) &&
!@valid_named_routes.bsearch_first(base_named_route[HASH_FOR_PREFIX.length, base_named_route.length-1]).nil?
return true
else
return false
end
else
return true
end
end
def generate_routes_report
File.open(ROUTES_REPORT_FILENAME, 'w') do |output_file|
output_file.puts "OK Routes:"
ok_routes = @found_named_routes - @error_routes
ok_routes.each do |named_route|
output_file.puts named_route
end
output_file.puts "\nRoutes with errors:" unless @error_routes.empty?
no_suggestion_count = 0
@error_routes.each_with_index do |named_route, index|
output_file.print named_route
if @fix_suggestions[index].blank?
no_suggestion_count += 1
else
output_file.print " -> #{@fix_suggestions[index]}"
end
output_file.print "\n"
end
output_file.puts "\nInvalid named routes with no fix suggestion: #{no_suggestion_count}"
end
end
def prepare_file(filename)
raise("A non-empty #{filename} exists!") if FileTest::exists?(filename) && !(FileTest::zero?(filename))
f = File.new(filename, 'w')
f.close
end
def suggest_valid_names(named_route)
words = named_route.split('_')
# pops _path or _url out of the words
suffix = words.pop
suggestions = []
words.uniq.permutation do |p|
key = p.join('_')
unless @valid_named_routes.bsearch_first(key).nil?
suggestions << "#{key}_#{suffix}"
end
end
suggestions.join(' ')
end
end
# Notes:
# 1. script/console errors
# 2. Design script
# 3. Use Dir[].each instead of Dir.each
# 4. Scan all matches instead of 1 match per line
# 5. Use collections instead of files. Only use file for final report.
# 6. Using eval("app.#{named_route}") won't work,
# use ActionController::Routing::Routes.named_routes.names
# 7. Convert to rake task
# 8. Add option to manually list strings to be excluded from the matching?
# a. exclude: polymorphic_path
# 9. Suggest alternative path names
# a. permutations of invalid named routes are checked against the list of valid ones
# b. "hash_for_", try removing hash_for <--
# 10. Handle instance variables by matching only named routes not preceded by @
# Weird: name_prefix => nil is not working for nested map.resources
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.