minitest-heat 0.0.5 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +23 -0
- data/Gemfile +5 -3
- data/Gemfile.lock +30 -1
- data/README.md +12 -3
- data/Rakefile +8 -6
- data/lib/minitest/heat/backtrace.rb +19 -74
- data/lib/minitest/heat/hit.rb +79 -0
- data/lib/minitest/heat/issue.rb +49 -34
- data/lib/minitest/heat/line.rb +74 -0
- data/lib/minitest/heat/location.rb +20 -14
- data/lib/minitest/heat/map.rb +7 -34
- data/lib/minitest/heat/output/backtrace.rb +32 -32
- data/lib/minitest/heat/output/issue.rb +144 -0
- data/lib/minitest/heat/output/map.rb +59 -3
- data/lib/minitest/heat/output/marker.rb +50 -0
- data/lib/minitest/heat/output/results.rb +44 -22
- data/lib/minitest/heat/output/source_code.rb +2 -2
- data/lib/minitest/heat/output/token.rb +15 -13
- data/lib/minitest/heat/output.rb +23 -120
- data/lib/minitest/heat/results.rb +19 -75
- data/lib/minitest/heat/timer.rb +81 -0
- data/lib/minitest/heat/version.rb +3 -1
- data/lib/minitest/heat.rb +3 -0
- data/lib/minitest/heat_plugin.rb +5 -5
- data/lib/minitest/heat_reporter.rb +50 -26
- data/minitest-heat.gemspec +4 -2
- metadata +64 -4
- data/lib/minitest/heat/output/location.rb +0 -20
@@ -14,10 +14,18 @@ module Minitest
|
|
14
14
|
# - 'most_relevant' represents the most specific file to investigate starting with the source
|
15
15
|
# code and then looking to the test code with final line of the backtrace as a fallback
|
16
16
|
class Location
|
17
|
+
TestDefinition = Struct.new(:pathname, :line_number) do
|
18
|
+
def initialize(pathname, line_number)
|
19
|
+
@pathname = Pathname(pathname)
|
20
|
+
@line_number = Integer(line_number)
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
17
25
|
attr_reader :test_location, :backtrace
|
18
26
|
|
19
27
|
def initialize(test_location, backtrace = [])
|
20
|
-
@test_location = test_location
|
28
|
+
@test_location = TestDefinition.new(*test_location)
|
21
29
|
@backtrace = Backtrace.new(backtrace)
|
22
30
|
end
|
23
31
|
|
@@ -58,28 +66,28 @@ module Minitest
|
|
58
66
|
#
|
59
67
|
# @return [String] the relative path to the file from the project root
|
60
68
|
def most_relevant_file
|
61
|
-
Pathname(most_relevant_location
|
69
|
+
Pathname(most_relevant_location.pathname)
|
62
70
|
end
|
63
71
|
|
64
72
|
# The line number of the `most_relevant_file` where the failure originated
|
65
73
|
#
|
66
74
|
# @return [Integer] line number
|
67
75
|
def most_relevant_failure_line
|
68
|
-
most_relevant_location
|
76
|
+
most_relevant_location.line_number
|
69
77
|
end
|
70
78
|
|
71
79
|
# The final location of the stacktrace regardless of whether it's from within the project
|
72
80
|
#
|
73
81
|
# @return [String] the relative path to the file from the project root
|
74
82
|
def final_file
|
75
|
-
Pathname(final_location
|
83
|
+
Pathname(final_location.pathname)
|
76
84
|
end
|
77
85
|
|
78
86
|
# The line number of the `final_file` where the failure originated
|
79
87
|
#
|
80
88
|
# @return [Integer] line number
|
81
89
|
def final_failure_line
|
82
|
-
final_location
|
90
|
+
final_location.line_number
|
83
91
|
end
|
84
92
|
|
85
93
|
# The final location of the stacktrace regardless of whether it's from within the project
|
@@ -100,7 +108,7 @@ module Minitest
|
|
100
108
|
#
|
101
109
|
# @return [String, nil] the relative path to the file from the project root
|
102
110
|
def source_code_file
|
103
|
-
return nil unless backtrace.
|
111
|
+
return nil unless backtrace.source_code_entries.any?
|
104
112
|
|
105
113
|
backtrace.final_source_code_location.pathname
|
106
114
|
end
|
@@ -109,30 +117,30 @@ module Minitest
|
|
109
117
|
#
|
110
118
|
# @return [Integer] line number
|
111
119
|
def source_code_failure_line
|
112
|
-
return nil unless backtrace.
|
120
|
+
return nil unless backtrace.source_code_entries.any?
|
113
121
|
|
114
|
-
backtrace.final_source_code_location.
|
122
|
+
backtrace.final_source_code_location.line_number
|
115
123
|
end
|
116
124
|
|
117
125
|
# The final location from the stacktrace that is within the project's test directory
|
118
126
|
#
|
119
127
|
# @return [String, nil] the relative path to the file from the project root
|
120
128
|
def test_file
|
121
|
-
Pathname(test_location
|
129
|
+
Pathname(test_location.pathname)
|
122
130
|
end
|
123
131
|
|
124
132
|
# The line number of the `test_file` where the test is defined
|
125
133
|
#
|
126
134
|
# @return [Integer] line number
|
127
135
|
def test_definition_line
|
128
|
-
test_location
|
136
|
+
test_location.line_number
|
129
137
|
end
|
130
138
|
|
131
139
|
# The line number from within the `test_file` test definition where the failure occurred
|
132
140
|
#
|
133
141
|
# @return [Integer] line number
|
134
142
|
def test_failure_line
|
135
|
-
backtrace.final_test_location&.
|
143
|
+
backtrace.final_test_location&.line_number || test_definition_line
|
136
144
|
end
|
137
145
|
|
138
146
|
# The line number from within the `test_file` test definition where the failure occurred
|
@@ -166,10 +174,8 @@ module Minitest
|
|
166
174
|
end
|
167
175
|
|
168
176
|
def backtrace?
|
169
|
-
backtrace.
|
177
|
+
backtrace.parsed_entries.any?
|
170
178
|
end
|
171
179
|
end
|
172
180
|
end
|
173
181
|
end
|
174
|
-
|
175
|
-
|
data/lib/minitest/heat/map.rb
CHANGED
@@ -3,55 +3,28 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Map
|
6
|
-
|
6
|
+
MAXIMUM_FILES_TO_SHOW = 5
|
7
7
|
|
8
|
-
|
9
|
-
# trying to fix something. These are ranked based on the possibility they represent ripple
|
10
|
-
# effects where fixing one problem could potentially fix multiple other failures.
|
11
|
-
#
|
12
|
-
# For example, if there's an exception in the file, start there. Broken code can't run. If a
|
13
|
-
# test is broken (i.e. raising an exception), that's a special sort of failure that would be
|
14
|
-
# misleading. It doesn't represent a proper failure, but rather a test that doesn't work.
|
15
|
-
WEIGHTS = {
|
16
|
-
error: 3, # exceptions from source code have the highest liklihood of a ripple effect
|
17
|
-
broken: 1, # broken tests won't have ripple effects but can't help if they can't run
|
18
|
-
failure: 1, # failures are kind of the whole point, and they could have ripple effects
|
19
|
-
skipped: 0, # skips aren't failures, but they shouldn't go ignored
|
20
|
-
painful: 0, # slow tests aren't failures, but they shouldn't be ignored
|
21
|
-
slow: 0,
|
22
|
-
}
|
8
|
+
attr_reader :hits
|
23
9
|
|
24
10
|
def initialize
|
25
11
|
@hits = {}
|
26
12
|
end
|
27
13
|
|
28
14
|
def add(filename, line_number, type)
|
29
|
-
@hits[filename] ||=
|
30
|
-
@hits[filename][:total] += 1
|
31
|
-
@hits[filename][:weight] += WEIGHTS[type]
|
15
|
+
@hits[filename] ||= Hit.new(filename)
|
32
16
|
|
33
|
-
@hits[filename]
|
34
|
-
@hits[filename][type] << line_number
|
17
|
+
@hits[filename].log(type, line_number)
|
35
18
|
end
|
36
19
|
|
37
|
-
def
|
38
|
-
hot_files
|
39
|
-
.sort_by { |filename, weight| weight }
|
40
|
-
.reverse
|
41
|
-
.take(5)
|
20
|
+
def file_hits
|
21
|
+
hot_files.take(MAXIMUM_FILES_TO_SHOW)
|
42
22
|
end
|
43
23
|
|
44
24
|
private
|
45
25
|
|
46
26
|
def hot_files
|
47
|
-
|
48
|
-
@hits.each_pair do |filename, details|
|
49
|
-
# Can't really be a "hot spot" with just a single issue
|
50
|
-
# next unless details[:weight] > 1
|
51
|
-
|
52
|
-
files[filename] = details[:weight]
|
53
|
-
end
|
54
|
-
files
|
27
|
+
hits.values.sort_by(&:weight).reverse
|
55
28
|
end
|
56
29
|
end
|
57
30
|
end
|
@@ -5,7 +5,7 @@ module Minitest
|
|
5
5
|
class Output
|
6
6
|
# Builds the collection of tokens for a backtrace when an exception occurs
|
7
7
|
class Backtrace
|
8
|
-
DEFAULT_LINE_COUNT =
|
8
|
+
DEFAULT_LINE_COUNT = 10
|
9
9
|
DEFAULT_INDENTATION_SPACES = 2
|
10
10
|
|
11
11
|
attr_accessor :location, :backtrace
|
@@ -21,22 +21,18 @@ module Minitest
|
|
21
21
|
# final backtrace line if it might be relevant/helpful?
|
22
22
|
|
23
23
|
# Iterate over the selected lines from the backtrace
|
24
|
-
|
24
|
+
backtrace_entries.each do |backtrace_entry|
|
25
25
|
# Get the source code for the line from the backtrace
|
26
|
-
source_code = source_code_for(backtrace_line)
|
27
|
-
|
28
26
|
parts = [
|
29
27
|
indentation_token,
|
30
|
-
path_token(
|
31
|
-
|
32
|
-
source_code_line_token(source_code)
|
28
|
+
path_token(backtrace_entry),
|
29
|
+
*file_and_line_number_tokens(backtrace_entry),
|
30
|
+
source_code_line_token(backtrace_entry.source_code)
|
33
31
|
]
|
34
32
|
|
35
|
-
parts << file_freshness(
|
33
|
+
parts << file_freshness(backtrace_entry) if most_recently_modified?(backtrace_entry)
|
36
34
|
|
37
35
|
@tokens << parts
|
38
|
-
|
39
|
-
|
40
36
|
end
|
41
37
|
|
42
38
|
@tokens
|
@@ -55,37 +51,31 @@ module Minitest
|
|
55
51
|
# ...it could be influenced by a "compact" or "robust" reporter super-style?
|
56
52
|
# ...it's smart about exceptions that were raised outside of the project?
|
57
53
|
# ...it's smart about highlighting lines of code differently based on whether it's source code, test code, or external code?
|
58
|
-
def
|
59
|
-
|
54
|
+
def backtrace_entries
|
55
|
+
all_entries
|
60
56
|
end
|
61
57
|
|
62
58
|
private
|
63
59
|
|
64
|
-
def
|
65
|
-
|
60
|
+
def all_backtrace_entries_from_project?
|
61
|
+
backtrace_entries.all? { |line| line.path.to_s.include?(project_root_dir) }
|
66
62
|
end
|
67
63
|
|
68
64
|
def project_root_dir
|
69
65
|
Dir.pwd
|
70
66
|
end
|
71
67
|
|
72
|
-
def
|
73
|
-
backtrace.
|
74
|
-
end
|
75
|
-
|
76
|
-
def all_lines
|
77
|
-
backtrace.parsed_lines.take(line_count)
|
68
|
+
def project_entries
|
69
|
+
backtrace.project_entries.take(line_count)
|
78
70
|
end
|
79
71
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
Minitest::Heat::Source.new(filename, line_number: line.number, max_line_count: 1)
|
72
|
+
def all_entries
|
73
|
+
backtrace.parsed_entries.take(line_count)
|
84
74
|
end
|
85
75
|
|
86
76
|
def most_recently_modified?(line)
|
87
77
|
# If there's more than one line being displayed, and the current line is the freshest
|
88
|
-
|
78
|
+
backtrace_entries.size > 1 && line == backtrace.freshest_project_location
|
89
79
|
end
|
90
80
|
|
91
81
|
def indentation_token
|
@@ -93,25 +83,31 @@ module Minitest
|
|
93
83
|
end
|
94
84
|
|
95
85
|
def path_token(line)
|
86
|
+
style = line.to_s.include?(Dir.pwd) ? :default : :muted
|
96
87
|
path = "#{line.path}/"
|
97
88
|
|
98
89
|
# If all of the backtrace lines are from the project, no point in the added redundant
|
99
90
|
# noise of showing the project root directory over and over again
|
100
|
-
path = path.delete_prefix(project_root_dir) if
|
91
|
+
path = path.delete_prefix(project_root_dir) if all_backtrace_entries_from_project?
|
101
92
|
|
102
|
-
[
|
93
|
+
[style, path]
|
103
94
|
end
|
104
95
|
|
105
|
-
def
|
106
|
-
|
96
|
+
def file_and_line_number_tokens(backtrace_entry)
|
97
|
+
style = backtrace_entry.to_s.include?(Dir.pwd) ? :bold : :muted
|
98
|
+
[
|
99
|
+
[style, backtrace_entry.file],
|
100
|
+
[:muted, ':'],
|
101
|
+
[style, backtrace_entry.line_number]
|
102
|
+
]
|
107
103
|
end
|
108
104
|
|
109
105
|
def source_code_line_token(source_code)
|
110
|
-
[:muted, " `#{source_code.line.strip}`"]
|
106
|
+
[:muted, " #{Output::SYMBOLS[:arrow]} `#{source_code.line.strip}`"]
|
111
107
|
end
|
112
108
|
|
113
|
-
def file_freshness(
|
114
|
-
[:
|
109
|
+
def file_freshness(_line)
|
110
|
+
[:default, " #{Output::SYMBOLS[:middot]} Most Recently Modified File"]
|
115
111
|
end
|
116
112
|
|
117
113
|
# The number of spaces each line of code should be indented. Currently defaults to 2 in
|
@@ -125,6 +121,10 @@ module Minitest
|
|
125
121
|
def indentation
|
126
122
|
DEFAULT_INDENTATION_SPACES
|
127
123
|
end
|
124
|
+
|
125
|
+
def style_for(path)
|
126
|
+
style = path.to_s.include?(Dir.pwd) ? :default : :muted
|
127
|
+
end
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
@@ -11,7 +11,151 @@ module Minitest
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def tokens
|
14
|
+
case issue.type
|
15
|
+
when :error then error_tokens
|
16
|
+
when :broken then broken_tokens
|
17
|
+
when :failure then failure_tokens
|
18
|
+
when :skipped then skipped_tokens
|
19
|
+
when :painful then painful_tokens
|
20
|
+
when :slow then slow_tokens
|
21
|
+
end
|
14
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def error_tokens
|
27
|
+
[
|
28
|
+
headline_tokens,
|
29
|
+
test_location_tokens,
|
30
|
+
summary_tokens,
|
31
|
+
*backtrace_tokens,
|
32
|
+
newline_tokens
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
def broken_tokens
|
37
|
+
[
|
38
|
+
headline_tokens,
|
39
|
+
test_location_tokens,
|
40
|
+
summary_tokens,
|
41
|
+
*backtrace_tokens,
|
42
|
+
newline_tokens
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
def failure_tokens
|
47
|
+
[
|
48
|
+
headline_tokens,
|
49
|
+
test_location_tokens,
|
50
|
+
summary_tokens,
|
51
|
+
newline_tokens
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
def skipped_tokens
|
56
|
+
[
|
57
|
+
headline_tokens,
|
58
|
+
test_location_tokens,
|
59
|
+
newline_tokens
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
def painful_tokens
|
64
|
+
[
|
65
|
+
headline_tokens,
|
66
|
+
slowness_summary_tokens,
|
67
|
+
newline_tokens
|
68
|
+
]
|
69
|
+
end
|
70
|
+
|
71
|
+
def slow_tokens
|
72
|
+
[
|
73
|
+
headline_tokens,
|
74
|
+
slowness_summary_tokens,
|
75
|
+
newline_tokens
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
def headline_tokens
|
80
|
+
[[issue.type, issue.label], spacer_token, [:default, issue.test_name]]
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_name_and_class_tokens
|
84
|
+
[[:default, issue.test_class], *test_location_tokens ]
|
85
|
+
end
|
86
|
+
|
87
|
+
def backtrace_tokens
|
88
|
+
backtrace = ::Minitest::Heat::Output::Backtrace.new(issue.location)
|
89
|
+
|
90
|
+
backtrace.tokens
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_location_tokens
|
94
|
+
[[:default, test_file_short_location], [:muted, ':'], [:default, issue.test_definition_line], arrow_token, [:default, issue.test_failure_line], [:muted, test_line_source]]
|
95
|
+
end
|
96
|
+
|
97
|
+
def location_tokens
|
98
|
+
[[:default, most_relevant_short_location], [:muted, ':'], [:default, issue.location.most_relevant_failure_line], [:muted, most_relevant_line_source]]
|
99
|
+
end
|
100
|
+
|
101
|
+
def source_tokens
|
102
|
+
filename = issue.location.project_file
|
103
|
+
line_number = issue.location.project_failure_line
|
104
|
+
|
105
|
+
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
106
|
+
[[:muted, " #{Output::SYMBOLS[:arrow]} `#{source.line.strip}`"]]
|
107
|
+
end
|
108
|
+
|
109
|
+
def summary_tokens
|
110
|
+
[[:italicized, issue.summary.delete_suffix("---------------")]]
|
111
|
+
end
|
112
|
+
|
113
|
+
def slowness_summary_tokens
|
114
|
+
[
|
115
|
+
[:bold, issue.slowness],
|
116
|
+
spacer_token,
|
117
|
+
[:default, issue.location.test_file.to_s.delete_prefix(Dir.pwd)],
|
118
|
+
[:muted, ':'],
|
119
|
+
[:default, issue.location.test_definition_line]
|
120
|
+
]
|
121
|
+
end
|
122
|
+
|
123
|
+
def newline_tokens
|
124
|
+
[]
|
125
|
+
end
|
126
|
+
|
127
|
+
def most_relevant_short_location
|
128
|
+
issue.location.most_relevant_file.to_s.delete_prefix("#{Dir.pwd}/")
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_file_short_location
|
132
|
+
issue.location.test_file.to_s.delete_prefix("#{Dir.pwd}/")
|
133
|
+
end
|
134
|
+
|
135
|
+
def most_relevant_line_source
|
136
|
+
filename = issue.location.project_file
|
137
|
+
line_number = issue.location.project_failure_line
|
138
|
+
|
139
|
+
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
140
|
+
"\n #{source.line.strip}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_line_source
|
144
|
+
filename = issue.location.test_file
|
145
|
+
line_number = issue.location.test_failure_line
|
146
|
+
|
147
|
+
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
148
|
+
"\n #{source.line.strip}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def spacer_token
|
152
|
+
Output::TOKENS[:spacer]
|
153
|
+
end
|
154
|
+
|
155
|
+
def arrow_token
|
156
|
+
Output::TOKENS[:muted_arrow]
|
157
|
+
end
|
158
|
+
|
15
159
|
end
|
16
160
|
end
|
17
161
|
end
|
@@ -4,16 +4,72 @@ module Minitest
|
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
6
|
class Map
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :results
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
9
|
+
def initialize(results)
|
10
|
+
@results = results
|
11
|
+
@tokens = []
|
11
12
|
end
|
12
13
|
|
13
14
|
def tokens
|
15
|
+
map.file_hits.each do |hit|
|
16
|
+
file_tokens = pathname(hit)
|
17
|
+
line_number_tokens = line_numbers(hit)
|
18
|
+
|
19
|
+
next if line_number_tokens.empty?
|
20
|
+
|
21
|
+
@tokens << [
|
22
|
+
*file_tokens,
|
23
|
+
*line_number_tokens
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
@tokens
|
14
28
|
end
|
15
29
|
|
16
30
|
private
|
31
|
+
|
32
|
+
def map
|
33
|
+
results.heat_map
|
34
|
+
end
|
35
|
+
|
36
|
+
def relevant_issue_types
|
37
|
+
issue_types = %i[error broken failure]
|
38
|
+
|
39
|
+
issue_types << :skipped unless results.problems?
|
40
|
+
issue_types << :painful unless results.problems? || results.skips.any?
|
41
|
+
issue_types << :slow unless results.problems? || results.skips.any?
|
42
|
+
|
43
|
+
issue_types
|
44
|
+
end
|
45
|
+
|
46
|
+
def pathname(file)
|
47
|
+
directory = "#{file.pathname.dirname.to_s.delete_prefix(Dir.pwd)}/"
|
48
|
+
filename = file.pathname.basename.to_s
|
49
|
+
|
50
|
+
[
|
51
|
+
[:default, directory],
|
52
|
+
[:bold, filename],
|
53
|
+
[:default, ' · ']
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
def hit_line_numbers(file, issue_type)
|
58
|
+
numbers = []
|
59
|
+
line_numbers_for_issue_type = file.issues.fetch(issue_type) { [] }
|
60
|
+
line_numbers_for_issue_type.sort.map do |line_number|
|
61
|
+
numbers << [issue_type, "#{line_number} "]
|
62
|
+
end
|
63
|
+
numbers
|
64
|
+
end
|
65
|
+
|
66
|
+
def line_numbers(file)
|
67
|
+
line_number_tokens = []
|
68
|
+
relevant_issue_types.each do |issue_type|
|
69
|
+
line_number_tokens += hit_line_numbers(file, issue_type)
|
70
|
+
end
|
71
|
+
line_number_tokens.compact.sort_by { |number_token| number_token[1] }
|
72
|
+
end
|
17
73
|
end
|
18
74
|
end
|
19
75
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minitest
|
4
|
+
module Heat
|
5
|
+
# Friendly API for printing nicely-formatted output to the console
|
6
|
+
class Output
|
7
|
+
class Marker
|
8
|
+
SYMBOLS = {
|
9
|
+
success: '·',
|
10
|
+
slow: '♦',
|
11
|
+
painful: '♦',
|
12
|
+
broken: 'B',
|
13
|
+
error: 'E',
|
14
|
+
skipped: 'S',
|
15
|
+
failure: 'F'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
STYLES = {
|
19
|
+
success: :success,
|
20
|
+
slow: :slow,
|
21
|
+
painful: :painful,
|
22
|
+
broken: :error,
|
23
|
+
error: :error,
|
24
|
+
skipped: :skipped,
|
25
|
+
failure: :failure
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
attr_accessor :issue_type
|
29
|
+
|
30
|
+
def initialize(issue_type)
|
31
|
+
@issue_type = issue_type
|
32
|
+
end
|
33
|
+
|
34
|
+
def token
|
35
|
+
[style, symbol]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def style
|
41
|
+
STYLES.fetch(issue_type, :default)
|
42
|
+
end
|
43
|
+
|
44
|
+
def symbol
|
45
|
+
SYMBOLS.fetch(issue_type, '?')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|