Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 3 additions & 77 deletions exe/dead_end
Original file line number Diff line number Diff line change
@@ -1,81 +1,7 @@
#!/usr/bin/env ruby

require "pathname"
require "optparse"
require_relative "../lib/dead_end"

options = {}
options[:record_dir] = ENV["DEAD_END_RECORD_DIR"]

parser = OptionParser.new do |opts|
opts.banner = <<~EOM
Usage: dead_end <file> [options]

Parses a ruby source file and searches for syntax error(s) such as
unexpected `end', expecting end-of-input.

Example:

$ dead_end dog.rb

# ...

❯ 10 defdog
❯ 15 end
❯ 16

Env options:

DEAD_END_RECORD_DIR=<dir>

When enabled, records the steps used to search for a syntax error to the
given directory

Options:
EOM

opts.version = DeadEnd::VERSION

opts.on("--help", "Help - displays this message") do |v|
puts opts
exit
end

opts.on("--record <dir>", "When enabled, records the steps used to search for a syntax error to the given directory") do |v|
options[:record_dir] = v
end

opts.on("--terminal", "Enable terminal highlighting") do |v|
options[:terminal] = true
end

opts.on("--no-terminal", "Disable terminal highlighting") do |v|
options[:terminal] = false
end
end
parser.parse!

file = ARGV[0]

if file.nil? || file.empty?
# Display help if raw command
parser.parse! %w[--help]
end

file = Pathname(file)
options[:record_dir] = "tmp" if ENV["DEBUG"]

warn "Record dir: #{options[:record_dir]}" if options[:record_dir]

display = DeadEnd.call(
source: file.read,
filename: file.expand_path,
terminal: options.fetch(:terminal, DeadEnd::DEFAULT_VALUE),
record_dir: options[:record_dir]
)

if display.document_ok?
exit(0)
else
exit(1)
end
DeadEnd::Cli.new(
argv: ARGV
).call
1 change: 1 addition & 0 deletions lib/dead_end.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,4 @@ def self.valid?(source)
require_relative "dead_end/explain_syntax"

require_relative "dead_end/auto"
require_relative "dead_end/cli"
118 changes: 118 additions & 0 deletions lib/dead_end/cli.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# frozen_string_literal: true

require "pathname"
require "optparse"

module DeadEnd
# All the logic of the exe/dead_end CLI in one handy spot
#
# Cli.new(argv: ["--help"]).call
# Cli.new(argv: ["<path/to/file>.rb"]).call
# Cli.new(argv: ["<path/to/file>.rb", "--record=tmp"]).call
# Cli.new(argv: ["<path/to/file>.rb", "--terminal"]).call
#
class Cli
attr_accessor :options, :file_name

# ARGV is Everything passed to the executable, does not include executable name
#
# All other intputs are dependency injection for testing
def initialize(argv:, exit_obj: Kernel, io: $stdout, env: ENV)
@options = {}
@parser = nil
options[:record_dir] = env["DEAD_END_RECORD_DIR"]
options[:record_dir] = "tmp" if env["DEBUG"]
options[:terminal] = DeadEnd::DEFAULT_VALUE

@io = io
@argv = argv
@file_name = argv[0]
@exit_obj = exit_obj
end

def call
if file_name.nil? || file_name.empty?
# Display help if raw command
parser.parse! %w[--help]
else
parse
end

# Needed for testing since we fake exit
return if options[:exit]

file = Pathname(file_name)

@io.puts "Record dir: #{options[:record_dir]}" if options[:record_dir]

display = DeadEnd.call(
io: @io,
source: file.read,
filename: file.expand_path,
terminal: options.fetch(:terminal, DeadEnd::DEFAULT_VALUE),
record_dir: options[:record_dir]
)

if display.document_ok?
@exit_obj.exit(0)
else
@exit_obj.exit(1)
end
end

def parse
parser.parse!(@argv)

self
end

def parser
@parser ||= OptionParser.new do |opts|
opts.banner = <<~EOM
Usage: dead_end <file> [options]

Parses a ruby source file and searches for syntax error(s) such as
unexpected `end', expecting end-of-input.

Example:

$ dead_end dog.rb

# ...

❯ 10 defdog
❯ 15 end

ENV options:

DEAD_END_RECORD_DIR=<dir>

Records the steps used to search for a syntax error
to the given directory

Options:
EOM

opts.version = DeadEnd::VERSION

opts.on("--help", "Help - displays this message") do |v|
@io.puts opts
options[:exit] = true
@exit_obj.exit
end

opts.on("--record <dir>", "Records the steps used to search for a syntax error to the given directory") do |v|
options[:record_dir] = v
end

opts.on("--terminal", "Enable terminal highlighting") do |v|
options[:terminal] = true
end

opts.on("--no-terminal", "Disable terminal highlighting") do |v|
options[:terminal] = false
end
end
end
end
end
21 changes: 21 additions & 0 deletions spec/integration/dead_end_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,26 @@ module DeadEnd
debug_display(io.string)
debug_display(benchmark)
end

it "handles heredocs" do
lines = fixtures_dir.join("rexe.rb.txt").read.lines
lines.delete_at(85 - 1)
io = StringIO.new
DeadEnd.call(
io: io,
source: lines.join
)

out = io.string
debug_display(out)

expect(out).to include(<<~EOM)
16 class Rexe
❯ 77 class Lookups
❯ 78 def input_modes
❯ 148 end
551 end
EOM
end
end
end
61 changes: 0 additions & 61 deletions spec/integration/exe_cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,67 +14,6 @@ def exe(cmd)
out
end

it "parses valid code" do
ruby_file = exe_path
out = exe(ruby_file)
expect(out.strip).to include("Syntax OK")
expect($?.success?).to be_truthy
end

it "parses invalid code" do
ruby_file = fixtures_dir.join("this_project_extra_def.rb.txt")
out = exe(ruby_file)
debug_display(out)

expect(out.strip).to include("❯ 36 def filename")
expect($?.success?).to be_falsey
end

it "handles heredocs" do
lines = fixtures_dir.join("rexe.rb.txt").read.lines
Tempfile.create do |file|
lines.delete_at(85 - 1)

Pathname(file.path).write(lines.join)

out = exe(file.path)
debug_display(out)

expect(out).to include(<<~EOM)
16 class Rexe
❯ 77 class Lookups
❯ 78 def input_modes
❯ 148 end
551 end
EOM
end
end

# When ruby sub shells it is not a interactive shell and dead_end will
# default to no coloring. Colors/bold can be forced with `--terminal`
# flag
it "passing --terminal will force color codes" do
ruby_file = fixtures_dir.join("this_project_extra_def.rb.txt")
out = exe("#{ruby_file} --terminal")

expect(out.strip).to include("\e[0m❯ 36 \e[1;3m def filename")
end

it "records search" do
Dir.mktmpdir do |dir|
dir = Pathname(dir)
tmp_dir = dir.join("tmp").tap(&:mkpath)
ruby_file = dir.join("file.rb")
ruby_file.write("def foo\n end\nend")

expect(tmp_dir).to be_empty

exe("#{ruby_file} --record #{tmp_dir}")

expect(tmp_dir).to_not be_empty
end
end

it "prints the version" do
out = exe("-v")
expect(out.strip).to include(DeadEnd::VERSION)
Expand Down
34 changes: 17 additions & 17 deletions spec/integration/ruby_command_line_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,32 @@ module DeadEnd
RSpec.describe "Requires with ruby cli" do
it "namespaces all monkeypatched methods" do
Dir.mktmpdir do |dir|
@tmpdir = Pathname(dir)
@script = @tmpdir.join("script.rb")
@script.write <<~'EOM'
tmpdir = Pathname(dir)
script = tmpdir.join("script.rb")
script.write <<~'EOM'
puts Kernel.private_methods
EOM
dead_end_methods_file = tmpdir.join("dead_end_methods.txt")
kernel_methods_file = tmpdir.join("kernel_methods.txt")

dead_end_methods_array = `ruby -I#{lib_dir} -rdead_end/auto #{@script} 2>&1`.strip.lines.map(&:strip)
kernel_methods_array = `ruby #{@script} 2>&1`.strip.lines.map(&:strip)
methods = (dead_end_methods_array - kernel_methods_array).sort
expect(methods).to eq(["dead_end_original_load", "dead_end_original_require", "dead_end_original_require_relative", "timeout"])
d_pid = Process.spawn("ruby -I#{lib_dir} -rdead_end/auto #{script} 2>&1 > #{dead_end_methods_file}")
k_pid = Process.spawn("ruby #{script} 2>&1 >> #{kernel_methods_file}")

@script.write <<~'EOM'
puts Kernel.private_methods
EOM
Process.wait(k_pid)
Process.wait(d_pid)

dead_end_methods_array = `ruby -I#{lib_dir} -rdead_end/auto #{@script} 2>&1`.strip.lines.map(&:strip)
kernel_methods_array = `ruby #{@script} 2>&1`.strip.lines.map(&:strip)
dead_end_methods_array = dead_end_methods_file.read.strip.lines.map(&:strip)
kernel_methods_array = kernel_methods_file.read.strip.lines.map(&:strip)
methods = (dead_end_methods_array - kernel_methods_array).sort
expect(methods).to eq(["dead_end_original_load", "dead_end_original_require", "dead_end_original_require_relative", "timeout"])
end
end

it "detects require error and adds a message with auto mode" do
Dir.mktmpdir do |dir|
@tmpdir = Pathname(dir)
@script = @tmpdir.join("script.rb")
@script.write <<~EOM
tmpdir = Pathname(dir)
script = tmpdir.join("script.rb")
script.write <<~EOM
describe "things" do
it "blerg" do
end
Expand All @@ -45,14 +44,15 @@ module DeadEnd
end
EOM

require_rb = @tmpdir.join("require.rb")
require_rb = tmpdir.join("require.rb")
require_rb.write <<~EOM
require_relative "./script.rb"
EOM

`ruby -I#{lib_dir} -rdead_end #{require_rb} 2>&1`
out = `ruby -I#{lib_dir} -rdead_end #{require_rb} 2>&1`

expect($?.success?).to be_falsey
expect(out).to include('❯ 5 it "flerg"')
end
end
end
Expand Down
Loading