clin 0.3.0 → 0.4.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/.lint-ci.yml +2 -0
- data/.simplecov +5 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +11 -0
- data/README.md +5 -4
- data/benchmarks/bench.rb +21 -0
- data/benchmarks/text_bench.rb +78 -0
- data/clin.gemspec +2 -1
- data/examples/reusable_options.rb +19 -0
- data/examples/simple.rb +8 -3
- data/examples/test.rb +5 -5
- data/examples/text_builder.rb +40 -0
- data/lib/clin/argument.rb +19 -2
- data/lib/clin/command_mixin/core.rb +13 -18
- data/lib/clin/command_mixin/options.rb +37 -26
- data/lib/clin/command_parser.rb +46 -57
- data/lib/clin/common/help_options.rb +1 -0
- data/lib/clin/errors.rb +50 -4
- data/lib/clin/line_reader/basic.rb +38 -0
- data/lib/clin/line_reader/readline.rb +53 -0
- data/lib/clin/line_reader.rb +16 -0
- data/lib/clin/option.rb +24 -11
- data/lib/clin/option_parser.rb +159 -0
- data/lib/clin/shell.rb +36 -15
- data/lib/clin/shell_interaction/choose.rb +19 -11
- data/lib/clin/shell_interaction/file_conflict.rb +4 -1
- data/lib/clin/shell_interaction/select.rb +44 -0
- data/lib/clin/shell_interaction.rb +1 -0
- data/lib/clin/text/table.rb +270 -0
- data/lib/clin/text.rb +152 -0
- data/lib/clin/version.rb +1 -1
- data/lib/clin.rb +10 -1
- data/spec/clin/command_dispacher_spec.rb +1 -1
- data/spec/clin/command_mixin/options_spec.rb +38 -15
- data/spec/clin/command_parser_spec.rb +27 -51
- data/spec/clin/line_reader/basic_spec.rb +54 -0
- data/spec/clin/line_reader/readline_spec.rb +64 -0
- data/spec/clin/line_reader_spec.rb +17 -0
- data/spec/clin/option_parser_spec.rb +217 -0
- data/spec/clin/option_spec.rb +5 -7
- data/spec/clin/shell_interaction/choose_spec.rb +30 -0
- data/spec/clin/shell_interaction/file_interaction_spec.rb +18 -0
- data/spec/clin/shell_interaction/select_spec.rb +96 -0
- data/spec/clin/shell_spec.rb +42 -0
- data/spec/clin/text/table_cell_spec.rb +72 -0
- data/spec/clin/text/table_row_spec.rb +74 -0
- data/spec/clin/text/table_separator_row_spec.rb +82 -0
- data/spec/clin/text/table_spec.rb +259 -0
- data/spec/clin/text_spec.rb +158 -0
- data/spec/examples/list_option_spec.rb +6 -2
- data/spec/examples/reusable_options_spec.rb +21 -0
- data/spec/examples/simple_spec.rb +9 -9
- data/spec/spec_helper.rb +3 -2
- metadata +54 -3
@@ -0,0 +1,270 @@
|
|
1
|
+
require 'clin'
|
2
|
+
require 'clin/text'
|
3
|
+
|
4
|
+
# Table text builder
|
5
|
+
class Clin::Text
|
6
|
+
# Helper class to display tables
|
7
|
+
class Table
|
8
|
+
# Header row(Nil for no header)
|
9
|
+
attr_writer :header
|
10
|
+
|
11
|
+
# List of rows in the table
|
12
|
+
attr_accessor :rows
|
13
|
+
|
14
|
+
# Column delimiters
|
15
|
+
# Can be either:
|
16
|
+
# * 1 string: All the column delimiter will use this.
|
17
|
+
# * list of string: The delimiters will be this list of string.
|
18
|
+
# The size must be the column size - 1
|
19
|
+
attr_accessor :column_delimiters
|
20
|
+
|
21
|
+
# Global Row delimiter
|
22
|
+
# All the separator will default to this value
|
23
|
+
attr_accessor :row_delim
|
24
|
+
|
25
|
+
# Boolean if yes or no outside border shall be included
|
26
|
+
attr_accessor :border
|
27
|
+
|
28
|
+
# Column alignment. Can either be a global value(i.e. [Symbol])
|
29
|
+
# or a column specific with an array of [Symbol]
|
30
|
+
# * :left
|
31
|
+
# * :center
|
32
|
+
# * :right
|
33
|
+
attr_accessor :alignment
|
34
|
+
|
35
|
+
# If blank cell should be separated with the column separator.
|
36
|
+
attr_accessor :separate_blank
|
37
|
+
|
38
|
+
attr_accessor :column_length
|
39
|
+
|
40
|
+
# Create a new table
|
41
|
+
# @param col_delim [String] Set the column delimiter @see #column_delimiters
|
42
|
+
# @param row_delim [String] Set the row delimiter @see #row_delim
|
43
|
+
# @param border [String] Set border @see #border
|
44
|
+
# @param align [String] Set alignment @see #align
|
45
|
+
# @param separate_blank [Boolean] If the column separator should be added near blank cells
|
46
|
+
# @param block [Proc] Block with self passed as param.
|
47
|
+
def initialize(col_delim: ' | ', row_delim: '-',
|
48
|
+
border: true, align: :left, separate_blank: true, &block)
|
49
|
+
@rows = []
|
50
|
+
@header = nil
|
51
|
+
@column_length = {}
|
52
|
+
@column_delimiters = col_delim
|
53
|
+
@row_delim = row_delim
|
54
|
+
@border = border
|
55
|
+
@separate_blank = separate_blank
|
56
|
+
@alignment = align
|
57
|
+
block.call(self) if block_given?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Add a new row
|
61
|
+
# @param cells [Array<String>] Cells.
|
62
|
+
def row(*cells)
|
63
|
+
@rows << TableRow.new(self, cells)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set or get the header row.
|
67
|
+
# @param cells [Array<String>] Cells.
|
68
|
+
def header(*cells)
|
69
|
+
@header = TableRow.new(self, cells) if cells.any?
|
70
|
+
@header
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add a separator row
|
74
|
+
# @param char [Char] Separator char. If nil will use the default row delimiter
|
75
|
+
def separator(char = nil)
|
76
|
+
@rows << TableSeparatorRow.new(self, char)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set or get the the column alignment
|
80
|
+
# @param args [Array|Symbol] List of alignment.
|
81
|
+
# ```
|
82
|
+
# t.align :center # => All column will be centered
|
83
|
+
# t.align :left, :center, :right #=> First column will be align left, second center, ...
|
84
|
+
# t.align []:left, :center, :right] #=> Equivalent
|
85
|
+
# ```
|
86
|
+
# @see #alignment
|
87
|
+
def align(*args)
|
88
|
+
@alignment = sym_or_array(*args) if args.any?
|
89
|
+
@alignment
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set a specific column delimiter for all the column
|
93
|
+
def column_delimiter(*args)
|
94
|
+
@column_delimiters = sym_or_array(*args)
|
95
|
+
end
|
96
|
+
|
97
|
+
def border?
|
98
|
+
@border
|
99
|
+
end
|
100
|
+
|
101
|
+
def separate_blank?
|
102
|
+
separate_blank
|
103
|
+
end
|
104
|
+
|
105
|
+
def vertical_border
|
106
|
+
'|'
|
107
|
+
end
|
108
|
+
|
109
|
+
# Build the text object for this table.
|
110
|
+
def to_text
|
111
|
+
t = Clin::Text.new
|
112
|
+
unless @header.nil?
|
113
|
+
t.line @header.to_s
|
114
|
+
t.line TableSeparatorRow.new(self).to_s
|
115
|
+
end
|
116
|
+
t.lines @rows.map(&:to_s)
|
117
|
+
add_border(t) if border?
|
118
|
+
t
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_s
|
122
|
+
to_text.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
def update_column_length(index, cell_length)
|
126
|
+
@column_length[index] ||= 0
|
127
|
+
@column_length[index] = [cell_length, @column_length[index]].max
|
128
|
+
end
|
129
|
+
|
130
|
+
def delimiter_at(index)
|
131
|
+
return '' if index >= @column_length.size - 1
|
132
|
+
if @column_delimiters.is_a? Array
|
133
|
+
@column_delimiters[index]
|
134
|
+
else
|
135
|
+
@column_delimiters
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
protected def sym_or_array(*args)
|
140
|
+
return args if args.empty?
|
141
|
+
args.flatten!
|
142
|
+
args.size == 1 ? args.first : args
|
143
|
+
end
|
144
|
+
|
145
|
+
# Add the top and bottom border to the lines
|
146
|
+
protected def add_border(text)
|
147
|
+
line = TableSeparatorRow.new(self, col_delimiter: false).to_s
|
148
|
+
text.prefix(line)
|
149
|
+
text.line line
|
150
|
+
text
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Table Row container class
|
155
|
+
class TableRow
|
156
|
+
include Enumerable
|
157
|
+
|
158
|
+
# List of cells in the row.
|
159
|
+
attr_accessor :cells
|
160
|
+
|
161
|
+
def initialize(table, cells)
|
162
|
+
@table = table
|
163
|
+
@cells = cells.flatten.each_with_index.map { |x, i| TableCell.new(table, i, x) }
|
164
|
+
end
|
165
|
+
|
166
|
+
def each(&block)
|
167
|
+
@table.column_length.size.times.each do |i|
|
168
|
+
block.call(@cells[i] || '')
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def border(text, separator = '')
|
173
|
+
return text unless @table.border?
|
174
|
+
@table.vertical_border + separator + text + separator + @table.vertical_border
|
175
|
+
end
|
176
|
+
|
177
|
+
# Get the delimiter to insert after the cell at +index+
|
178
|
+
# @param index [Integer] Cell index.
|
179
|
+
# Will return the corresponding delimiter and for the last cell will return ''
|
180
|
+
def delimiter_at(index)
|
181
|
+
delim = @table.delimiter_at(index)
|
182
|
+
if !@table.separate_blank? && (@cells[index].blank? || @cells[index + 1].blank?)
|
183
|
+
delim = ' ' * delim.size
|
184
|
+
end
|
185
|
+
delim
|
186
|
+
end
|
187
|
+
|
188
|
+
def to_s
|
189
|
+
out = ''
|
190
|
+
each_with_index do |cell, i|
|
191
|
+
out << cell.to_s
|
192
|
+
out << delimiter_at(i)
|
193
|
+
end
|
194
|
+
border(out, ' ')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Table row that is filled with the same character
|
199
|
+
class TableSeparatorRow < TableRow
|
200
|
+
# Char used for separation.
|
201
|
+
attr_accessor :char
|
202
|
+
|
203
|
+
def initialize(table, char = nil, col_delimiter: true)
|
204
|
+
super(table, [])
|
205
|
+
@char = char || @table.row_delim
|
206
|
+
@include_column_delimiter = col_delimiter
|
207
|
+
end
|
208
|
+
|
209
|
+
def delimiter_at(index)
|
210
|
+
col_delim = super(index)
|
211
|
+
@include_column_delimiter ? col_delim : (@char * col_delim.size)
|
212
|
+
end
|
213
|
+
|
214
|
+
def to_s
|
215
|
+
out = ''
|
216
|
+
each_with_index do |_, i|
|
217
|
+
out << @char * @table.column_length[i]
|
218
|
+
out << delimiter_at(i)
|
219
|
+
end
|
220
|
+
border(out, @char)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Table cell container class
|
225
|
+
class TableCell
|
226
|
+
def initialize(table, index, value)
|
227
|
+
@table = table
|
228
|
+
@index = index
|
229
|
+
@value = value.to_s
|
230
|
+
@table.update_column_length(index, @value.length)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Get the length of the cell
|
234
|
+
# @return [Integer]
|
235
|
+
def length
|
236
|
+
@table.column_length[@index]
|
237
|
+
end
|
238
|
+
|
239
|
+
def blank?
|
240
|
+
@value.blank?
|
241
|
+
end
|
242
|
+
|
243
|
+
# Get the alignment for this cell
|
244
|
+
# @return [Symbol]
|
245
|
+
# Can be:
|
246
|
+
# * :left
|
247
|
+
# * :center
|
248
|
+
# * :right
|
249
|
+
def align
|
250
|
+
return @table.align if @table.align.is_a? Symbol
|
251
|
+
fail Clin::Error, 'Align must either be a symbol or an Array!' unless @table.align.is_a? Array
|
252
|
+
@table.align[@index]
|
253
|
+
end
|
254
|
+
|
255
|
+
# Convert the cell to_s using the length of the column and the column alignment
|
256
|
+
# @return [String]
|
257
|
+
def to_s
|
258
|
+
case align
|
259
|
+
when :left
|
260
|
+
@value.to_s.ljust(length)
|
261
|
+
when :right
|
262
|
+
@value.to_s.rjust(length)
|
263
|
+
when :center
|
264
|
+
@value.to_s.center(length)
|
265
|
+
else
|
266
|
+
fail Clin::Error, "Invalid align #{align}"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
data/lib/clin/text.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
# Text builder class.
|
4
|
+
# Text is designed for building dynamic text that need multiple lines
|
5
|
+
# Help message and documentation are easily built with this.
|
6
|
+
# ```
|
7
|
+
# text = Clin::Text.new do
|
8
|
+
# line 'Usage:'
|
9
|
+
# indent 2 do
|
10
|
+
# line 'display Message'
|
11
|
+
# line 'print Message'
|
12
|
+
# end
|
13
|
+
# line 'Description: '
|
14
|
+
# line 'This is a description', indent: 3
|
15
|
+
# blank
|
16
|
+
# line 'Examples:'
|
17
|
+
# indent ' -' do
|
18
|
+
# line 'display Message'
|
19
|
+
# line 'print Message'
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# puts text # =>
|
23
|
+
# $ Usage:
|
24
|
+
# display Message
|
25
|
+
# print Message
|
26
|
+
# Description:
|
27
|
+
# This is a description
|
28
|
+
#
|
29
|
+
# Examples:
|
30
|
+
# -display Message
|
31
|
+
# -print Message
|
32
|
+
#
|
33
|
+
# ```
|
34
|
+
class Clin::Text
|
35
|
+
attr_accessor :_lines
|
36
|
+
|
37
|
+
# All the lines added to this text will be indented with this.
|
38
|
+
attr_accessor :global_indent
|
39
|
+
|
40
|
+
# List of block that listen for line added to the next builder.
|
41
|
+
attr_accessor :listeners
|
42
|
+
|
43
|
+
def initialize(indent: '', &block)
|
44
|
+
@_lines = []
|
45
|
+
@inital_indent = compute_indent(indent)
|
46
|
+
@global_indent = @inital_indent
|
47
|
+
@listeners = []
|
48
|
+
block.call(self) if block_given?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a new line
|
52
|
+
# @param text [String] line to add
|
53
|
+
# @param indent [String|Integer] Indent the line with x spaces or the given text
|
54
|
+
# ```
|
55
|
+
# line('Some line') #=> 'Some line'
|
56
|
+
# line('Some line', indent: 3) #=> ' Some line'
|
57
|
+
# line('Some line', indent: '- ') #=> '- Some line'
|
58
|
+
# ```
|
59
|
+
def line(text, indent: '')
|
60
|
+
l = process_line(text, indent: indent)
|
61
|
+
@_lines << l
|
62
|
+
broadcast(l)
|
63
|
+
l
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add a line at the beginning of the text
|
67
|
+
# @param text [String] line to add
|
68
|
+
# @param indent [String|Integer] Indent the line with x spaces or the given text
|
69
|
+
def prefix(text, indent: '')
|
70
|
+
l = process_line(text, indent: indent)
|
71
|
+
@_lines.unshift l
|
72
|
+
l
|
73
|
+
end
|
74
|
+
|
75
|
+
# Add a blank line n times
|
76
|
+
# @param times [Integer] Number of times to add the line.
|
77
|
+
def blank(times = 1)
|
78
|
+
@_lines += [''] * times
|
79
|
+
times.times.each { broadcast('') }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add a list of string as lines or get the existing lines.
|
83
|
+
# @param array [Array<String>] List of lines to add.
|
84
|
+
# @param indent [String|Integer] Indent each line. @see #line
|
85
|
+
# @return list of lines
|
86
|
+
def lines(array = [], indent: '')
|
87
|
+
array.each do |l|
|
88
|
+
line(l, indent: indent)
|
89
|
+
end
|
90
|
+
@_lines
|
91
|
+
end
|
92
|
+
|
93
|
+
# Add the content of another Clin::Text object
|
94
|
+
# @param text [Clin::Text]
|
95
|
+
# @param indent [String|Integer] Indent the content
|
96
|
+
def text(text, indent: '')
|
97
|
+
lines(text._lines, indent: indent)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Indent all the content inside this block.
|
101
|
+
# @param indent [String|Integer] indent value
|
102
|
+
# @param block [Proc] Callback.
|
103
|
+
def indent(indent, &block)
|
104
|
+
previous_indent = @global_indent
|
105
|
+
@global_indent += compute_indent(indent)
|
106
|
+
block.call(self)
|
107
|
+
@global_indent = previous_indent
|
108
|
+
end
|
109
|
+
|
110
|
+
def table(indent: '', **options, &block)
|
111
|
+
text Clin::Text::Table.new(**options, &block).to_text, indent: indent
|
112
|
+
end
|
113
|
+
|
114
|
+
# Join the lines together to form the output
|
115
|
+
# @return [String]
|
116
|
+
def to_s
|
117
|
+
"#{@_lines.join("\n")}\n"
|
118
|
+
end
|
119
|
+
|
120
|
+
def process_line(text, indent: '')
|
121
|
+
indent = compute_indent(indent)
|
122
|
+
"#{global_indent}#{indent}#{text}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def on(&block)
|
126
|
+
@listeners << block
|
127
|
+
end
|
128
|
+
|
129
|
+
# Call the the listener with the newly added line
|
130
|
+
def broadcast(line)
|
131
|
+
@listeners.each do |block|
|
132
|
+
block.call(line)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def ==(other)
|
137
|
+
return to_s == other if other.is_a? String
|
138
|
+
return false unless other.is_a? Clin::Text
|
139
|
+
@_lines == other._lines && @global_indent == other.global_indent
|
140
|
+
end
|
141
|
+
|
142
|
+
alias_method :eql?, :==
|
143
|
+
|
144
|
+
# Process the indent.
|
145
|
+
# If the indent is an integer it will return +indent+ spaces
|
146
|
+
# @return [String.]
|
147
|
+
protected def compute_indent(indent)
|
148
|
+
indent.is_a?(Integer) ? ' ' * indent : indent
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
require 'clin/text/table'
|
data/lib/clin/version.rb
CHANGED
data/lib/clin.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_support/core_ext'
|
3
|
-
require 'optparse'
|
4
3
|
require 'readline'
|
5
4
|
require 'clin/version'
|
6
5
|
|
@@ -13,6 +12,9 @@ module Clin
|
|
13
12
|
# Set the command when comparing 2 files(Used in the shell)
|
14
13
|
attr_writer :diff_cmd
|
15
14
|
|
15
|
+
# If the line reader should use Readline(For autocomplete and history)
|
16
|
+
attr_writer :use_readline
|
17
|
+
|
16
18
|
def default_exe_name
|
17
19
|
'command'
|
18
20
|
end
|
@@ -26,11 +28,16 @@ module Clin
|
|
26
28
|
def diff_cmd
|
27
29
|
@diff_cmd ||= 'diff -u'
|
28
30
|
end
|
31
|
+
|
32
|
+
def use_readline?
|
33
|
+
@use_readline ||= !ENV['CLIN_NO_READLINE']
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|
31
37
|
|
32
38
|
require 'clin/command_mixin'
|
33
39
|
require 'clin/command'
|
40
|
+
require 'clin/option_parser'
|
34
41
|
require 'clin/command_parser'
|
35
42
|
require 'clin/general_option'
|
36
43
|
require 'clin/command_dispatcher'
|
@@ -38,4 +45,6 @@ require 'clin/common/help_options'
|
|
38
45
|
require 'clin/errors'
|
39
46
|
require 'clin/option'
|
40
47
|
require 'clin/option_list'
|
48
|
+
require 'clin/text'
|
41
49
|
require 'clin/shell'
|
50
|
+
require 'clin/line_reader'
|
@@ -57,7 +57,7 @@ RSpec.describe Clin::CommandDispatcher do
|
|
57
57
|
|
58
58
|
context 'when first command return FixedArgumentError' do
|
59
59
|
before do
|
60
|
-
allow(cmd1).to receive(:parse) { fail Clin::
|
60
|
+
allow(cmd1).to receive(:parse) { fail Clin::RequiredArgumentError, :some }
|
61
61
|
subject.parse(args)
|
62
62
|
end
|
63
63
|
it { expect(cmd1).to have_received(:parse).with(args, fallback_help: false) }
|
@@ -43,25 +43,48 @@ RSpec.describe Clin::CommandMixin::Options do
|
|
43
43
|
it { expect(subject.general_options.values.first).to eq(true) }
|
44
44
|
end
|
45
45
|
|
46
|
-
describe '#
|
46
|
+
describe '#options' do
|
47
47
|
subject { new_subject }
|
48
|
-
let(:
|
49
|
-
let(:
|
50
|
-
let(:
|
51
|
-
let(:
|
52
|
-
let(:
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
48
|
+
let(:option1) { double(:option1) }
|
49
|
+
let(:option2) { double(:option2) }
|
50
|
+
let(:option3) { double(:option3) }
|
51
|
+
let(:option4) { double(:option4) }
|
52
|
+
let(:general_option1) do
|
53
|
+
opt = Class.new(Clin::GeneralOption)
|
54
|
+
opt.add_option option3
|
55
|
+
opt.add_option option4
|
56
|
+
opt
|
57
|
+
end
|
58
|
+
|
59
|
+
let(:general_option2) do
|
60
|
+
opt = Class.new(Clin::GeneralOption)
|
61
|
+
opt.general_option general_option1
|
62
|
+
opt
|
63
|
+
end
|
58
64
|
|
59
|
-
|
65
|
+
it 'get every options' do
|
66
|
+
subject.add_option option1
|
67
|
+
subject.add_option option2
|
68
|
+
expect(subject.options).to eq([option1, option2])
|
60
69
|
end
|
61
70
|
|
62
|
-
it
|
63
|
-
|
64
|
-
|
71
|
+
it 'get every general option options' do
|
72
|
+
subject.general_option general_option1
|
73
|
+
expect(subject.options).to eq([option3, option4])
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'get every options and general option options' do
|
77
|
+
subject.add_option option1
|
78
|
+
subject.add_option option2
|
79
|
+
subject.general_option general_option1
|
80
|
+
expect(subject.options).to eq([option1, option2, option3, option4])
|
81
|
+
end
|
65
82
|
|
83
|
+
it 'get nested general options' do
|
84
|
+
subject.add_option option1
|
85
|
+
subject.add_option option2
|
86
|
+
subject.general_option general_option2
|
87
|
+
expect(subject.options).to eq([option1, option2, option3, option4])
|
88
|
+
end
|
66
89
|
end
|
67
90
|
end
|
@@ -12,10 +12,15 @@ RSpec.describe Clin::CommandParser do
|
|
12
12
|
subject { Clin::CommandParser.new(@command, []) }
|
13
13
|
|
14
14
|
it 'raise argument when option value is missing' do
|
15
|
-
|
15
|
+
subject.parse_options(%w(--name))
|
16
|
+
expect(subject.valid?).to be false
|
17
|
+
expect(subject.errors.first).to be_a(Clin::MissingOptionArgumentError)
|
16
18
|
end
|
17
|
-
|
18
|
-
|
19
|
+
|
20
|
+
it 'add error when unknown option' do
|
21
|
+
subject.parse_options(%w(--other))
|
22
|
+
expect(subject.errors.size).to be 1
|
23
|
+
expect(subject.errors.first).to be_a(Clin::OptionError)
|
19
24
|
end
|
20
25
|
|
21
26
|
it { expect(subject.parse_options(%w(--name MyName))).to eq(name: 'MyName') }
|
@@ -25,7 +30,7 @@ RSpec.describe Clin::CommandParser do
|
|
25
30
|
|
26
31
|
it { expect(subject.parse_options(%w(-v))).to eq(verbose: true) }
|
27
32
|
|
28
|
-
it { expect(subject.parse_options(%w(--echo))).to eq(echo:
|
33
|
+
it { expect(subject.parse_options(%w(--echo))).to eq(echo: true) }
|
29
34
|
it { expect(subject.parse_options(%w(-e EchoThis))).to eq(echo: 'EchoThis') }
|
30
35
|
end
|
31
36
|
|
@@ -39,14 +44,21 @@ RSpec.describe Clin::CommandParser do
|
|
39
44
|
subject { Clin::CommandParser.new(@command, []) }
|
40
45
|
|
41
46
|
it 'raise argument when fixed in different' do
|
42
|
-
|
47
|
+
subject.parse_arguments(%w(other val opt))
|
48
|
+
expect(subject.valid?).to be false
|
49
|
+
expect(subject.errors.first).to be_a Clin::CommandLineError
|
43
50
|
end
|
51
|
+
|
44
52
|
it 'raise error when too few arguments' do
|
45
|
-
|
53
|
+
subject.parse_arguments(%w(fix))
|
54
|
+
expect(subject.valid?).to be false
|
55
|
+
expect(subject.errors.first).to be_a Clin::CommandLineError
|
46
56
|
end
|
57
|
+
|
47
58
|
it 'raise error when too much argument' do
|
48
|
-
|
49
|
-
|
59
|
+
subject.parse_arguments(%w(other val opt more))
|
60
|
+
expect(subject.valid?).to be false
|
61
|
+
expect(subject.errors.first).to be_a Clin::CommandLineError
|
50
62
|
end
|
51
63
|
|
52
64
|
it 'map arguments' do
|
@@ -58,43 +70,6 @@ RSpec.describe Clin::CommandParser do
|
|
58
70
|
end
|
59
71
|
end
|
60
72
|
|
61
|
-
describe '#skipped_options' do
|
62
|
-
def skipped_options(argv)
|
63
|
-
Clin::CommandParser.new(@command, argv).skipped_options
|
64
|
-
end
|
65
|
-
|
66
|
-
before :all do
|
67
|
-
@command = Class.new(Clin::Command)
|
68
|
-
@command.skip_options true
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'when all options should be skipped' do
|
72
|
-
it { expect(skipped_options(%w(pos arg))).to eq([]) }
|
73
|
-
|
74
|
-
it { expect(skipped_options(%w(pos arg --ignore -t))).to eq(%w(--ignore -t)) }
|
75
|
-
|
76
|
-
it { expect(skipped_options(%w(pos arg --ignore value -t))).to eq(%w(--ignore value -t)) }
|
77
|
-
|
78
|
-
end
|
79
|
-
context 'when option are define they should not be skipped' do
|
80
|
-
before :all do
|
81
|
-
@command.flag_option :verbose, 'Verbose'
|
82
|
-
end
|
83
|
-
|
84
|
-
it { expect(skipped_options(%w(pos arg --ignore value -t -v))).to eq(%w(--ignore value -t)) }
|
85
|
-
|
86
|
-
it do
|
87
|
-
expect(skipped_options(%w(pos arg --verbose --ignore value -t)))
|
88
|
-
.to eq(%w(--ignore value -t))
|
89
|
-
end
|
90
|
-
|
91
|
-
it do
|
92
|
-
expect(skipped_options(%w(pos arg --ignore value --verbose -t)))
|
93
|
-
.to eq(%w(--ignore value -t))
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
73
|
describe '.handle_dispatch' do
|
99
74
|
let(:args) { [Faker::Lorem.word, Faker::Lorem.word] }
|
100
75
|
before :all do
|
@@ -132,12 +107,14 @@ RSpec.describe Clin::CommandParser do
|
|
132
107
|
context 'when using commands' do
|
133
108
|
let(:cmd1) { double(:command_mixin) }
|
134
109
|
let(:cmd2) { double(:command_mixin) }
|
110
|
+
let(:dispatcher) { double(:dispatcher, parse: true) }
|
135
111
|
before do
|
136
112
|
@command.dispatch :args, commands: [cmd1, cmd2]
|
137
|
-
allow_any_instance_of(Clin::CommandDispatcher).to receive(:initialize)
|
138
113
|
end
|
114
|
+
|
139
115
|
it 'call the command dispatcher with the right arguments' do
|
140
|
-
|
116
|
+
expect(Clin::CommandDispatcher)
|
117
|
+
.to receive(:new).once.with([cmd1, cmd2]).and_return(dispatcher)
|
141
118
|
subject.redispatch(remote: 'remote', args: args)
|
142
119
|
end
|
143
120
|
end
|
@@ -146,19 +123,18 @@ RSpec.describe Clin::CommandParser do
|
|
146
123
|
let(:new_message) { Faker::Lorem.sentence }
|
147
124
|
before do
|
148
125
|
@command.dispatch :args
|
149
|
-
allow_any_instance_of(Clin::CommandDispatcher).to receive(:
|
126
|
+
allow_any_instance_of(Clin::CommandDispatcher).to receive(:new)
|
150
127
|
allow_any_instance_of(Clin::CommandDispatcher).to receive(:parse) do
|
151
128
|
fail Clin::HelpError, 'Dispatcher error'
|
152
129
|
end
|
153
|
-
allow(@command).to receive(:
|
130
|
+
allow(@command).to receive(:help).and_return(new_message)
|
154
131
|
end
|
155
132
|
it do
|
156
133
|
expect { subject.redispatch(remote: 'remote', args: args) }
|
157
134
|
.to raise_error(Clin::HelpError)
|
158
135
|
end
|
159
136
|
it do
|
160
|
-
expect { subject.redispatch(remote: 'remote', args: args) }
|
161
|
-
.to raise_error(new_message)
|
137
|
+
expect { subject.redispatch(remote: 'remote', args: args) }.to raise_error(new_message)
|
162
138
|
end
|
163
139
|
end
|
164
140
|
end
|