mentor 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|