gli 1.6.0 → 2.0.0.rc3

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