Created
March 26, 2010 06:57
-
-
Save mikong/344609 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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