clin 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.lint-ci.yml +2 -0
  3. data/.simplecov +5 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +11 -0
  6. data/README.md +5 -4
  7. data/benchmarks/bench.rb +21 -0
  8. data/benchmarks/text_bench.rb +78 -0
  9. data/clin.gemspec +2 -1
  10. data/examples/reusable_options.rb +19 -0
  11. data/examples/simple.rb +8 -3
  12. data/examples/test.rb +5 -5
  13. data/examples/text_builder.rb +40 -0
  14. data/lib/clin/argument.rb +19 -2
  15. data/lib/clin/command_mixin/core.rb +13 -18
  16. data/lib/clin/command_mixin/options.rb +37 -26
  17. data/lib/clin/command_parser.rb +46 -57
  18. data/lib/clin/common/help_options.rb +1 -0
  19. data/lib/clin/errors.rb +50 -4
  20. data/lib/clin/line_reader/basic.rb +38 -0
  21. data/lib/clin/line_reader/readline.rb +53 -0
  22. data/lib/clin/line_reader.rb +16 -0
  23. data/lib/clin/option.rb +24 -11
  24. data/lib/clin/option_parser.rb +159 -0
  25. data/lib/clin/shell.rb +36 -15
  26. data/lib/clin/shell_interaction/choose.rb +19 -11
  27. data/lib/clin/shell_interaction/file_conflict.rb +4 -1
  28. data/lib/clin/shell_interaction/select.rb +44 -0
  29. data/lib/clin/shell_interaction.rb +1 -0
  30. data/lib/clin/text/table.rb +270 -0
  31. data/lib/clin/text.rb +152 -0
  32. data/lib/clin/version.rb +1 -1
  33. data/lib/clin.rb +10 -1
  34. data/spec/clin/command_dispacher_spec.rb +1 -1
  35. data/spec/clin/command_mixin/options_spec.rb +38 -15
  36. data/spec/clin/command_parser_spec.rb +27 -51
  37. data/spec/clin/line_reader/basic_spec.rb +54 -0
  38. data/spec/clin/line_reader/readline_spec.rb +64 -0
  39. data/spec/clin/line_reader_spec.rb +17 -0
  40. data/spec/clin/option_parser_spec.rb +217 -0
  41. data/spec/clin/option_spec.rb +5 -7
  42. data/spec/clin/shell_interaction/choose_spec.rb +30 -0
  43. data/spec/clin/shell_interaction/file_interaction_spec.rb +18 -0
  44. data/spec/clin/shell_interaction/select_spec.rb +96 -0
  45. data/spec/clin/shell_spec.rb +42 -0
  46. data/spec/clin/text/table_cell_spec.rb +72 -0
  47. data/spec/clin/text/table_row_spec.rb +74 -0
  48. data/spec/clin/text/table_separator_row_spec.rb +82 -0
  49. data/spec/clin/text/table_spec.rb +259 -0
  50. data/spec/clin/text_spec.rb +158 -0
  51. data/spec/examples/list_option_spec.rb +6 -2
  52. data/spec/examples/reusable_options_spec.rb +21 -0
  53. data/spec/examples/simple_spec.rb +9 -9
  54. data/spec/spec_helper.rb +3 -2
  55. 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
@@ -1,4 +1,4 @@
1
1
  # Clin version
2
2
  module Clin
3
- VERSION = '0.3.0'
3
+ VERSION = '0.4.0'
4
4
  end
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::FixedArgumentError, :some }
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 '#register_options' do
46
+ describe '#options' do
47
47
  subject { new_subject }
48
- let(:opt1) { double(:option, register: true) }
49
- let(:opt2) { double(:option, register: true) }
50
- let(:g_opt_cls) { double(:general_option_class, register_options: true) }
51
- let(:g_opt) { double(:general_option, class: g_opt_cls) }
52
- let(:opts) { double(:options) }
53
- let(:out) { double(:out) }
54
- before do
55
- subject.add_option(opt1)
56
- subject.add_option(opt2)
57
- subject.general_options = {g_opt_cls => g_opt}
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
- subject.register_options(opts, out)
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 { expect(opt1).to have_received(:register).with(opts, out) }
63
- it { expect(opt2).to have_received(:register).with(opts, out) }
64
- it { expect(g_opt_cls).to have_received(:register_options).with(opts, out) }
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
- expect { subject.parse_options(%w(--name)) }.to raise_error(OptionParser::MissingArgument)
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
- it 'raise error when unknown option' do
18
- expect { subject.parse_options(%w(--other)) }.to raise_error(Clin::OptionError)
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: nil) }
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
- expect { subject.parse_arguments(%w(other val opt)) }.to raise_error(Clin::CommandLineError)
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
- expect { subject.parse_arguments(['fix']) }.to raise_error(Clin::CommandLineError)
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
- expect { subject.parse_arguments(%w(other val opt more)) }
49
- .to raise_error(Clin::CommandLineError)
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
- expect_any_instance_of(Clin::CommandDispatcher).to receive(:initialize).once.with([cmd1, cmd2])
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(:initialize)
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(:option_parser).and_return(new_message)
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