opt 0.1.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.
@@ -0,0 +1,232 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec::Matchers.define :parse do |*expected|
4
+ match do |option|
5
+ result = {}
6
+ argv = expected.dup
7
+ option.parse!(argv, result)
8
+
9
+ expect(result).to include results if results.any?
10
+ expect(argv).to eq expected[1..-1] if shift?
11
+ end
12
+
13
+ chain :emit do |hash|
14
+ results.merge! hash
15
+ end
16
+
17
+ chain :shift_argv do
18
+ @shift = true
19
+ end
20
+
21
+ def shift?
22
+ @shift.nil? ? (@shift = false) : @shift
23
+ end
24
+
25
+ def results
26
+ @results ||= {}
27
+ end
28
+ end
29
+
30
+ def t(type, value)
31
+ Opt::Command::Token.new(type, value)
32
+ end
33
+
34
+ describe Opt::Option do
35
+ let(:option) { described_class.new(defin, {name: :test}.merge(opts)) }
36
+ let(:defin) { '' }
37
+ let(:opts) { {} }
38
+ subject { option }
39
+
40
+ describe '.parse_nargs' do
41
+ it 'should parse single fixnum' do
42
+ expect(described_class.parse_nargs(5)).to eq 5..5
43
+ end
44
+
45
+ it 'should parse range' do
46
+ expect(described_class.parse_nargs(2..6)).to eq 2..6
47
+ end
48
+
49
+ it 'should parse fixnum string' do
50
+ expect(described_class.parse_nargs('12')).to eq 12..12
51
+ end
52
+
53
+ it 'should parse special infinity string' do
54
+ expect(described_class.parse_nargs('*')).to eq 0..Float::INFINITY
55
+ end
56
+
57
+ it 'should parse special infinity symbol (I)' do
58
+ expect(described_class.parse_nargs(:inf)).to eq 0..Float::INFINITY
59
+ end
60
+
61
+ it 'should parse special infinity symbol (II)' do
62
+ expect(described_class.parse_nargs(:infinity)).to eq 0..Float::INFINITY
63
+ end
64
+
65
+ it 'should parse array (I)' do
66
+ expect(described_class.parse_nargs([3, :inf])).to eq 3..Float::INFINITY
67
+ end
68
+
69
+ it 'should parse array (II)' do
70
+ expect(described_class.parse_nargs([3, 100])).to eq 3..100
71
+ end
72
+
73
+ it 'should reject invalid string' do
74
+ expect do
75
+ described_class.parse_nargs('a12')
76
+ end.to raise_error(/invalid value for Integer/)
77
+ end
78
+
79
+ it 'should flip negative range' do
80
+ expect(described_class.parse_nargs(6..2)).to eq 2..6
81
+ end
82
+
83
+ it 'should reject negative range' do
84
+ expect do
85
+ described_class.parse_nargs(-2..2)
86
+ end.to raise_error(/Argument number must not be less than zero/)
87
+ end
88
+ end
89
+
90
+ describe '#initialize' do
91
+ it 'should derive name from switches (I)' do
92
+ expect(described_class.new('-h').name).to eq 'h'
93
+ end
94
+
95
+ it 'should derive name from switches (II)' do
96
+ expect(described_class.new('--help').name).to eq 'help'
97
+ end
98
+
99
+ it 'should use name from opts' do
100
+ expect(described_class.new('--help', name: :test).name).to eq 'test'
101
+ end
102
+
103
+ it 'should parse switches' do
104
+ option = described_class.new('-h, --help')
105
+ expect(option.switches.size).to eq 2
106
+ expect(option.switches.each.map(&:name)).to match_array %w(h help)
107
+ end
108
+
109
+ it 'should parse free-text arg' do
110
+ option = described_class.new('file', nargs: 0..Float::INFINITY)
111
+ expect(option.switches.size).to eq 0
112
+ expect(option).to be_text
113
+ expect(option.name).to eq 'file'
114
+ end
115
+ end
116
+
117
+ describe '#parse!' do
118
+ context 'with shirt code' do
119
+ let(:defin) { '-h, -2, -め' }
120
+
121
+ it 'should match short token' do
122
+ option.parse! argv = [t(:short, 'h')], result = {}
123
+
124
+ expect(argv).to be_empty
125
+ expect(result).to eq 'test' => true
126
+ end
127
+
128
+ it 'should match grouped short token' do
129
+ option.parse! argv = [t(:short, '2b4')], result = {}
130
+
131
+ expect(argv.size).to eq 1
132
+ expect(argv.first.value).to eq 'b4'
133
+ expect(result).to eq 'test' => true
134
+ end
135
+
136
+ it 'should match short unicode token' do
137
+ option.parse! argv = [t(:short, 'め')], result = {}
138
+
139
+ expect(argv).to be_empty
140
+ expect(result).to eq 'test' => true
141
+ end
142
+
143
+ it 'should not match long token' do
144
+ option.parse! argv = [t(:long, 'め')], result = {}
145
+
146
+ expect(argv.size).to eq 1
147
+ expect(result).to be_empty
148
+ end
149
+
150
+ it 'should not match different short token' do
151
+ option.parse! argv = [t(:short, '1')], result = {}
152
+
153
+ expect(argv.size).to eq 1
154
+ expect(result).to be_empty
155
+ end
156
+ end
157
+
158
+ context 'with long code' do
159
+ let(:defin) { '--help, --2, --めこそ' }
160
+
161
+ it 'should match long argv (I)' do
162
+ option.parse! argv = [t(:long, 'help')], result = {}
163
+
164
+ expect(argv).to be_empty
165
+ expect(result).to eq 'test' => true
166
+ end
167
+
168
+ it 'should match long argv (II)' do
169
+ option.parse! argv = [t(:long, '2')], result = {}
170
+
171
+ expect(argv).to be_empty
172
+ expect(result).to eq 'test' => true
173
+ end
174
+
175
+ it 'should match long argv (III)' do
176
+ option.parse! argv = [t(:long, 'めこそ')], result = {}
177
+
178
+ expect(argv).to be_empty
179
+ expect(result).to eq 'test' => true
180
+ end
181
+ end
182
+
183
+ context 'with free-text' do
184
+ let(:defin) { 'file' }
185
+ let(:opts) { {nargs: 1} }
186
+
187
+ it 'should match text' do
188
+ option.parse! argv = [t(:text, 'help')], result = {}
189
+
190
+ expect(argv).to be_empty
191
+ expect(result).to eq 'test' => 'help'
192
+ end
193
+
194
+ context 'with lower bound' do
195
+ let(:opts) { {nargs: 2..Float::INFINITY} }
196
+
197
+ it 'should reject to less arguments' do
198
+ expect do
199
+ option.parse! [t(:text, 'a')], {}
200
+ end.to raise_error(/wrong number of arguments/)
201
+ end
202
+
203
+ it 'should accept enough arguments' do
204
+ option.parse! argv = [t(:text, 'a'), t(:text, 'b')], result = {}
205
+
206
+ expect(argv).to be_empty
207
+ expect(result).to eq 'test' => %w(a b)
208
+ end
209
+ end
210
+
211
+ context 'with upper bound' do
212
+ let(:opts) { {nargs: 0..2} }
213
+
214
+ it 'should eat arguments up to upper bound' do
215
+ option.parse! argv = [t(:text, 'a'), t(:text, 'b'), t(:text, 'c')],
216
+ result = {}
217
+
218
+ expect(argv).to eq [t(:text, 'c')]
219
+ expect(result).to eq 'test' => %w(a b)
220
+ end
221
+
222
+ it 'should eat less arguments if there are less tokens' do
223
+ option.parse! argv = [t(:text, 'a'), t(:short, 'b'), t(:text, 'c')],
224
+ result = {}
225
+
226
+ expect(argv).to eq [t(:short, 'b'), t(:text, 'c')]
227
+ expect(result).to eq 'test' => %w(a)
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opt::Switch do
4
+ describe '.new' do
5
+ subject { described_class.new arg }
6
+
7
+ context 'with Switch object' do
8
+ let(:arg) { described_class.new '-x' }
9
+
10
+ it 'should return original switch object' do
11
+ is_expected.to eql arg
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#initialize' do
17
+ subject { described_class.new arg }
18
+
19
+ context 'with short code string' do
20
+ let(:arg) { '-h' }
21
+
22
+ it 'should parse to short code' do
23
+ expect(subject).to be_short
24
+ expect(subject.name).to eq 'h'
25
+ end
26
+ end
27
+
28
+ context 'with long code string' do
29
+ let(:arg) { '--help' }
30
+
31
+ it 'should parse to long code' do
32
+ expect(subject).to be_long
33
+ expect(subject.name).to eq 'help'
34
+ end
35
+ end
36
+
37
+ context 'with unicode parameter string' do
38
+ let(:arg) { '--めこそ' }
39
+
40
+ it 'should parse to long code' do
41
+ expect(subject).to be_long
42
+ expect(subject.name).to eq 'めこそ'
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '.parse' do
48
+ subject { described_class.parse arg }
49
+
50
+ context 'with string' do
51
+ let(:arg) { '-h, --help, -h' }
52
+
53
+ it 'should parse to Set' do
54
+ expect(subject).to be_a Set
55
+ expect(subject.size).to eq 2
56
+ expect(subject.map(&:name)).to match_array %w(h help)
57
+ end
58
+ end
59
+ end
60
+ end
data/spec/opt_spec.rb ADDED
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opt do
4
+ let(:opt) { Opt.new }
5
+
6
+ it 'should parse options (I)' do
7
+ opt.option '--help, -h', value: :yes
8
+
9
+ result = opt.parse %w(-h)
10
+ expect(result.help).to eq :yes
11
+
12
+ result = opt.parse %w()
13
+ expect(result.help).to eq nil
14
+ end
15
+
16
+ it 'should detect double-dash' do
17
+ opt.option '-v', name: :version
18
+ opt.option 'rest', nargs: '+'
19
+
20
+ result = opt.parse %w(-v -- -h)
21
+ expect(result.version?).to be true
22
+ expect(result.rest).to eq %w(-h)
23
+ end
24
+
25
+ it 'should parse options (II)' do
26
+ opt.option '--help', default: :no
27
+ opt.option '--version'
28
+
29
+ result = opt.parse %w(--version)
30
+ expect(result.version).to eql true
31
+ expect(result.help).to eql :no
32
+ end
33
+
34
+ it 'should parse options with argument' do
35
+ opt.option '--level, -l', nargs: 1
36
+
37
+ result = opt.parse %w(--level 5)
38
+ expect(result.level).to eq '5'
39
+
40
+ result = opt.parse %w(--level=5)
41
+ expect(result.level).to eq '5'
42
+
43
+ result = opt.parse %w(-l 5)
44
+ expect(result.level).to eq '5'
45
+
46
+ result = opt.parse %w(-l5)
47
+ expect(result.level).to eq '5'
48
+ end
49
+
50
+ it 'should parse options with arguments' do
51
+ opt.option '--level, -l', nargs: 2..3
52
+
53
+ result = opt.parse %w(--level 5 6)
54
+ expect(result.level).to eq %w(5 6)
55
+
56
+ result = opt.parse %w(-l 5 6)
57
+ expect(result.level).to eq %w(5 6)
58
+ end
59
+
60
+ it 'should parse flags' do
61
+ pending
62
+
63
+ opt.option :force, '-f, --force', default: false
64
+ opt.option :quiet, '-q, --quiet', default: true
65
+
66
+ result = opt.parse %w(--force)
67
+ expect(result[:force]).to be true
68
+ expect(result[:quiet]).to be true
69
+
70
+ result = opt.parse %w(--no-force -q)
71
+ expect(result[:force]).to be false
72
+ expect(result[:quiet]).to be true
73
+
74
+ result = opt.parse %w(-f --no-quiet)
75
+ expect(result[:force]).to be true
76
+ expect(result[:quiet]).to be false
77
+
78
+ result = opt.parse %w(--no-quiet)
79
+ expect(result[:force]).to be false
80
+ expect(result[:quiet]).to be false
81
+ end
82
+
83
+ it 'should parse subcommands (I)' do
84
+ opt.option '--force, -f'
85
+
86
+ opt.command :add, 'Add some things' do |cmd|
87
+ cmd.option '--verbose'
88
+ end
89
+
90
+ result = opt.parse %w(-f add --verbose)
91
+
92
+ expect(result.force?).to eq true
93
+ expect(result.verbose?).to eq true
94
+ expect(result.command).to eq %w(add)
95
+
96
+ result = opt.parse %w(add -f --verbose)
97
+
98
+ expect(result.force?).to eq true
99
+ expect(result.verbose?).to eq true
100
+ expect(result.command).to eq %w(add)
101
+ end
102
+
103
+ it 'should parse subcommands (II)' do
104
+ opt.option '-f'
105
+
106
+ opt.command :add, 'Add some things' do |cmd|
107
+ cmd.option '-a'
108
+ cmd.option '-j', nargs: 1
109
+ end
110
+
111
+ result = opt.parse %w(add -af)
112
+
113
+ expect(result.a?).to eq true
114
+ expect(result.f?).to eq true
115
+ expect(result.command).to eq %w(add)
116
+
117
+ result = opt.parse %w(add -ajf)
118
+
119
+ expect(result.a?).to eq true
120
+ expect(result.f?).to eq nil
121
+ expect(result.j).to eq 'f'
122
+ expect(result.command).to eq %w(add)
123
+ end
124
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opt do
4
+ let(:opt) { Opt.new }
5
+
6
+ it 'should pass README examples' do
7
+ content = File.read(File.expand_path('../../README.md', __FILE__))
8
+
9
+ while content =~ /^```ruby\n(.*?)^```\n(.*?)\z/m
10
+ example = $1
11
+ content = $2
12
+
13
+ example.gsub!(/^\s*(.*?)\s*#=>\s*(.*?)\s*$/, 'expect( \1 ).to eq( \2 )')
14
+
15
+ instance_eval example, __FILE__, __LINE__
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require 'rspec'
2
+
3
+ if ENV['CI'] || ENV['COVERAGE']
4
+ require 'coveralls'
5
+ Coveralls.wear! do
6
+ add_filter 'spec'
7
+ end
8
+ end
9
+
10
+ # Load stic
11
+ require 'opt'
12
+
13
+ # Load spec support files
14
+ Dir[File.expand_path('spec/support/**/*.rb')].each {|f| require f }
15
+
16
+ RSpec.configure do |config|
17
+ # Random order
18
+ config.order = 'random'
19
+ end