Skip to content

Instantly share code, notes, and snippets.

@jaredbeck
Created October 10, 2017 23:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredbeck/505b8d8bf4bc78932ab394596ef07127 to your computer and use it in GitHub Desktop.
Save jaredbeck/505b8d8bf4bc78932ab394596ef07127 to your computer and use it in GitHub Desktop.
A better way to diff the before/after output of rake routes
#!/usr/bin/env ruby
# frozen_string_literal: true
# Usage:
#
# ```
# bin/rake routes > tmp/routes_before
# # edit routes.rb
# bin/rake routes > tmp/routes_after
# bin/dev/route_diff.rb tmp/routes_before tmp/routes_after
# ```
#
# Example output:
#
# ```
# Route sets are not equal
# Number of routes: File 1: 380, File 2: 380
# Routes in both files: 379
# Routes in file1 not in file2: 1
# logout GET /logout(.:format) user_sessions#destroy
# Routes in file2 not in file1: 1
# logout GET /low_gut(.:format) user_sessions#destroy
# ```
require 'set'
class RouteDiff
class Route
attr_reader :name, :verb, :path, :action, :options
def initialize(name, verb, path, action, options)
@name = name.to_s.strip
@verb = verb.to_s.strip
@path = path.to_s.strip
@action = action.to_s.strip
@options = options.to_s.strip
end
# Alternate constructor
def self.parse(line)
parts = line.split
case parts.length
when 3
new(nil, *parts, nil)
when 4
new(nil, *parts)
when 5
new(*parts)
else
raise(
format(
'Unable to parse line: Expected 4-5 parts, got %d: %s',
parts.length,
line
)
)
end
end
# Because we will have two `Set`s composed of `Route`s, we must
# define `eql?` and `hash`.
def eql?(other)
@name == other.name &&
@verb == other.verb &&
@path == other.path &&
@action == other.action &&
@options == other.options
end
# Because we will have two `Set`s composed of `Route`s, we must
# define `eql?` and `hash`.
def hash
@_hash ||= [@name, @verb, @path, @action, @options].join.hash
end
def to_s
[@name, @verb, @path, @action, @options].join("\t")
end
end
class Diff
def initialize(file1, file2)
@routes1 = parse(file1)
@routes2 = parse(file2)
end
def run
if @routes1 == @routes2
puts 'Route sets are equal'
else
diff
end
end
private
def diff
puts 'Route sets are not equal'
diff_totals
diff_intersection
diff_left
diff_right
end
def diff_intersection
intersection = @routes1 & @routes2
puts format("\nRoutes in both files: %d", intersection.size)
end
def diff_left
left = @routes1 - @routes2
puts format("\nRoutes in file1 not in file2: %d", left.size)
left.each { |route| puts route }
end
def diff_right
right = @routes2 - @routes1
puts format("\nRoutes in file2 not in file1: %d", right.size)
right.each { |route| puts route }
end
def diff_totals
puts(
format(
"\nNumber of routes: File 1: %d, File 2: %d",
@routes1.size,
@routes2.size
)
)
end
def parse(file)
lines = File.readlines(file)
lines.shift # discard first line (column headers)
Set.new(lines.map { |line| Route.parse(line) })
end
end
end
RouteDiff::Diff.new(*ARGV).run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment