Module: DeadEnd
- Defined in:
- lib/dead_end/api.rb,
lib/dead_end/cli.rb,
lib/dead_end/lex_all.rb,
lib/dead_end/code_line.rb,
lib/dead_end/lex_value.rb,
lib/dead_end/code_block.rb,
lib/dead_end/code_search.rb,
lib/dead_end/block_expand.rb,
lib/dead_end/code_frontier.rb,
lib/dead_end/ripper_errors.rb,
lib/dead_end/clean_document.rb,
lib/dead_end/explain_syntax.rb,
lib/dead_end/priority_queue.rb,
lib/dead_end/unvisited_lines.rb,
lib/dead_end/around_block_scan.rb,
lib/dead_end/capture_code_context.rb,
lib/dead_end/left_right_lex_count.rb,
lib/dead_end/pathname_from_message.rb,
lib/dead_end/priority_engulf_queue.rb,
lib/dead_end/display_invalid_blocks.rb,
lib/dead_end/parse_blocks_from_indent_line.rb,
lib/dead_end/display_code_with_line_numbers.rb
Defined Under Namespace
Classes: AroundBlockScan, BlockExpand, CaptureCodeContext, CleanDocument, Cli, CodeBlock, CodeFrontier, CodeLine, CodeSearch, DisplayCodeWithLineNumbers, DisplayInvalidBlocks, Error, ExplainSyntax, LeftRightLexCount, LexAll, LexValue, ParseBlocksFromIndentLine, PathnameFromMessage, PriorityEngulfQueue, PriorityQueue, RipperErrors, UnvisitedLines
Constant Summary collapse
- VERSION =
UnloadedDeadEnd::VERSION
- DEFAULT_VALUE =
Used to indicate a default value that cannot be confused with another input.
Object.new.freeze
- TIMEOUT_DEFAULT =
ENV.fetch("DEAD_END_TIMEOUT", 1).to_i
Class Method Summary collapse
-
.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr) ⇒ Object
DeadEnd.call [Private].
-
.handle_error(e, re_raise: true, io: $stderr) ⇒ Object
DeadEnd.handle_error [Public].
-
.invalid?(source) ⇒ Boolean
DeadEnd.invalid? [Private].
-
.record_dir(dir) ⇒ Object
DeadEnd.record_dir [Private].
-
.valid?(source) ⇒ Boolean
DeadEnd.valid? [Private].
-
.valid_without?(without_lines:, code_lines:) ⇒ Boolean
DeadEnd.valid_without? [Private].
Class Method Details
.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr) ⇒ Object
DeadEnd.call [Private]
Main private interface
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/dead_end/api.rb', line 66 def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, 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 |
.handle_error(e, re_raise: true, io: $stderr) ⇒ Object
DeadEnd.handle_error [Public]
Takes a SyntaxError exception, uses the
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).
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/dead_end/api.rb', line 43 def self.handle_error(e, re_raise: true, io: $stderr) unless e.is_a?(SyntaxError) io.puts("DeadEnd: Must pass a SyntaxError, got: #{e.class}") raise e end file = PathnameFromMessage.new(e., io: io).call.name raise e unless file io.sync = true call( io: io, source: file.read, filename: file ) raise e if re_raise end |
.invalid?(source) ⇒ Boolean
DeadEnd.invalid? [Private]
Opposite of DeadEnd.valid?
133 134 135 136 137 138 |
# File 'lib/dead_end/api.rb', line 133 def self.invalid?(source) source = source.join if source.is_a?(Array) source = source.to_s Ripper.new(source).tap(&:parse).error? end |
.record_dir(dir) ⇒ Object
DeadEnd.record_dir [Private]
Used to generate a unique directory to record search steps for debugging
91 92 93 94 95 96 97 98 99 |
# File 'lib/dead_end/api.rb', line 91 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 |
.valid?(source) ⇒ Boolean
DeadEnd.valid? [Private]
Returns truthy if a given input source is valid syntax
DeadEnd.valid?("def foo\nend\n") # => true
DeadEnd.valid?("def foo\n def bar # Syntax error here\nend\n") # => false
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.
176 177 178 |
# File 'lib/dead_end/api.rb', line 176 def self.valid?(source) !invalid?(source) end |
.valid_without?(without_lines:, code_lines:) ⇒ Boolean
DeadEnd.valid_without? [Private]
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
120 121 122 123 124 125 126 127 128 |
# File 'lib/dead_end/api.rb', line 120 def self.valid_without?(without_lines:, code_lines:) lines = code_lines - Array(without_lines).flatten if lines.empty? true else valid?(lines) end end |