Skip to content

Commit 2ab8288

Browse files
committed
Update DeadEnd to latest
``` $ tool/sync_default_gems.rb dead_end ``` Incorporates changes from these prs: - [Breaking] Lazy load DeadEnd internals only if there is a Syntax error. Use `require "dead_end"; require "dead_end/api"` to load eagerly all internals. Otherwise `require "dead_end"` will set up an autoload for the first time the DeadEnd module is used in code. This should only happen on a syntax error. (ruby/syntax_suggest#142) - Monkeypatch `SyntaxError#detailed_message` in Ruby 3.2+ instead of `require`, `load`, and `require_relative` (ruby/syntax_suggest#139)
1 parent 35aa49b commit 2ab8288

File tree

5 files changed

+91
-32
lines changed

5 files changed

+91
-32
lines changed

lib/dead_end.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
# frozen_string_literal: true
22

3-
require_relative "dead_end/api"
43
require_relative "dead_end/core_ext"

lib/dead_end/api.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require_relative "version"
24

35
require "tmpdir"
@@ -7,6 +9,8 @@
79
require "timeout"
810

911
module DeadEnd
12+
VERSION = UnloadedDeadEnd::VERSION
13+
1014
# Used to indicate a default value that cannot
1115
# be confused with another input.
1216
DEFAULT_VALUE = Object.new.freeze
@@ -16,7 +20,7 @@ class Error < StandardError; end
1620

1721
# DeadEnd.handle_error [Public]
1822
#
19-
# Takes a `SyntaxError`` exception, uses the
23+
# Takes a `SyntaxError` exception, uses the
2024
# error message to locate the file. Then the file
2125
# will be analyzed to find the location of the syntax
2226
# error and emit that location to stderr.

lib/dead_end/core_ext.rb

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,87 @@
11
# frozen_string_literal: true
22

3-
# Monkey patch kernel to ensure that all `require` calls call the same
4-
# method
5-
module Kernel
6-
module_function
7-
8-
alias_method :dead_end_original_require, :require
9-
alias_method :dead_end_original_require_relative, :require_relative
10-
alias_method :dead_end_original_load, :load
11-
12-
def load(file, wrap = false)
13-
dead_end_original_load(file)
14-
rescue SyntaxError => e
15-
DeadEnd.handle_error(e)
16-
end
3+
# Allow lazy loading, only load code if/when there's a syntax error
4+
autoload :DeadEnd, "dead_end/api"
5+
6+
# Ruby 3.2+ has a cleaner way to hook into Ruby that doesn't use `require`
7+
if SyntaxError.new.respond_to?(:detailed_message)
8+
module DeadEndUnloaded
9+
class MiniStringIO
10+
def initialize(isatty: $stderr.isatty)
11+
@string = +""
12+
@isatty = isatty
13+
end
1714

18-
def require(file)
19-
dead_end_original_require(file)
20-
rescue SyntaxError => e
21-
DeadEnd.handle_error(e)
15+
attr_reader :isatty
16+
def puts(value = $/, **)
17+
@string << value
18+
end
19+
20+
attr_reader :string
21+
end
2222
end
2323

24-
def require_relative(file)
25-
if Pathname.new(file).absolute?
26-
dead_end_original_require file
27-
else
28-
relative_from = caller_locations(1..1).first
29-
relative_from_path = relative_from.absolute_path || relative_from.path
30-
dead_end_original_require File.expand_path("../#{file}", relative_from_path)
24+
SyntaxError.prepend Module.new {
25+
def detailed_message(highlight: nil, **)
26+
message = super
27+
file = DeadEnd::PathnameFromMessage.new(message).call.name
28+
io = DeadEndUnloaded::MiniStringIO.new
29+
30+
if file
31+
DeadEnd.call(
32+
io: io,
33+
source: file.read,
34+
filename: file
35+
)
36+
annotation = io.string
37+
38+
annotation + message
39+
else
40+
message
41+
end
42+
rescue => e
43+
if ENV["DEBUG"]
44+
$stderr.warn(e.message)
45+
$stderr.warn(e.backtrace)
46+
end
47+
48+
raise e
49+
end
50+
}
51+
else
52+
autoload :Pathname, "pathname"
53+
54+
# Monkey patch kernel to ensure that all `require` calls call the same
55+
# method
56+
module Kernel
57+
module_function
58+
59+
alias_method :dead_end_original_require, :require
60+
alias_method :dead_end_original_require_relative, :require_relative
61+
alias_method :dead_end_original_load, :load
62+
63+
def load(file, wrap = false)
64+
dead_end_original_load(file)
65+
rescue SyntaxError => e
66+
DeadEnd.handle_error(e)
67+
end
68+
69+
def require(file)
70+
dead_end_original_require(file)
71+
rescue SyntaxError => e
72+
DeadEnd.handle_error(e)
73+
end
74+
75+
def require_relative(file)
76+
if Pathname.new(file).absolute?
77+
dead_end_original_require file
78+
else
79+
relative_from = caller_locations(1..1).first
80+
relative_from_path = relative_from.absolute_path || relative_from.path
81+
dead_end_original_require File.expand_path("../#{file}", relative_from_path)
82+
end
83+
rescue SyntaxError => e
84+
DeadEnd.handle_error(e)
3185
end
32-
rescue SyntaxError => e
33-
DeadEnd.handle_error(e)
3486
end
3587
end

lib/dead_end/dead_end.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ end
88

99
Gem::Specification.new do |spec|
1010
spec.name = "dead_end"
11-
spec.version = DeadEnd::VERSION
11+
spec.version = UnloadedDeadEnd::VERSION
1212
spec.authors = ["schneems"]
1313
spec.email = ["richard.schneeman+foo@gmail.com"]
1414

lib/dead_end/version.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# frozen_string_literal: true
22

3-
module DeadEnd
4-
VERSION = "3.1.1"
3+
# Calling `DeadEnd::VERSION` forces an eager load due to
4+
# an `autoload` on the `DeadEnd` constant.
5+
#
6+
# This is used for gemspec access in tests
7+
module UnloadedDeadEnd
8+
VERSION = "3.1.2"
59
end

0 commit comments

Comments
 (0)