Created
October 10, 2017 23:36
-
-
Save jaredbeck/505b8d8bf4bc78932ab394596ef07127 to your computer and use it in GitHub Desktop.
A better way to diff the before/after output of rake routes
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
#!/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