youplot 0.4.6 → 0.5.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/LICENSE.txt +1 -0
- data/README.md +36 -29
- data/lib/youplot/aggregation.rb +140 -0
- data/lib/youplot/backends/unicode_plot.rb +52 -24
- data/lib/youplot/command.rb +185 -67
- data/lib/youplot/dsv.rb +16 -8
- data/lib/youplot/options.rb +20 -0
- data/lib/youplot/parser.rb +60 -32
- data/lib/youplot/version.rb +1 -1
- data/logo.svg +319 -0
- data/sig/youplot/aggregation.rbs +5 -0
- metadata +6 -7
- data/lib/youplot/backends/processing.rb +0 -36
data/lib/youplot/command.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'tempfile'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
|
|
3
6
|
require_relative 'dsv'
|
|
4
7
|
require_relative 'parser'
|
|
5
8
|
|
|
6
|
-
# FIXME
|
|
7
9
|
require_relative 'backends/unicode_plot'
|
|
8
10
|
|
|
9
11
|
module YouPlot
|
|
@@ -42,23 +44,7 @@ module YouPlot
|
|
|
42
44
|
|
|
43
45
|
# progressive mode
|
|
44
46
|
if options[:progressive]
|
|
45
|
-
|
|
46
|
-
Signal.trap(:INT) { stop = true }
|
|
47
|
-
|
|
48
|
-
# make cursor invisible
|
|
49
|
-
options[:output].print "\e[?25l"
|
|
50
|
-
|
|
51
|
-
# mainloop
|
|
52
|
-
while (input = Kernel.gets)
|
|
53
|
-
n = main_progressive(input)
|
|
54
|
-
break if stop
|
|
55
|
-
|
|
56
|
-
options[:output].print "\e[#{n}F"
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
options[:output].print "\e[0J"
|
|
60
|
-
# make cursor visible
|
|
61
|
-
options[:output].print "\e[?25h"
|
|
47
|
+
run_progressive
|
|
62
48
|
|
|
63
49
|
# normal mode
|
|
64
50
|
else
|
|
@@ -77,6 +63,47 @@ module YouPlot
|
|
|
77
63
|
|
|
78
64
|
private
|
|
79
65
|
|
|
66
|
+
def run_progressive
|
|
67
|
+
out = progressive_output
|
|
68
|
+
stop = false
|
|
69
|
+
last_rendered_lines = 0
|
|
70
|
+
Signal.trap(:INT) { stop = true }
|
|
71
|
+
|
|
72
|
+
# make cursor invisible
|
|
73
|
+
out.print "\e[?25l"
|
|
74
|
+
|
|
75
|
+
# mainloop
|
|
76
|
+
begin
|
|
77
|
+
while (input = Kernel.gets)
|
|
78
|
+
n = main_progressive(input)
|
|
79
|
+
# Track the latest plot height so cleanup can move below the plot.
|
|
80
|
+
last_rendered_lines = n if n && n > 0
|
|
81
|
+
break if stop
|
|
82
|
+
|
|
83
|
+
out.print "\e[#{n}F" if n && n > 0
|
|
84
|
+
end
|
|
85
|
+
ensure
|
|
86
|
+
sanitize_progressive_output(out, last_rendered_lines)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def progressive_output
|
|
91
|
+
out = options[:output]
|
|
92
|
+
raise 'In progressive mode, output to a file is not possible.' if out.is_a?(String)
|
|
93
|
+
return out if out.respond_to?(:print) && out.respond_to?(:flush)
|
|
94
|
+
|
|
95
|
+
raise 'In progressive mode, output to a file is not possible.'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def sanitize_progressive_output(out = progressive_output, last_rendered_lines = 0)
|
|
99
|
+
# Move below the last rendered plot (CSI n E).
|
|
100
|
+
out.print "\e[#{last_rendered_lines}E" if last_rendered_lines > 0
|
|
101
|
+
# Clear from cursor to end of screen (CSI 0 J).
|
|
102
|
+
out.print "\e[0J"
|
|
103
|
+
# Show cursor again (CSI ?25 h).
|
|
104
|
+
out.print "\e[?25h"
|
|
105
|
+
end
|
|
106
|
+
|
|
80
107
|
def main(input)
|
|
81
108
|
# Outputs input data to a file or stdout.
|
|
82
109
|
output_data(input)
|
|
@@ -110,31 +137,98 @@ module YouPlot
|
|
|
110
137
|
def main_progressive(input)
|
|
111
138
|
output_data(input)
|
|
112
139
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# plots when there is only one header line.
|
|
116
|
-
if @raw_data.nil?
|
|
117
|
-
@raw_data = String.new
|
|
118
|
-
if options[:headers]
|
|
119
|
-
@raw_data << input
|
|
120
|
-
return
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
@raw_data << input
|
|
140
|
+
row = parse_progressive_row(input)
|
|
141
|
+
return 0 if row.nil?
|
|
124
142
|
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
@data = progressive_update_data(row)
|
|
144
|
+
return 0 if @data.nil?
|
|
127
145
|
|
|
128
146
|
plot = create_plot
|
|
129
147
|
output_plot_progressive(plot)
|
|
130
148
|
end
|
|
131
149
|
|
|
150
|
+
def parse_progressive_row(input)
|
|
151
|
+
line = normalize_input_encoding!(input)
|
|
152
|
+
|
|
153
|
+
begin
|
|
154
|
+
row = CSV.parse_line(line, col_sep: options[:delimiter])
|
|
155
|
+
rescue CSV::MalformedCSVError => e
|
|
156
|
+
warn 'Failed to parse the text. '
|
|
157
|
+
warn 'Please try to set the correct character encoding with --encoding option.'
|
|
158
|
+
warn e.backtrace.grep(/youplot/).first
|
|
159
|
+
exit 1
|
|
160
|
+
rescue ArgumentError => e
|
|
161
|
+
warn 'Failed to parse the text. '
|
|
162
|
+
warn e.backtrace.grep(/youplot/).first
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
return nil if row.nil? || row.empty? || row.all?(&:nil?)
|
|
167
|
+
|
|
168
|
+
row
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def progressive_update_data(row)
|
|
172
|
+
init_progressive_state
|
|
173
|
+
|
|
174
|
+
return nil if consume_progressive_header?(row)
|
|
175
|
+
|
|
176
|
+
append_progressive_row(row)
|
|
177
|
+
progressive_data
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def init_progressive_state
|
|
181
|
+
return if @progressive_initialized
|
|
182
|
+
|
|
183
|
+
@progressive_initialized = true
|
|
184
|
+
@progressive_headers = options[:headers] ? [] : nil
|
|
185
|
+
@progressive_series = []
|
|
186
|
+
@progressive_header_consumed = false
|
|
187
|
+
@progressive_row_count = 0
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def consume_progressive_header?(row)
|
|
191
|
+
return false unless options[:headers]
|
|
192
|
+
return false if options[:transpose]
|
|
193
|
+
return false if @progressive_header_consumed
|
|
194
|
+
|
|
195
|
+
@progressive_headers = row
|
|
196
|
+
@progressive_header_consumed = true
|
|
197
|
+
true
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def append_progressive_row(row)
|
|
201
|
+
if options[:headers] && options[:transpose]
|
|
202
|
+
@progressive_headers << row[0]
|
|
203
|
+
@progressive_series << row[1..-1]
|
|
204
|
+
elsif options[:transpose]
|
|
205
|
+
@progressive_series << row
|
|
206
|
+
else
|
|
207
|
+
append_progressive_columns(row)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def progressive_data
|
|
212
|
+
DSV.build_data(@progressive_headers, @progressive_series)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def append_progressive_columns(row)
|
|
216
|
+
if row.size > @progressive_series.size
|
|
217
|
+
(@progressive_series.size...row.size).each do |i|
|
|
218
|
+
@progressive_series[i] = Array.new(@progressive_row_count, nil)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
0.upto(@progressive_series.size - 1) do |i|
|
|
223
|
+
@progressive_series[i] << row[i]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
@progressive_row_count += 1
|
|
227
|
+
end
|
|
228
|
+
|
|
132
229
|
def parse_dsv(input)
|
|
133
230
|
# If encoding is specified, convert to UTF-8
|
|
134
|
-
|
|
135
|
-
input.force_encoding(options[:encoding])
|
|
136
|
-
.encode!('utf-8')
|
|
137
|
-
end
|
|
231
|
+
normalize_input_encoding!(input)
|
|
138
232
|
|
|
139
233
|
begin
|
|
140
234
|
data = DSV.parse(input, options[:delimiter], options[:headers], options[:transpose])
|
|
@@ -152,6 +246,13 @@ module YouPlot
|
|
|
152
246
|
data
|
|
153
247
|
end
|
|
154
248
|
|
|
249
|
+
def normalize_input_encoding!(input)
|
|
250
|
+
return input unless options[:encoding]
|
|
251
|
+
|
|
252
|
+
input.force_encoding(options[:encoding])
|
|
253
|
+
.encode!('utf-8')
|
|
254
|
+
end
|
|
255
|
+
|
|
155
256
|
def create_plot
|
|
156
257
|
case command
|
|
157
258
|
when :bar, :barplot
|
|
@@ -179,50 +280,67 @@ module YouPlot
|
|
|
179
280
|
|
|
180
281
|
def output_data(input)
|
|
181
282
|
# Pass the input to subsequent pipelines
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
283
|
+
out = options[:pass]
|
|
284
|
+
# Handle Tempfile first to keep tests and behavior consistent.
|
|
285
|
+
# Ruby 2.7 Tempfile is Delegator-based and does not match IO/File checks.
|
|
286
|
+
# Then handle path strings and IO-like objects.
|
|
287
|
+
case out
|
|
288
|
+
when Tempfile
|
|
289
|
+
# Keep file descriptor state consistent with the Tempfile object.
|
|
290
|
+
out.truncate(0) # clear existing content
|
|
291
|
+
out.rewind # move pointer to the beginning
|
|
292
|
+
out.print(input) # write new content
|
|
293
|
+
out.flush # flush buffered writes before immediate read
|
|
294
|
+
out.rewind # move pointer back to the beginning for out.read
|
|
295
|
+
when String
|
|
296
|
+
File.open(out, 'w') do |f|
|
|
297
|
+
f.print(input)
|
|
190
298
|
end
|
|
299
|
+
else
|
|
300
|
+
out.print(input) if out.respond_to?(:print)
|
|
191
301
|
end
|
|
192
302
|
end
|
|
193
303
|
|
|
194
304
|
def output_plot(plot)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
305
|
+
out = options[:output]
|
|
306
|
+
# Handle Tempfile first to keep tests and behavior consistent.
|
|
307
|
+
# Ruby 2.7 Tempfile is Delegator-based and does not match IO/File checks.
|
|
308
|
+
# Then handle path strings and IO-like objects.
|
|
309
|
+
case out
|
|
310
|
+
when Tempfile
|
|
311
|
+
# Keep file descriptor state consistent with the Tempfile object.
|
|
312
|
+
out.truncate(0) # clear existing content
|
|
313
|
+
out.rewind # move pointer to the beginning
|
|
314
|
+
plot.render(out) # write new content
|
|
315
|
+
out.flush # flush buffered writes before immediate read
|
|
316
|
+
out.rewind # move pointer back to the beginning for out.read
|
|
317
|
+
when String
|
|
318
|
+
File.open(out, 'w') do |f|
|
|
200
319
|
plot.render(f)
|
|
201
320
|
end
|
|
321
|
+
else
|
|
322
|
+
plot.render(out) if out.respond_to?(:write)
|
|
202
323
|
end
|
|
203
324
|
end
|
|
204
325
|
|
|
205
326
|
def output_plot_progressive(plot)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
end
|
|
220
|
-
options[:output].print "\e[0J"
|
|
221
|
-
options[:output].flush
|
|
222
|
-
out.string.lines.size
|
|
223
|
-
else
|
|
224
|
-
raise 'In progressive mode, output to a file is not possible.'
|
|
327
|
+
target = progressive_output
|
|
328
|
+
|
|
329
|
+
# RefactorMe
|
|
330
|
+
out = StringIO.new(String.new)
|
|
331
|
+
def out.tty?
|
|
332
|
+
true
|
|
333
|
+
end
|
|
334
|
+
plot.render(out)
|
|
335
|
+
lines = out.string.lines
|
|
336
|
+
lines.each do |line|
|
|
337
|
+
target.print line.chomp
|
|
338
|
+
target.print "\e[0K"
|
|
339
|
+
target.puts
|
|
225
340
|
end
|
|
341
|
+
target.print "\e[0J"
|
|
342
|
+
target.flush
|
|
343
|
+
lines.size
|
|
226
344
|
end
|
|
227
345
|
end
|
|
228
346
|
end
|
data/lib/youplot/dsv.rb
CHANGED
|
@@ -23,9 +23,24 @@ module YouPlot
|
|
|
23
23
|
# get series
|
|
24
24
|
series = get_series(arr, headers, transpose)
|
|
25
25
|
|
|
26
|
+
build_data(headers, series)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Transpose different sized ruby arrays
|
|
30
|
+
# https://stackoverflow.com/q/26016632
|
|
31
|
+
def transpose2(arr)
|
|
32
|
+
Array.new(arr.map(&:length).max) { |i| arr.map { |e| e[i] } }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_data(headers, series)
|
|
26
36
|
# Return if No header
|
|
27
37
|
return Data.new(headers, series) if headers.nil?
|
|
28
38
|
|
|
39
|
+
h_size, s_size = validate_headers(headers, series)
|
|
40
|
+
Data.new(headers, series) if h_size == s_size
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate_headers(headers, series)
|
|
29
44
|
# Warn if header contains nil
|
|
30
45
|
warn "\e[35mHeaders contains nil in it.\e[0m" if headers.include?(nil)
|
|
31
46
|
|
|
@@ -39,19 +54,12 @@ module YouPlot
|
|
|
39
54
|
if h_size > s_size
|
|
40
55
|
warn "\e[35mThe number of headers is greater than the number of series.\e[0m"
|
|
41
56
|
exit 1 if YouPlot.run_as_executable?
|
|
42
|
-
|
|
43
57
|
elsif h_size < s_size
|
|
44
58
|
warn "\e[35mThe number of headers is less than the number of series.\e[0m"
|
|
45
59
|
exit 1 if YouPlot.run_as_executable?
|
|
46
60
|
end
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Transpose different sized ruby arrays
|
|
52
|
-
# https://stackoverflow.com/q/26016632
|
|
53
|
-
def transpose2(arr)
|
|
54
|
-
Array.new(arr.map(&:length).max) { |i| arr.map { |e| e[i] } }
|
|
62
|
+
[h_size, s_size]
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
def get_headers(arr, headers, transpose)
|
data/lib/youplot/options.rb
CHANGED
|
@@ -15,4 +15,24 @@ module YouPlot
|
|
|
15
15
|
:color_names, # color
|
|
16
16
|
:debug
|
|
17
17
|
)
|
|
18
|
+
|
|
19
|
+
# Default values for options.
|
|
20
|
+
# These are applied in Parser#resolve_options.
|
|
21
|
+
# Based on the following priority:
|
|
22
|
+
# 1. CLI options (highest priority)
|
|
23
|
+
# 2. Config file options
|
|
24
|
+
# 3. Default values (lowest priority) specified here.
|
|
25
|
+
Options::DEFAULTS = {
|
|
26
|
+
delimiter: "\t",
|
|
27
|
+
transpose: false,
|
|
28
|
+
headers: nil,
|
|
29
|
+
pass: false,
|
|
30
|
+
output: nil, # resolved to $stderr at parse time (late binding)
|
|
31
|
+
fmt: 'xyy',
|
|
32
|
+
progressive: false,
|
|
33
|
+
encoding: nil,
|
|
34
|
+
reverse: false,
|
|
35
|
+
color_names: false,
|
|
36
|
+
debug: false
|
|
37
|
+
}.freeze
|
|
18
38
|
end
|
data/lib/youplot/parser.rb
CHANGED
|
@@ -14,28 +14,14 @@ module YouPlot
|
|
|
14
14
|
|
|
15
15
|
def initialize
|
|
16
16
|
@command = nil
|
|
17
|
-
|
|
18
|
-
@
|
|
19
|
-
"\t", # elimiter:
|
|
20
|
-
false, # transpose:
|
|
21
|
-
nil, # headers:
|
|
22
|
-
false, # pass:
|
|
23
|
-
$stderr, # output:
|
|
24
|
-
'xyy', # fmt:
|
|
25
|
-
false, # progressive:
|
|
26
|
-
nil, # encoding:
|
|
27
|
-
false, # color_names:
|
|
28
|
-
false # debug:
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
@params = Parameters.new
|
|
17
|
+
@options = Options.new
|
|
18
|
+
@params = Parameters.new
|
|
32
19
|
end
|
|
33
20
|
|
|
34
21
|
def apply_config_file
|
|
35
22
|
return if !config_file && find_config_file.nil?
|
|
36
23
|
|
|
37
24
|
read_config_file
|
|
38
|
-
configure
|
|
39
25
|
end
|
|
40
26
|
|
|
41
27
|
def config_file_candidate_paths
|
|
@@ -70,20 +56,45 @@ module YouPlot
|
|
|
70
56
|
@config = YAML.load_file(config_file)
|
|
71
57
|
end
|
|
72
58
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
else
|
|
84
|
-
raise Error, "Unknown option/param in config file: #{k}"
|
|
59
|
+
# Resolve options by applying the following priority:
|
|
60
|
+
# 1. CLI options -- cli_val = @options[k]
|
|
61
|
+
# 2. Config file -- cfg_val = @config[k.to_s]
|
|
62
|
+
# 3. DEFAULTS -- def_val from Options::DEFAULTS
|
|
63
|
+
def resolve_options
|
|
64
|
+
# Validate config keys up front.
|
|
65
|
+
if @config
|
|
66
|
+
known = (@options.members + @params.members).map(&:to_s)
|
|
67
|
+
@config.each_key do |k|
|
|
68
|
+
raise Error, "Unknown option/param in config file: #{k}" unless known.include?(k)
|
|
85
69
|
end
|
|
86
70
|
end
|
|
71
|
+
|
|
72
|
+
Options::DEFAULTS.each do |k, def_val|
|
|
73
|
+
cfg_val = @config && @config[k.to_s]
|
|
74
|
+
cli_val = @options[k]
|
|
75
|
+
@options[k] = if !cli_val.nil? # can be false
|
|
76
|
+
cli_val
|
|
77
|
+
elsif !cfg_val.nil? # can be false
|
|
78
|
+
cfg_val
|
|
79
|
+
else
|
|
80
|
+
def_val
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# $stderr is evaluated here, not in DEFAULTS.
|
|
85
|
+
# DEFAULTS is a constant, so values in it are fixed at class load time.
|
|
86
|
+
# Tests redirect $stderr = tempfile after load, so placing $stderr in DEFAULTS
|
|
87
|
+
# would capture the original stderr and ignore the test redirect.
|
|
88
|
+
@options[:output] = $stderr if @options[:output].nil?
|
|
89
|
+
@options[:output] = $stdout if @options[:output] == '-'
|
|
90
|
+
@options[:pass] = $stdout if @options[:pass] == '-'
|
|
91
|
+
|
|
92
|
+
@params.members.each do |k|
|
|
93
|
+
cfg_val = @config && @config[k.to_s]
|
|
94
|
+
cli_val = @params[k]
|
|
95
|
+
# no def_val for params
|
|
96
|
+
@params[k] = cfg_val if cli_val.nil? && !cfg_val.nil?
|
|
97
|
+
end
|
|
87
98
|
end
|
|
88
99
|
|
|
89
100
|
def create_base_parser
|
|
@@ -96,11 +107,11 @@ module YouPlot
|
|
|
96
107
|
parser.on('Common options:')
|
|
97
108
|
parser.on('-O', '--pass [FILE]', 'file to output input data to [stdout]',
|
|
98
109
|
'for inserting YouPlot in the middle of Unix pipes') do |v|
|
|
99
|
-
options[:pass] = v || $stdout
|
|
110
|
+
options[:pass] = v.nil? || v == '-' ? $stdout : v
|
|
100
111
|
end
|
|
101
112
|
parser.on('-o', '--output [FILE]', 'file to output plots to [stdout]',
|
|
102
113
|
'If no option is specified, plot will print to stderr') do |v|
|
|
103
|
-
options[:output] = v || $stdout
|
|
114
|
+
options[:output] = v.nil? || v == '-' ? $stdout : v
|
|
104
115
|
end
|
|
105
116
|
parser.on('-d', '--delimiter DELIM', String, 'use DELIM instead of [TAB] for field delimiter') do |v|
|
|
106
117
|
options[:delimiter] = v
|
|
@@ -217,8 +228,10 @@ module YouPlot
|
|
|
217
228
|
end
|
|
218
229
|
|
|
219
230
|
def show_config_info
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
ensure_config_loaded
|
|
232
|
+
|
|
233
|
+
if @config_file
|
|
234
|
+
puts "config file : #{@config_file}"
|
|
222
235
|
puts config.inspect
|
|
223
236
|
else
|
|
224
237
|
puts <<~EOS
|
|
@@ -234,6 +247,15 @@ module YouPlot
|
|
|
234
247
|
exit if YouPlot.run_as_executable?
|
|
235
248
|
end
|
|
236
249
|
|
|
250
|
+
def ensure_config_loaded
|
|
251
|
+
apply_config_file unless config
|
|
252
|
+
rescue StandardError => e
|
|
253
|
+
raise unless YouPlot.run_as_executable?
|
|
254
|
+
|
|
255
|
+
warn "YouPlot: #{e.message}"
|
|
256
|
+
exit 1
|
|
257
|
+
end
|
|
258
|
+
|
|
237
259
|
def sub_parser_add_symbol
|
|
238
260
|
sub_parser.on_head('--symbol STR', String, 'character to be used to plot the bars') do |v|
|
|
239
261
|
params.symbol = v
|
|
@@ -383,6 +405,9 @@ module YouPlot
|
|
|
383
405
|
end
|
|
384
406
|
|
|
385
407
|
def parse_options(argv = ARGV)
|
|
408
|
+
# keep original ARGV intact.
|
|
409
|
+
argv = argv.equal?(ARGV) ? argv : argv.dup
|
|
410
|
+
|
|
386
411
|
begin
|
|
387
412
|
create_main_parser.order!(argv)
|
|
388
413
|
rescue OptionParser::ParseError => e
|
|
@@ -399,12 +424,15 @@ module YouPlot
|
|
|
399
424
|
exit 1 if YouPlot.run_as_executable?
|
|
400
425
|
end
|
|
401
426
|
|
|
427
|
+
# Read config after CLI parsing, then resolve: defaults < config < CLI.
|
|
402
428
|
begin
|
|
403
429
|
apply_config_file
|
|
404
430
|
rescue StandardError => e
|
|
405
431
|
warn "YouPlot: #{e.message}"
|
|
406
432
|
exit 1 if YouPlot.run_as_executable?
|
|
407
433
|
end
|
|
434
|
+
|
|
435
|
+
resolve_options
|
|
408
436
|
end
|
|
409
437
|
end
|
|
410
438
|
end
|
data/lib/youplot/version.rb
CHANGED