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.
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