gli 1.6.0 → 2.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.gitignore +11 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE.txt +201 -0
  6. data/ObjectModel.graffle +1191 -0
  7. data/README.rdoc +60 -10
  8. data/Rakefile +145 -0
  9. data/bin/gli +12 -30
  10. data/bin/report_on_rake_results +10 -0
  11. data/bin/test_all_rubies.sh +6 -0
  12. data/features/gli_executable.feature +84 -0
  13. data/features/gli_init.feature +219 -0
  14. data/features/step_definitions/gli_executable_steps.rb +12 -0
  15. data/features/step_definitions/gli_init_steps.rb +11 -0
  16. data/features/step_definitions/todo_steps.rb +69 -0
  17. data/features/support/env.rb +49 -0
  18. data/features/todo.feature +182 -0
  19. data/gli.cheat +95 -0
  20. data/gli.gemspec +34 -0
  21. data/lib/gli.rb +11 -571
  22. data/lib/gli/app.rb +184 -0
  23. data/lib/gli/app_support.rb +226 -0
  24. data/lib/gli/command.rb +107 -95
  25. data/lib/gli/command_line_option.rb +34 -0
  26. data/lib/gli/command_line_token.rb +13 -9
  27. data/lib/gli/command_support.rb +200 -0
  28. data/lib/gli/commands/compound_command.rb +42 -0
  29. data/lib/gli/commands/help.rb +63 -0
  30. data/lib/gli/commands/help_modules/command_help_format.rb +134 -0
  31. data/lib/gli/commands/help_modules/global_help_format.rb +61 -0
  32. data/lib/gli/commands/help_modules/list_formatter.rb +22 -0
  33. data/lib/gli/commands/help_modules/options_formatter.rb +50 -0
  34. data/lib/gli/commands/help_modules/text_wrapper.rb +53 -0
  35. data/lib/gli/commands/initconfig.rb +67 -0
  36. data/lib/{support → gli/commands}/scaffold.rb +150 -34
  37. data/lib/gli/dsl.rb +194 -0
  38. data/lib/gli/exceptions.rb +13 -4
  39. data/lib/gli/flag.rb +30 -41
  40. data/lib/gli/gli_option_parser.rb +98 -0
  41. data/lib/gli/option_parser_factory.rb +44 -0
  42. data/lib/gli/options.rb +2 -1
  43. data/lib/gli/switch.rb +19 -51
  44. data/lib/gli/terminal.rb +30 -20
  45. data/lib/gli/version.rb +5 -0
  46. data/test/apps/README.md +2 -0
  47. data/test/apps/todo/Gemfile +2 -0
  48. data/test/apps/todo/README.rdoc +6 -0
  49. data/test/apps/todo/Rakefile +23 -0
  50. data/test/apps/todo/bin/todo +52 -0
  51. data/test/apps/todo/lib/todo/commands/create.rb +22 -0
  52. data/test/apps/todo/lib/todo/commands/list.rb +53 -0
  53. data/test/apps/todo/lib/todo/commands/ls.rb +47 -0
  54. data/test/apps/todo/lib/todo/version.rb +3 -0
  55. data/test/apps/todo/test/tc_nothing.rb +14 -0
  56. data/test/apps/todo/todo.gemspec +23 -0
  57. data/test/apps/todo/todo.rdoc +5 -0
  58. data/test/config.yaml +10 -0
  59. data/test/fake_std_out.rb +30 -0
  60. data/test/gli.reek +122 -0
  61. data/test/init_simplecov.rb +8 -0
  62. data/test/option_test_helper.rb +13 -0
  63. data/test/roodi.yaml +18 -0
  64. data/test/tc_command.rb +260 -0
  65. data/test/tc_compount_command.rb +22 -0
  66. data/test/tc_flag.rb +56 -0
  67. data/test/tc_gli.rb +611 -0
  68. data/test/tc_help.rb +223 -0
  69. data/test/tc_options.rb +31 -0
  70. data/test/tc_subcommands.rb +162 -0
  71. data/test/tc_switch.rb +57 -0
  72. data/test/tc_terminal.rb +97 -0
  73. data/test/test_helper.rb +13 -0
  74. metadata +318 -49
  75. data/lib/gli_version.rb +0 -3
  76. data/lib/support/help.rb +0 -179
  77. data/lib/support/initconfig.rb +0 -34
  78. data/lib/support/rdoc.rb +0 -119
@@ -0,0 +1,5 @@
1
+ = todo
2
+
3
+ Generate this with
4
+ todo rdoc
5
+ After you have described your command line interface
data/test/config.yaml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ commands:
3
+ :command:
4
+ :g: bar
5
+ :s: true
6
+ :other_command:
7
+ :g: foobar
8
+ :f: barfoo
9
+ :f: foo
10
+ :bleorgh: true
@@ -0,0 +1,30 @@
1
+ class FakeStdOut
2
+ attr_reader :strings
3
+
4
+ def initialize
5
+ @strings = []
6
+ end
7
+
8
+ def puts(string=nil)
9
+ @strings << string unless string.nil?
10
+ end
11
+
12
+ def write(x)
13
+ puts(x)
14
+ end
15
+
16
+ def printf(*args)
17
+ puts(Kernel.printf(*args))
18
+ end
19
+
20
+ # Returns true if the regexp matches anything in the output
21
+ def contained?(regexp)
22
+ strings.find{ |x| x =~ regexp }
23
+ end
24
+
25
+ def flush; end
26
+
27
+ def to_s
28
+ @strings.join("\n")
29
+ end
30
+ end
data/test/gli.reek ADDED
@@ -0,0 +1,122 @@
1
+ ---
2
+ ControlCouple:
3
+ exclude:
4
+ - 'parse_options_helper'
5
+ - 'program_desc'
6
+ - 'program_name'
7
+ - 'name_as_string'
8
+ - 'GLI::Command'
9
+ enabled: false
10
+ UncommunicativeParameterName:
11
+ accept: []
12
+
13
+ exclude: []
14
+
15
+ enabled: false
16
+ reject:
17
+ - !ruby/regexp /^.$/
18
+ - !ruby/regexp /[0-9]$/
19
+ - !ruby/regexp /[A-Z]/
20
+ LargeClass:
21
+ max_methods: 25
22
+ exclude: []
23
+
24
+ enabled: true
25
+ max_instance_variables: 10
26
+ UncommunicativeMethodName:
27
+ accept: []
28
+
29
+ exclude: []
30
+
31
+ enabled: true
32
+ reject:
33
+ - !ruby/regexp /^[a-z]$/
34
+ - !ruby/regexp /[0-9]$/
35
+ - !ruby/regexp /[A-Z]/
36
+ LongParameterList:
37
+ max_params: 5
38
+ exclude: []
39
+
40
+ enabled: true
41
+ overrides:
42
+ initialize:
43
+ max_params: 6
44
+ FeatureEnvy:
45
+ exclude: &id001 []
46
+
47
+ enabled: false
48
+ ClassVariable:
49
+ exclude: *id001
50
+ enabled: false
51
+ BooleanParameter:
52
+ exclude: ['initialize']
53
+ enabled: true
54
+ IrresponsibleModule:
55
+ exclude: *id001
56
+ enabled: true
57
+ UncommunicativeModuleName:
58
+ accept:
59
+ - Inline::C
60
+ exclude: []
61
+
62
+ enabled: true
63
+ reject:
64
+ - !ruby/regexp /^.$/
65
+ - !ruby/regexp /[0-9]$/
66
+ NestedIterators:
67
+ ignore_iterators: []
68
+
69
+ exclude: ['GLI::App']
70
+
71
+ enabled: true
72
+ max_allowed_nesting: 3
73
+ LongMethod:
74
+ max_statements: 10
75
+ exclude:
76
+ - initialize
77
+ - parse_options
78
+ - run
79
+ - reset
80
+ enabled: true
81
+ Duplication:
82
+ allow_calls: []
83
+
84
+ exclude: ['run']
85
+
86
+ enabled: true
87
+ max_calls: 3
88
+ UtilityFunction:
89
+ max_helper_calls: 1
90
+ exclude: [ 'find_non_flag_index', 'override_default', 'proceed?', 'command_exists?' ]
91
+
92
+ enabled: true
93
+ Attribute:
94
+ exclude: []
95
+
96
+ enabled: false
97
+ UncommunicativeVariableName:
98
+ accept: []
99
+
100
+ exclude: []
101
+
102
+ enabled: true
103
+ reject:
104
+ - !ruby/regexp /^[a-z]$/
105
+ - !ruby/regexp /[0-9]$/
106
+ - !ruby/regexp /[A-Z]/
107
+ SimulatedPolymorphism:
108
+ exclude: []
109
+
110
+ enabled: true
111
+ max_ifs: 2
112
+ DataClump:
113
+ exclude: []
114
+
115
+ enabled: false
116
+ max_copies: 2
117
+ min_clump_size: 2
118
+ LongYieldList:
119
+ max_params: 3
120
+ exclude: []
121
+
122
+ enabled: true
@@ -0,0 +1,8 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter "/test"
5
+ end
6
+ rescue LoadError
7
+ # Don't care
8
+ end
@@ -0,0 +1,13 @@
1
+ module OptionTestHelper
2
+ def name_should_be(name)
3
+ lambda {
4
+ assert_equal(name,@cli_option.name)
5
+ }
6
+ end
7
+
8
+ def aliases_should_be(aliases)
9
+ lambda {
10
+ assert_equal(aliases,@cli_option.aliases)
11
+ }
12
+ end
13
+ end
data/test/roodi.yaml ADDED
@@ -0,0 +1,18 @@
1
+ # Not the most strict checks, but they pass for now
2
+ AssignmentInConditionalCheck: {}
3
+ ClassLineCountCheck:
4
+ line_count: 400
5
+ ClassNameCheck:
6
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
7
+ CyclomaticComplexityBlockCheck:
8
+ complexity: 5
9
+ CyclomaticComplexityMethodCheck:
10
+ complexity: 8
11
+ EmptyRescueBodyCheck: {}
12
+ ForLoopCheck: {}
13
+ MethodNameCheck:
14
+ pattern: !ruby/regexp /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
15
+ ModuleNameCheck:
16
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
17
+ ParameterNumberCheck:
18
+ parameter_count: 5
@@ -0,0 +1,260 @@
1
+ require 'test_helper'
2
+ require 'tempfile'
3
+
4
+ class TC_testCommand < Clean::Test::TestCase
5
+ include TestHelper
6
+ def setup
7
+ @app = CLIApp.new
8
+ @app.reset
9
+ @app.program_desc 'A super awesome program'
10
+ @app.desc 'Some Global Option'
11
+ @app.switch :g
12
+ @app.switch :blah
13
+ @app.long_desc 'This is a very long description for a flag'
14
+ @app.flag [:y,:yes]
15
+ @pre_called = false
16
+ @post_called = false
17
+ @error_called = false
18
+ @app.pre { |g,c,o,a| @pre_called = true }
19
+ @app.post { |g,c,o,a| @post_called = true }
20
+ @app.on_error { |g,c,o,a| @error_called = true }
21
+ @glob = nil
22
+ @verbose = nil
23
+ @glob_verbose = nil
24
+ @configure = nil
25
+ @args = nil
26
+ @app.desc 'Some Basic Command that potentially has a really really really really really really really long description and stuff, but you know, who cares?'
27
+ @app.long_desc 'This is the long description: "Some Basic Command that potentially has a really really really really really really really long description and stuff, but you know, who cares?"'
28
+ @app.arg_name 'first_file second_file'
29
+ @app.command [:basic,:bs] do |c|
30
+ c.desc 'be verbose'
31
+ c.switch :v
32
+ c.desc 'configure something or other, in some way that requires a lot of verbose text and whatnot'
33
+ c.default_value 'crud'
34
+ c.flag [:c,:configure]
35
+ c.action do |global_options,options,arguments|
36
+ @glob = global_options[:g] ? 'true' : 'false'
37
+ @verbose = options[:v] ? 'true' : 'false'
38
+ @glob_verbose = global_options[:v] ? 'true' : 'false'
39
+ @configure = options[:c]
40
+ @args = arguments
41
+ end
42
+ end
43
+ @app.desc "Testing long help wrapping"
44
+ @app.long_desc <<-EOS
45
+ This will create a scaffold command line project that uses @app
46
+ for command line processing. Specifically, this will create
47
+ an executable ready to go, as well as a lib and test directory, all
48
+ inside the directory named for your project
49
+ EOS
50
+ @app.command [:test_wrap] do |c|
51
+ c.action {}
52
+ end
53
+ @fake_stdout = FakeStdOut.new
54
+ @fake_stderr = FakeStdOut.new
55
+ @app.error_device=@fake_stderr
56
+ ENV.delete('GLI_DEBUG')
57
+ @original_stdout = $stdout
58
+ $stdout = @fake_stdout
59
+ @original_stderr = $stderr
60
+ $stderr = @fake_stderr
61
+ end
62
+
63
+ def tear_down
64
+ $stdout = @original_stdout
65
+ $stderr = @original_stderr
66
+ FileUtils.rm_f "cruddo.rdoc"
67
+ end
68
+
69
+ def test_names
70
+ command = GLI::Command.new(:names => [:ls,:list,:'list-them-all'],:description => "List")
71
+ assert_equal "ls, list, list-them-all",command.names
72
+ end
73
+
74
+ def test_command_sort
75
+ commands = [GLI::Command.new(:names => :foo)]
76
+ commands << GLI::Command.new(:names => :bar)
77
+ commands << GLI::Command.new(:names => :zazz)
78
+ commands << GLI::Command.new(:names => :zaz)
79
+
80
+ sorted = commands.sort
81
+ assert_equal :bar,sorted[0].name
82
+ assert_equal :foo,sorted[1].name
83
+ assert_equal :zaz,sorted[2].name
84
+ assert_equal :zazz,sorted[3].name
85
+ end
86
+
87
+ def test_basic_command
88
+ args_args = [%w(-g basic -v -c foo bar baz quux), %w(-g basic -v --configure=foo bar baz quux)]
89
+ args_args.each do |args|
90
+ args_orig = args.clone
91
+ @app.run(args)
92
+ assert_equal('true',@glob,"For args #{args_orig}")
93
+ assert_equal('true',@verbose,"For args #{args_orig}")
94
+ assert_equal('false',@glob_verbose,"For args #{args_orig}")
95
+ assert_equal('foo',@configure,"For args #{args_orig}")
96
+ assert_equal(%w(bar baz quux),@args,"For args #{args_orig}")
97
+ assert(@pre_called,"Pre block should have been called for args #{args_orig}")
98
+ assert(@post_called,"Post block should have been called for args #{args_orig}")
99
+ assert(!@error_called,"Error block should not have been called for args #{args_orig}")
100
+ end
101
+ end
102
+
103
+ def test_command_skips_pre
104
+ @app.skips_pre
105
+ @app.skips_post
106
+
107
+ skips_pre_called = false
108
+ runs_pre_called = false
109
+
110
+ @app.command [:skipspre] do |c|
111
+ c.action do |g,o,a|
112
+ skips_pre_called = true
113
+ end
114
+ end
115
+
116
+ # Making sure skips_pre doesn't leak to other commands
117
+ @app.command [:runspre] do |c|
118
+ c.action do |g,o,a|
119
+ runs_pre_called = true
120
+ end
121
+ end
122
+
123
+ @app.run(['skipspre'])
124
+
125
+ assert(skips_pre_called,"'skipspre' should have been called")
126
+ assert(!@pre_called,"Pre block should not have been called")
127
+ assert(!@post_called,"Post block should not have been called")
128
+ assert(!@error_called,"Error block should not have been called")
129
+
130
+ @app.run(['runspre'])
131
+
132
+ assert(runs_pre_called,"'runspre' should have been called")
133
+ assert(@pre_called,"Pre block should not have been called")
134
+ assert(@post_called,"Post block SHOULD have been called")
135
+ assert(!@error_called,"Error block should not have been called")
136
+ end
137
+
138
+ def test_command_no_globals
139
+ args = %w(basic -c foo bar baz quux)
140
+ @app.run(args)
141
+ assert_equal('foo',@configure)
142
+ assert_equal(%w(bar baz quux),@args)
143
+ end
144
+
145
+ def test_defaults_get_set
146
+ args = %w(basic bar baz quux)
147
+ @app.run(args)
148
+ assert_equal('false',@glob)
149
+ assert_equal('false',@verbose)
150
+ assert_equal('crud',@configure)
151
+ assert_equal(%w(bar baz quux),@args)
152
+ end
153
+
154
+ def test_negatable_gets_created
155
+ @app.command [:foo] do |c|
156
+ c.action do |g,o,a|
157
+ assert !g[:blah]
158
+ end
159
+ end
160
+ exit_status = @app.run(%w(--no-blah foo))
161
+ assert_equal 0,exit_status
162
+ end
163
+
164
+ def test_arguments_are_not_frozen
165
+ @args = []
166
+
167
+
168
+ @app.command [:foo] do |c|
169
+ c.action do |g,o,a|
170
+ @args = a
171
+ end
172
+ end
173
+ exit_status = @app.run(%w(foo a b c d e).map { |arg| arg.freeze })
174
+ assert_equal 0,exit_status
175
+ assert_equal 5,@args.length,"Action block was not called"
176
+
177
+ @args.each_with_index do |arg,index|
178
+ assert !arg.frozen?,"Expected argument at index #{index} to not be frozen"
179
+ end
180
+ end
181
+
182
+ def test_no_arguments
183
+ args = %w(basic -v)
184
+ @app.run(args)
185
+ assert_equal('true',@verbose)
186
+ assert_equal('crud',@configure)
187
+ assert_equal([],@args)
188
+ end
189
+
190
+ def test_unknown_command
191
+ args = %w(blah)
192
+ @app.run(args)
193
+ assert(!@post_called)
194
+ assert(@error_called)
195
+ assert_contained(@fake_stderr,/Unknown command 'blah'/)
196
+ end
197
+
198
+ def test_unknown_global_option
199
+ args = %w(--quux basic)
200
+ @app.run(args)
201
+ assert(!@post_called)
202
+ assert(@error_called,"Expected error callback to be called")
203
+ assert_contained(@fake_stderr,/Unknown option --quux/)
204
+ end
205
+
206
+ def test_unknown_argument
207
+ args = %w(basic --quux)
208
+ @app.run(args)
209
+ assert(!@post_called)
210
+ assert(@error_called)
211
+ assert_contained(@fake_stderr,/ Unknown option --quux/)
212
+ end
213
+
214
+ def test_forgot_action_block
215
+ @app.reset
216
+ @app.command :foo do
217
+ end
218
+
219
+ ENV['GLI_DEBUG'] = 'true'
220
+ assert_raises RuntimeError do
221
+ @app.run(['foo'])
222
+ end
223
+ assert_match /Command 'foo' has no action block/,@fake_stderr.to_s
224
+ end
225
+
226
+ def test_command_create
227
+ @app.desc 'single symbol'
228
+ @app.command :single do |c|; end
229
+ command = @app.commands[:single]
230
+ assert_equal :single, command.name
231
+ assert_equal nil, command.aliases
232
+
233
+ description = 'implicit array'
234
+ @app.desc description
235
+ @app.command :foo, :bar do |c|; end
236
+ command = @app.commands[:foo]
237
+ assert_equal :foo, command.name
238
+ assert_equal [:bar], command.aliases
239
+
240
+ description = 'explicit array'
241
+ @app.desc description
242
+ @app.command [:baz, :blah] do |c|; end
243
+ command = @app.commands[:baz]
244
+ assert_equal :baz, command.name
245
+ assert_equal [:blah], command.aliases
246
+ end
247
+
248
+ private
249
+
250
+ def assert_contained(output,regexp)
251
+ assert_not_nil output.contained?(regexp),
252
+ "Expected output to contain #{regexp.inspect}, output was:\n#{output}"
253
+ end
254
+
255
+ def assert_not_contained(output,regexp)
256
+ assert_nil output.contained?(regexp),
257
+ "Didn't expected output to contain #{regexp.inspect}, output was:\n#{output}"
258
+ end
259
+
260
+ end