Skip to content

Commit

Permalink
Move api to dead_end/api instead of env var
Browse files Browse the repository at this point in the history
Because setting environment variables on systems may be difficult or inconsistent we are moving the API to be require based. Now anyone who wants to use dead_end without mutating `require` can do so via requiring `dead_end/api`.
  • Loading branch information
schneems committed Nov 19, 2021
1 parent 56d2bf0 commit 5871901
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 215 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## HEAD (unreleased)

- Requiring `dead_end/auto` is now deprecated please require `dead_end` instead (https://github.com/zombocom/dead_end/pull/119)
- Requiring `dead_end/api` now loads code without monkeypatching core extensions (https://github.com/zombocom/dead_end/pull/119)
- The interface `DeadEnd.handle_error` is declared public and stable (https://github.com/zombocom/dead_end/pull/119)
- Requiring the gem with the environment variable `DISABLE_DEAD_END_CORE_EXT=1` now disables monkeypatching core extensions (https://github.com/zombocom/dead_end/pull/119)

## 3.0.3

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Here's an example:

## Use internals

To use the `dead_end` gem without monkeypatching you can set the environment variable `DISABLE_DEAD_END_CORE_EXT=1` before requiring the gem. This will allow you to load `dead_end` and use its internals without mutating `require`.
To use the `dead_end` gem without monkeypatching you can `require 'dead_en/api'`. This will allow you to load `dead_end` and use its internals without mutating `require`.

Stable internal interface(s):

Expand Down
192 changes: 2 additions & 190 deletions lib/dead_end.rb
Original file line number Diff line number Diff line change
@@ -1,191 +1,3 @@
# frozen_string_literal: true

require_relative "dead_end/version"

require "tmpdir"
require "stringio"
require "pathname"
require "ripper"
require "timeout"

module DeadEnd
# Used to indicate a default value that cannot
# be confused with another input
DEFAULT_VALUE = Object.new.freeze

class Error < StandardError; end
TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i

# DeadEnd.handle_error [Public interface]
#
# Takes an exception from a syntax error, uses that
# error message to locate the file. Then the file
# will be analyzed to find the location of the syntax
# error and emit that location to stderr.
#
# Example:
#
# begin
# require 'bad_file'
# rescue => e
# DeadEnd.handle_error(e)
# end
#
# By default it will re_raise the exception unless
# `re_raise: false`. The message output location
# can be configured using the `io: $stderr` input.
#
# If a valid filename cannot be determined, the original
# exception will be re-raised (even with
# `re_raise: false`).
def self.handle_error(e, re_raise: true, io: $stderr)
file = PathnameFromMessage.new(e.message).call.name
raise e unless file

io.sync = true

call(
io: io,
source: file.read,
filename: file
)

raise e if re_raise
end

def self.record_dir(dir)
time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
dir = Pathname(dir)
symlink = dir.join("last").tap { |path| path.delete if path.exist? }
dir.join(time).tap { |path|
path.mkpath
FileUtils.symlink(path.basename, symlink)
}
end

def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr)
search = nil
filename = nil if filename == DEFAULT_VALUE
Timeout.timeout(timeout) do
record_dir ||= ENV["DEBUG"] ? "tmp" : nil
search = CodeSearch.new(source, record_dir: record_dir).call
end

blocks = search.invalid_blocks
DisplayInvalidBlocks.new(
io: io,
blocks: blocks,
filename: filename,
terminal: terminal,
code_lines: search.code_lines
).call
rescue Timeout::Error => e
io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
io.puts e.backtrace.first(3).join($/)
end

# Used for counting spaces
module SpaceCount
def self.indent(string)
string.split(/\S/).first&.length || 0
end
end

# DeadEnd.valid_without? [Private interface]
#
# This will tell you if the `code_lines` would be valid
# if you removed the `without_lines`. In short it's a
# way to detect if we've found the lines with syntax errors
# in our document yet.
#
# code_lines = [
# CodeLine.new(line: "def foo\n", index: 0)
# CodeLine.new(line: " def bar\n", index: 1)
# CodeLine.new(line: "end\n", index: 2)
# ]
#
# DeadEnd.valid_without?(
# without_lines: code_lines[1],
# code_lines: code_lines
# ) # => true
#
# DeadEnd.valid?(code_lines) # => false
def self.valid_without?(without_lines:, code_lines:)
lines = code_lines - Array(without_lines).flatten

if lines.empty?
true
else
valid?(lines)
end
end

def self.invalid?(source)
source = source.join if source.is_a?(Array)
source = source.to_s

Ripper.new(source).tap(&:parse).error?
end

# DeadEnd.valid? [Private interface]
#
# Returns truthy if a given input source is valid syntax
#
# DeadEnd.valid?(<<~EOM) # => true
# def foo
# end
# EOM
#
# DeadEnd.valid?(<<~EOM) # => false
# def foo
# def bar # Syntax error here
# end
# EOM
#
# You can also pass in an array of lines and they'll be
# joined before evaluating
#
# DeadEnd.valid?(
# [
# "def foo\n",
# "end\n"
# ]
# ) # => true
#
# DeadEnd.valid?(
# [
# "def foo\n",
# " def bar\n", # Syntax error here
# "end\n"
# ]
# ) # => false
#
# As an FYI the CodeLine class instances respond to `to_s`
# so passing a CodeLine in as an object or as an array
# will convert it to it's code representation.
def self.valid?(source)
!invalid?(source)
end
end

# Integration
require_relative "dead_end/cli"
require_relative "dead_end/core_ext" unless ENV["DISABLE_DEAD_END_CORE_EXT"]

# Core logic
require_relative "dead_end/code_search"
require_relative "dead_end/code_frontier"
require_relative "dead_end/explain_syntax"
require_relative "dead_end/clean_document"

# Helpers
require_relative "dead_end/lex_all"
require_relative "dead_end/code_line"
require_relative "dead_end/code_block"
require_relative "dead_end/block_expand"
require_relative "dead_end/ripper_errors"
require_relative "dead_end/insertion_sort"
require_relative "dead_end/around_block_scan"
require_relative "dead_end/pathname_from_message"
require_relative "dead_end/display_invalid_blocks"
require_relative "dead_end/parse_blocks_from_indent_line"
require_relative "dead_end/api"
require_relative "dead_end/core_ext"
Loading

0 comments on commit 5871901

Please sign in to comment.