mentor 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/bin/mentor +2 -2
- data/lib/errors/mentor_error.rb +5 -1
- data/lib/errors/no_method_did_you_mean_suggestion_error.rb +3 -1
- data/lib/errors/no_method_for_nil_class_error.rb +35 -0
- data/lib/errors/no_method_for_nil_class_for_common_class_error.rb +74 -0
- data/lib/helpers/colorize.rb +46 -62
- data/lib/helpers/globals.rb +1 -7
- data/lib/helpers/output_helper.rb +20 -0
- data/lib/helpers/outputs.rb +35 -50
- data/lib/helpers/requires.rb +12 -12
- data/lib/helpers/text_to_color.rb +65 -0
- data/lib/main.rb +1 -1
- data/lib/mentor/version.rb +1 -1
- data/lib/mentor.rb +7 -11
- data/lib/sections/backtrace.rb +2 -4
- data/lib/sections/did_you_mean_correction.rb +1 -1
- data/lib/sections/docs.rb +18 -0
- data/lib/sections/examples.rb +28 -0
- data/lib/sections/header.rb +2 -4
- data/lib/sections/lines_of_codes.rb +8 -8
- data/lib/sections/relative_path.rb +2 -4
- data/lib/sections/ruby_error_complete.rb +9 -11
- data/lib/sections/ruby_error_main.rb +4 -10
- data/lib/sections/suggestion.rb +1 -1
- data/mentor.gemspec +3 -3
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d61b738e627b884222d11ae5e7e01b717a4b473
|
4
|
+
data.tar.gz: 30b3b83534080ed8a0da9eb9d32ba11dde4c6b01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 974dea0e3cf08d028480c1e4925df058fee38583c7a115d7a83d39bc8b09e6467f8371d47110913f5ed9c5c8655ba8666b84561b86ecabd86625b7756f9fa2a0
|
7
|
+
data.tar.gz: 8b8e7aa5f94c8c3b186c05d5bcc7f5013623b16dd2d6754e059d340197e4134861501729e9b0c5a197aacbffd5342bc6e0d6afc812209488c2252ed685c5ba88
|
data/.gitignore
CHANGED
data/bin/mentor
CHANGED
data/lib/errors/mentor_error.rb
CHANGED
@@ -20,16 +20,20 @@ module Mentor
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
private_class_method
|
24
24
|
|
25
25
|
def self.error_classes
|
26
26
|
[
|
27
27
|
NoMethodDidYouMeanSuggestionError,
|
28
|
+
NoMethodForNilClassForCommonClassError,
|
29
|
+
NoMethodForNilClassError,
|
28
30
|
MentorNoMethodError,
|
29
31
|
MentorError
|
30
32
|
]
|
31
33
|
end
|
32
34
|
|
35
|
+
private
|
36
|
+
|
33
37
|
def sections
|
34
38
|
[
|
35
39
|
Header.new,
|
@@ -3,7 +3,9 @@ module Mentor
|
|
3
3
|
class NoMethodDidYouMeanSuggestionError < MentorNoMethodError
|
4
4
|
|
5
5
|
def self.can_handle?
|
6
|
-
super &&
|
6
|
+
super &&
|
7
|
+
Mentor.tp.raised_exception.respond_to?(:corrections) &&
|
8
|
+
Mentor.tp.raised_exception.corrections.any?
|
7
9
|
end
|
8
10
|
|
9
11
|
def sections
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Mentor
|
2
|
+
|
3
|
+
class NoMethodForNilClassError < MentorNoMethodError
|
4
|
+
|
5
|
+
def self.can_handle?
|
6
|
+
super && Mentor.tp.raised_exception.to_s['NilClass']
|
7
|
+
end
|
8
|
+
|
9
|
+
def sections
|
10
|
+
[
|
11
|
+
Header.new,
|
12
|
+
RubyErrorComplete.new,
|
13
|
+
RelativePath.new,
|
14
|
+
LinesOfCode.new,
|
15
|
+
ErrorClassSpecificHelp.new([error_class_specific_help]),
|
16
|
+
Suggestion.new("Try setting #{var_for_method} to a value first and then call the method #{method_name} on it.")
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def error_class_specific_help
|
23
|
+
[
|
24
|
+
"It looks like you're trying to call the method #{method_name} on #{var_for_method}.",
|
25
|
+
'',
|
26
|
+
"It's likely #{var_for_method} was never defined, and therefore is equal to nil, which is how Ruby represents 'nothing'."
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Mentor
|
2
|
+
|
3
|
+
class NoMethodForNilClassForCommonClassError < MentorNoMethodError
|
4
|
+
|
5
|
+
def self.can_handle?
|
6
|
+
super &&
|
7
|
+
Mentor.tp.raised_exception.to_s['NilClass'] &&
|
8
|
+
common_methods.include?(method_name.to_sym)
|
9
|
+
end
|
10
|
+
|
11
|
+
def sections
|
12
|
+
[
|
13
|
+
Header.new,
|
14
|
+
RubyErrorComplete.new,
|
15
|
+
RelativePath.new,
|
16
|
+
LinesOfCode.new,
|
17
|
+
ErrorClassSpecificHelp.new(error_class_specific_help),
|
18
|
+
Examples.new(examples),
|
19
|
+
Docs.new(docs),
|
20
|
+
Suggestion.new("Try setting #{var_for_method} to #{a_an(common_classes_for_error.keys.first)} #{or_sentence(common_classes_for_error.keys)} first and then call the method #{method_name} on it.")
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def error_class_specific_help
|
27
|
+
[
|
28
|
+
"It looks like you're trying to call the method #{method_name} on #{var_for_method}.",
|
29
|
+
'',
|
30
|
+
"It's likely #{var_for_method} was never defined, and therefore is equal to nil, which is how Ruby represents 'nothing'.",
|
31
|
+
'',
|
32
|
+
"#{and_sentence(pluralize_words(common_classes_for_error.keys))} have the #{method_name} method, so you may want to set #{var_for_method} to #{a_an(common_classes_for_error.keys.first)} #{or_sentence(common_classes_for_error.keys)} first.",
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
def examples
|
37
|
+
common_classes_for_error.map { |klass, attrs| " #{var_for_method} = #{attrs[:example]} # sets it to #{a_an(klass)} #{klass}" }
|
38
|
+
end
|
39
|
+
|
40
|
+
def docs
|
41
|
+
common_classes_for_error.keys.map { |klass| "ri #{klass}##{method_name}" }
|
42
|
+
end
|
43
|
+
|
44
|
+
def common_classes_for_error
|
45
|
+
self.class.common_classes.select do |klass, attrs|
|
46
|
+
attrs[:methods].include? raised_exception.spell_checker.method_name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private_class_method
|
51
|
+
|
52
|
+
@@string_methods = String.new.methods
|
53
|
+
@@integer_methods = 1.methods
|
54
|
+
@@float_methods = 1.0.methods
|
55
|
+
@@array_methods = Array.new.methods
|
56
|
+
@@hash_methods = Hash.new.methods
|
57
|
+
|
58
|
+
def self.common_classes
|
59
|
+
{
|
60
|
+
String: { methods: @@string_methods, example: "Hello, world!" },
|
61
|
+
Integer: { methods: @@integer_methods, example: 42 },
|
62
|
+
Float: { methods: @@float_methods, example: 3.14 },
|
63
|
+
Array: { methods: @@array_methods, example: '[]' },
|
64
|
+
Hash: { methods: @@hash_methods, example: '{}' }
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.common_methods
|
69
|
+
common_classes.map { |klass, attrs| attrs[:methods] }.flatten - Object.methods
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/helpers/colorize.rb
CHANGED
@@ -2,10 +2,12 @@ module Mentor
|
|
2
2
|
|
3
3
|
module Colorize
|
4
4
|
|
5
|
+
include OutputHelper
|
6
|
+
|
5
7
|
def colorize(line)
|
6
8
|
|
7
|
-
if
|
8
|
-
line =
|
9
|
+
if line_of_padded_code?(line)
|
10
|
+
line = color_padded_code(line)
|
9
11
|
elsif backtrace?(line)
|
10
12
|
return rainbow(line, :backtrace_line)
|
11
13
|
end
|
@@ -14,10 +16,7 @@ module Mentor
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def colorize_section
|
17
|
-
@lines.map!
|
18
|
-
line = rainbow(line, :suggestion)
|
19
|
-
colorize(line)
|
20
|
-
end
|
19
|
+
@lines.map! { |line| colorize(line) }
|
21
20
|
end
|
22
21
|
|
23
22
|
private
|
@@ -26,84 +25,66 @@ module Mentor
|
|
26
25
|
line['from ']
|
27
26
|
end
|
28
27
|
|
29
|
-
def
|
28
|
+
def line_of_padded_code?(line)
|
30
29
|
line[padded_lineno_with_colon_regex]
|
31
30
|
end
|
32
31
|
|
33
|
-
def
|
34
|
-
|
35
|
-
line = color_pattern(line, error_lineno, "#{file_name}:#{error_lineno}", :error_lineno)
|
36
|
-
line = color_pattern(line, ruby_error_class, "(#{ruby_error_class})", :ruby_error_class)
|
37
|
-
line = color_pattern(line, ruby_error_class, " #{ruby_error_class} ", :ruby_error_class)
|
38
|
-
|
39
|
-
output_types = %i(
|
32
|
+
def output_types
|
33
|
+
%i[
|
40
34
|
did_you_mean_text did_you_mean_word
|
41
35
|
message horizontal_line ruby_error_text
|
42
|
-
calling_method
|
43
|
-
absolute_base_dir relative_base_dir app_dir
|
44
|
-
|
45
|
-
|
46
|
-
output_types.reduce(line) do |line, output_type|
|
47
|
-
color_text(line, send(output_type), output_type)
|
48
|
-
end
|
49
|
-
|
36
|
+
calling_method method_name var_for_method
|
37
|
+
absolute_base_dir relative_base_dir app_dir file_name
|
38
|
+
]
|
50
39
|
end
|
51
40
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
41
|
+
def apply_colors(line)
|
42
|
+
line = apply_pattern_colors(line)
|
43
|
+
line = apply_output_colors(line)
|
44
|
+
line = apply_nil_colors(line)
|
45
|
+
line = apply_common_classes_colors(line)
|
56
46
|
end
|
57
47
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
def apply_pattern_colors(line)
|
49
|
+
line = TextToColor.new(line, error_lineno, :error_lineno, "#{file_name}:#{error_lineno}").color_pattern
|
50
|
+
line = TextToColor.new(line, ruby_error_class, :ruby_error_class, "(#{ruby_error_class})").color_pattern
|
51
|
+
line = TextToColor.new(line, ruby_error_class, :ruby_error_class, " #{ruby_error_class} ").color_pattern
|
62
52
|
end
|
63
53
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
54
|
+
def apply_output_colors(line)
|
55
|
+
output_types.reduce(line) do |new_line, output_type|
|
56
|
+
TextToColor.new(new_line, send(output_type), output_type).colored
|
57
|
+
end
|
68
58
|
end
|
69
59
|
|
70
|
-
def
|
71
|
-
|
60
|
+
def apply_nil_colors(line)
|
61
|
+
line = TextToColor.new(line, ' nil', :nil_text).colored
|
62
|
+
line = TextToColor.new(line, 'NilClass', :nil_text).colored
|
72
63
|
end
|
73
64
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
colorized_text = rainbow(text_to_color, output_type)
|
79
|
-
remaining_text = full_text
|
80
|
-
text_processed = ''
|
81
|
-
|
82
|
-
while remaining_text[text_to_color]
|
83
|
-
before_text = before_text_to_color(remaining_text, text_to_color)
|
84
|
-
reset_color = get_reset_color(before_text).to_s
|
85
|
-
text_processed += before_text + colorized_text
|
86
|
-
index_after_text_processed = (before_text + text_to_color).size
|
87
|
-
remaining_text = reset_color + remaining_text[index_after_text_processed..-1].to_s
|
65
|
+
def apply_common_classes_colors(line)
|
66
|
+
%w(String Integer Float Array Hash [] {}).reduce(line) do |new_line, klass|
|
67
|
+
new_line = TextToColor.new(new_line, pluralize(klass), :common_class).colored
|
68
|
+
new_line = TextToColor.new(new_line, klass.to_s, :common_class).colored
|
88
69
|
end
|
89
|
-
|
90
|
-
text_processed + remaining_text
|
91
|
-
|
92
70
|
end
|
93
71
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
72
|
+
def color_padded_code(line)
|
73
|
+
padded_lineno = line[padded_lineno_with_colon_regex]
|
74
|
+
code = line[padded_lineno.size..-1]
|
75
|
+
color_padded_lineno(padded_lineno) + color_code(code)
|
98
76
|
end
|
99
77
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
78
|
+
def color_code(code)
|
79
|
+
formatter = Rouge::Formatters::Terminal256.new
|
80
|
+
lexer = Rouge::Lexers::Ruby.new
|
81
|
+
formatter.format(lexer.lex(code))
|
103
82
|
end
|
104
83
|
|
105
|
-
def
|
106
|
-
|
84
|
+
def color_padded_lineno(padded_lineno)
|
85
|
+
text_to_color_text = padded_lineno[padded_lineno_regex]
|
86
|
+
color = text_to_color_text['=>'] ? :error_lineno : :subtle
|
87
|
+
TextToColor.new(padded_lineno, text_to_color_text, color).colored
|
107
88
|
end
|
108
89
|
|
109
90
|
def padded_lineno_regex
|
@@ -124,6 +105,7 @@ module Mentor
|
|
124
105
|
method_name = :deepskyblue
|
125
106
|
subtle = :olive
|
126
107
|
very_subtle = :dimgray
|
108
|
+
nil_text = :orange
|
127
109
|
|
128
110
|
{
|
129
111
|
app_dir: subtle,
|
@@ -131,6 +113,7 @@ module Mentor
|
|
131
113
|
absolute_base_dir: subtle,
|
132
114
|
relative_base_dir: subtle,
|
133
115
|
calling_method: :green,
|
116
|
+
common_class: :wheat,
|
134
117
|
did_you_mean_text: :royalblue,
|
135
118
|
did_you_mean_word: method_name,
|
136
119
|
error_lineno: error_lineno,
|
@@ -141,6 +124,7 @@ module Mentor
|
|
141
124
|
message: error,
|
142
125
|
method_class: :mediumpurple,
|
143
126
|
method_name: method_name,
|
127
|
+
nil_text: nil_text,
|
144
128
|
other_class_or_module: :darksalmon,
|
145
129
|
prominent: :ivory,
|
146
130
|
ruby_error_class: :orangered,
|
data/lib/helpers/globals.rb
CHANGED
@@ -2,6 +2,26 @@ module Mentor
|
|
2
2
|
|
3
3
|
module OutputHelper
|
4
4
|
|
5
|
+
def a_an(word)
|
6
|
+
%w(A E I O U).include?(word[0]) ? 'an' : 'a'
|
7
|
+
end
|
8
|
+
|
9
|
+
def pluralize(word)
|
10
|
+
word.to_s + (word.to_s == 'Hash' ? 'es' :'s')
|
11
|
+
end
|
12
|
+
|
13
|
+
def pluralize_words(words)
|
14
|
+
words.map { |word| pluralize word }
|
15
|
+
end
|
16
|
+
|
17
|
+
def or_sentence(words)
|
18
|
+
words.join(' or ')
|
19
|
+
end
|
20
|
+
|
21
|
+
def and_sentence(words)
|
22
|
+
words.join(' and ')
|
23
|
+
end
|
24
|
+
|
5
25
|
def home_to_tilde(path)
|
6
26
|
path.sub(ENV['HOME'], '~')
|
7
27
|
end
|
data/lib/helpers/outputs.rb
CHANGED
@@ -4,31 +4,41 @@ module Mentor
|
|
4
4
|
|
5
5
|
include OutputHelper
|
6
6
|
|
7
|
+
private
|
8
|
+
|
9
|
+
def tp
|
10
|
+
Mentor.tp
|
11
|
+
end
|
12
|
+
|
13
|
+
def raised_exception
|
14
|
+
tp.raised_exception
|
15
|
+
end
|
16
|
+
|
7
17
|
def absolute_base_dir
|
8
18
|
Dir.pwd + '/'
|
9
19
|
end
|
10
20
|
|
11
21
|
def app_dir
|
12
|
-
|
13
|
-
sub(absolute_base_dir, '')
|
14
|
-
split('/')[0..-2]
|
15
|
-
join('/') +
|
22
|
+
tp.path # Full path
|
23
|
+
.sub(absolute_base_dir, '') # Remove path up until this file
|
24
|
+
.split('/')[0..-2] # Split path into parts and exclude filename
|
25
|
+
.join('/') + # Join with '/'
|
16
26
|
'/' # Add one more '/' to end of it
|
17
27
|
end
|
18
28
|
|
19
29
|
def backtrace_lines
|
20
30
|
|
21
|
-
#
|
31
|
+
# raised_exception.backtrace disappears after accessing it once
|
22
32
|
# So we just do it once and then save the result to @@backtrace_lines
|
23
33
|
if self.class.class_variable_defined?(:@@backtrace_lines)
|
24
34
|
return @@backtrace_lines
|
25
35
|
end
|
26
36
|
|
27
|
-
bt_lines =
|
28
|
-
backtrace[1..-1]
|
29
|
-
grep_v(/
|
30
|
-
map { |line| line.sub(Dir.pwd + '/', '') }
|
31
|
-
map { |line| 'from ' + line }
|
37
|
+
bt_lines = raised_exception
|
38
|
+
.backtrace[1..-1] # Remove first
|
39
|
+
.grep_v(%r{bin/mentor}) # Remove mentor involvement
|
40
|
+
.map { |line| line.sub(Dir.pwd + '/', '') } # Remove path before current dir
|
41
|
+
.map { |line| 'from ' + line } # Add 'from'
|
32
42
|
|
33
43
|
return bt_lines if bt_lines.empty?
|
34
44
|
|
@@ -40,13 +50,13 @@ module Mentor
|
|
40
50
|
end
|
41
51
|
|
42
52
|
def calling_method
|
43
|
-
(
|
53
|
+
(tp.method_id || '<main>').to_s
|
44
54
|
end
|
45
55
|
|
46
56
|
def did_you_mean_word
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
return unless raised_exception.respond_to? :corrections
|
58
|
+
|
59
|
+
raised_exception.corrections.first.to_s
|
50
60
|
end
|
51
61
|
|
52
62
|
def did_you_mean_text
|
@@ -58,60 +68,35 @@ module Mentor
|
|
58
68
|
end
|
59
69
|
|
60
70
|
def error_lineno
|
61
|
-
|
71
|
+
tp.lineno.to_s
|
62
72
|
end
|
63
73
|
|
64
74
|
def file_name
|
65
|
-
|
75
|
+
tp.path.split('/').last
|
66
76
|
end
|
67
77
|
|
68
78
|
def horizontal_line
|
69
79
|
'─' * terminal_width
|
70
80
|
end
|
71
81
|
|
72
|
-
def instance_methods
|
73
|
-
Mentor.tp.raised_exception.receiver.class.instance_methods -
|
74
|
-
Mentor.tp.raised_exception.receiver.class.superclass.instance_methods
|
75
|
-
end
|
76
|
-
|
77
82
|
def lineno_subtle_padded(width)
|
78
83
|
@lineno.to_s.rjust(width)
|
79
84
|
end
|
80
85
|
|
81
86
|
def message
|
82
|
-
if
|
83
|
-
|
87
|
+
if raised_exception.respond_to? :original_message
|
88
|
+
raised_exception.original_message
|
84
89
|
else
|
85
|
-
|
90
|
+
raised_exception.message
|
86
91
|
end
|
87
92
|
end
|
88
93
|
|
89
94
|
def method_name
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
else
|
94
|
-
raise "cannot determine method name"
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def method_class
|
100
|
-
if Mentor.tp.raised_exception.respond_to? :receiver
|
101
|
-
Mentor.tp.raised_exception.receiver.class.to_s
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def method_class_plural
|
106
|
-
pluralize method_class
|
107
|
-
end
|
108
|
-
|
109
|
-
def method_class_superclass
|
110
|
-
Mentor.tp.raised_exception.receiver.class.superclass
|
111
|
-
end
|
95
|
+
return unless
|
96
|
+
raised_exception.respond_to?(:spell_checker) &&
|
97
|
+
raised_exception.spell_checker.respond_to?(:method_name)
|
112
98
|
|
113
|
-
|
114
|
-
to_sentence(Mentor.tp.raised_exception.receiver.class.ancestors - typical_classes)
|
99
|
+
raised_exception.spell_checker.method_name.to_s
|
115
100
|
end
|
116
101
|
|
117
102
|
def relative_base_dir
|
@@ -119,7 +104,7 @@ module Mentor
|
|
119
104
|
end
|
120
105
|
|
121
106
|
def ruby_error_class
|
122
|
-
|
107
|
+
raised_exception.class.to_s
|
123
108
|
end
|
124
109
|
|
125
110
|
def ruby_error_text
|
@@ -127,7 +112,7 @@ module Mentor
|
|
127
112
|
end
|
128
113
|
|
129
114
|
def var_for_method
|
130
|
-
culprit_line = lines_from_file[
|
115
|
+
culprit_line = lines_from_file[tp.lineno]
|
131
116
|
var = culprit_line[/#{valid_var_name}\.#{method_name}/].to_s.chomp(".#{method_name}")
|
132
117
|
if var.empty?
|
133
118
|
culprit_line[/#{valid_var_name} method_name/].to_s.chomp(".#{method_name}")
|
data/lib/helpers/requires.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
module Mentor
|
2
|
-
gems = [
|
3
|
-
'pry',
|
4
|
-
'rainbow',
|
5
|
-
'rouge',
|
6
|
-
]
|
7
2
|
|
8
|
-
|
9
|
-
'output_helper',
|
10
|
-
'outputs',
|
11
|
-
'colorize'
|
12
|
-
].map { |file| Globals::DIR + '/lib/helpers/' + file }
|
3
|
+
dir = __dir__ + '/../..'
|
13
4
|
|
14
|
-
|
15
|
-
|
5
|
+
gems = %w[pry rainbow rouge]
|
6
|
+
|
7
|
+
helpers = %w[
|
8
|
+
output_helper
|
9
|
+
outputs
|
10
|
+
colorize
|
11
|
+
text_to_color
|
12
|
+
].map { |file| dir + '/lib/helpers/' + file }
|
13
|
+
|
14
|
+
sections = Dir.glob(dir + '/lib/sections/*.rb')
|
15
|
+
errors = Dir.glob(dir + '/lib/errors/*.rb')
|
16
16
|
|
17
17
|
(gems + helpers + sections + errors).each { |file| require file }
|
18
18
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Mentor
|
2
|
+
|
3
|
+
class TextToColor
|
4
|
+
|
5
|
+
include Colorize
|
6
|
+
|
7
|
+
def initialize(full_text, text_to_color, output_type, match_pattern = nil)
|
8
|
+
@full_text = full_text
|
9
|
+
@text_to_color = text_to_color
|
10
|
+
@output_type = output_type
|
11
|
+
@match_pattern = match_pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def color_pattern
|
15
|
+
@full_text.gsub(@match_pattern, colored)
|
16
|
+
end
|
17
|
+
|
18
|
+
def colored
|
19
|
+
return @full_text unless text_to_color_exists?
|
20
|
+
color_text
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def colorized_text
|
26
|
+
rainbow(@text_to_color, @output_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def text_to_color_exists?
|
30
|
+
@text_to_color && !@text_to_color.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
def color_text
|
34
|
+
remaining_text = @match_pattern || @full_text
|
35
|
+
text_processed = ''
|
36
|
+
|
37
|
+
while remaining_text[@text_to_color]
|
38
|
+
before_text = before_text_to_color(remaining_text)
|
39
|
+
reset_color = get_reset_color(before_text).to_s
|
40
|
+
text_processed += before_text + colorized_text
|
41
|
+
index_after_text_processed = (before_text + @text_to_color).size
|
42
|
+
remaining_text = reset_color + remaining_text[index_after_text_processed..-1]
|
43
|
+
end
|
44
|
+
|
45
|
+
text_processed + remaining_text
|
46
|
+
end
|
47
|
+
|
48
|
+
def before_text_to_color(remaining_text)
|
49
|
+
index = remaining_text.index(@text_to_color)
|
50
|
+
return '' if !index || index.zero?
|
51
|
+
remaining_text[0..index - 1]
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_reset_color(before_text)
|
55
|
+
index_of_color_to_reset_to = before_text.rindex(color_regex)
|
56
|
+
before_text[index_of_color_to_reset_to..-1][color_regex] if index_of_color_to_reset_to
|
57
|
+
end
|
58
|
+
|
59
|
+
def color_regex
|
60
|
+
/\e\[(\d|;)+m/
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/main.rb
CHANGED
data/lib/mentor/version.rb
CHANGED
data/lib/mentor.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'mentor/version'
|
2
|
-
|
3
1
|
module Mentor
|
4
2
|
|
5
3
|
class Init
|
@@ -11,19 +9,19 @@ module Mentor
|
|
11
9
|
Mentor.enable
|
12
10
|
end
|
13
11
|
|
14
|
-
|
12
|
+
private_class_method
|
15
13
|
|
16
14
|
def self.setup_trace_point
|
17
15
|
|
18
16
|
TracePoint.trace(:raise) do |tp|
|
19
17
|
tp.disable
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
if Mentor.enabled?
|
20
|
+
Mentor.disable
|
21
|
+
require_relative 'helpers/requires'
|
22
|
+
Mentor.tp = tp
|
23
|
+
Main.new
|
24
|
+
end
|
27
25
|
|
28
26
|
tp.enable
|
29
27
|
end
|
@@ -33,5 +31,3 @@ module Mentor
|
|
33
31
|
end
|
34
32
|
|
35
33
|
end
|
36
|
-
|
37
|
-
Mentor::Init.setup
|
data/lib/sections/backtrace.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mentor
|
2
|
+
|
3
|
+
class Docs
|
4
|
+
|
5
|
+
include Outputs, Colorize
|
6
|
+
|
7
|
+
attr_reader :lines
|
8
|
+
|
9
|
+
def initialize(lines)
|
10
|
+
@lines = ['For docs, type:', '']
|
11
|
+
@lines << indent_lines(lines)
|
12
|
+
@lines = indent_lines(@lines)
|
13
|
+
colorize_section
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Mentor
|
2
|
+
|
3
|
+
class Examples
|
4
|
+
|
5
|
+
include Outputs, Colorize
|
6
|
+
|
7
|
+
attr_reader :lines
|
8
|
+
|
9
|
+
def initialize(examples)
|
10
|
+
@lines =
|
11
|
+
indent_lines([
|
12
|
+
'For example:',
|
13
|
+
'',
|
14
|
+
examples
|
15
|
+
]).flatten
|
16
|
+
|
17
|
+
examples.each do |example|
|
18
|
+
@lines.map! do |line|
|
19
|
+
line.sub(example, color_code(example))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
colorize_section
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/sections/header.rb
CHANGED
@@ -4,10 +4,8 @@ module Mentor
|
|
4
4
|
|
5
5
|
include Outputs
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@lines = indent_lines(lines_of_code_from_method_or_file, indent: 4)
|
7
|
+
def lines
|
8
|
+
indent_lines(lines_of_code_from_method_or_file, indent: 4)
|
11
9
|
end
|
12
10
|
|
13
11
|
private
|
@@ -32,16 +30,19 @@ module Mentor
|
|
32
30
|
|
33
31
|
def set_first_and_last_from_method
|
34
32
|
method_text = Pry::Method.from_str("#{Mentor.tp.defined_class}##{calling_method}").source.split("\n")
|
35
|
-
@first_lineno = lines_from_file.select { |
|
33
|
+
@first_lineno = lines_from_file.select { |_lineno, line| line["def #{calling_method}"] }.sort.last.first
|
36
34
|
@last_lineno = @first_lineno + method_text.size - 1
|
37
35
|
end
|
38
36
|
|
39
37
|
def set_first_and_last_from_file
|
40
38
|
@first_lineno = Mentor.tp.lineno
|
41
|
-
@first_lineno -= 1 until first_line_at_start_of_file? ||
|
39
|
+
@first_lineno -= 1 until first_line_at_start_of_file? ||
|
40
|
+
first_line_empty? ||
|
42
41
|
line_prior_to_first_line_ends?
|
42
|
+
|
43
43
|
@last_lineno = Mentor.tp.lineno
|
44
|
-
@last_lineno += 1 until last_line_at_end_of_file? ||
|
44
|
+
@last_lineno += 1 until last_line_at_end_of_file? ||
|
45
|
+
last_line_empty?
|
45
46
|
end
|
46
47
|
|
47
48
|
def first_line_at_start_of_file?
|
@@ -67,4 +68,3 @@ module Mentor
|
|
67
68
|
end
|
68
69
|
|
69
70
|
end
|
70
|
-
|
@@ -4,10 +4,8 @@ module Mentor
|
|
4
4
|
|
5
5
|
include Outputs
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@lines = indent_lines(relative_base_dir + app_dir + file_name + ':' + error_lineno)
|
7
|
+
def lines
|
8
|
+
indent_lines(relative_base_dir + app_dir + file_name + ':' + error_lineno)
|
11
9
|
end
|
12
10
|
|
13
11
|
end
|
@@ -2,18 +2,16 @@ module Mentor
|
|
2
2
|
|
3
3
|
class RubyErrorComplete
|
4
4
|
|
5
|
-
include Outputs
|
5
|
+
include Outputs
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@lines << ''
|
16
|
-
@lines << horizontal_line
|
7
|
+
def lines
|
8
|
+
lines_array = []
|
9
|
+
lines_array << RubyErrorMain.new.lines
|
10
|
+
lines_array << DidYouMeanCorrection.new.line if DidYouMeanCorrection.can_handle?
|
11
|
+
lines_array << Backtrace.new.lines
|
12
|
+
lines_array = indent_lines(lines_array)
|
13
|
+
lines_array << ''
|
14
|
+
lines_array << horizontal_line
|
17
15
|
end
|
18
16
|
|
19
17
|
end
|
@@ -4,20 +4,14 @@ module Mentor
|
|
4
4
|
|
5
5
|
include Outputs
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@lines = ruby_error_main
|
7
|
+
def lines
|
8
|
+
ruby_error_main
|
11
9
|
end
|
12
10
|
|
13
11
|
private
|
14
12
|
|
15
13
|
def single_line
|
16
|
-
|
17
|
-
parts << path_and_lineno
|
18
|
-
parts << message
|
19
|
-
parts << bracketed_class
|
20
|
-
parts.flatten.join(' ')
|
14
|
+
[path_and_lineno, message, bracketed_class].flatten.join(' ')
|
21
15
|
end
|
22
16
|
|
23
17
|
def path_and_lineno
|
@@ -57,7 +51,7 @@ module Mentor
|
|
57
51
|
|
58
52
|
def fit_on_one_or_two_lines?(part_a, part_b)
|
59
53
|
fit_on_one_line?(part_a, part_b) ||
|
60
|
-
|
54
|
+
does_not_fit_on_one_line?(part_a) && fit_on_two_lines?(part_a, part_b)
|
61
55
|
end
|
62
56
|
|
63
57
|
def fit_on_one_line?(part_a, part_b)
|
data/lib/sections/suggestion.rb
CHANGED
data/mentor.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'mentor/version'
|
@@ -12,11 +11,11 @@ Gem::Specification.new do |spec|
|
|
12
11
|
spec.homepage = 'https://gitlab.com/seanlerner/mentor'
|
13
12
|
spec.license = 'MIT'
|
14
13
|
|
15
|
-
spec.files
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
15
|
f.match(%r{^(test|spec|features)/})
|
17
16
|
end
|
18
17
|
|
19
|
-
spec.executables
|
18
|
+
spec.executables << 'mentor'
|
20
19
|
spec.require_paths = ['lib']
|
21
20
|
|
22
21
|
spec.add_dependency 'pry', '~> 0.10'
|
@@ -24,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
24
23
|
spec.add_dependency 'rouge', '~> 2.0'
|
25
24
|
|
26
25
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
26
|
+
spec.add_development_dependency 'did_you_mean', '~> 1.1'
|
27
27
|
spec.add_development_dependency 'guard', '~> 2.14'
|
28
28
|
spec.add_development_dependency 'guard-minitest', '~> 2.4'
|
29
29
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mentor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Lerner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.13'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: did_you_mean
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.1'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: guard
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,17 +154,22 @@ files:
|
|
140
154
|
- lib/errors/mentor_error.rb
|
141
155
|
- lib/errors/mentor_no_method_error.rb
|
142
156
|
- lib/errors/no_method_did_you_mean_suggestion_error.rb
|
157
|
+
- lib/errors/no_method_for_nil_class_error.rb
|
158
|
+
- lib/errors/no_method_for_nil_class_for_common_class_error.rb
|
143
159
|
- lib/helpers/colorize.rb
|
144
160
|
- lib/helpers/globals.rb
|
145
161
|
- lib/helpers/output_helper.rb
|
146
162
|
- lib/helpers/outputs.rb
|
147
163
|
- lib/helpers/requires.rb
|
164
|
+
- lib/helpers/text_to_color.rb
|
148
165
|
- lib/main.rb
|
149
166
|
- lib/mentor.rb
|
150
167
|
- lib/mentor/version.rb
|
151
168
|
- lib/sections/backtrace.rb
|
152
169
|
- lib/sections/did_you_mean_correction.rb
|
170
|
+
- lib/sections/docs.rb
|
153
171
|
- lib/sections/error_class_specific_help.rb
|
172
|
+
- lib/sections/examples.rb
|
154
173
|
- lib/sections/header.rb
|
155
174
|
- lib/sections/line_of_code.rb
|
156
175
|
- lib/sections/lines_of_codes.rb
|