gli 2.0.0.rc3 → 2.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  = Git-Like Interface Command Line Parser
2
2
 
3
- <b>This describes the forthcoming GLI2 and might not be 100% accurate</b>
3
+ <b>GLI2 is currently in Release Candidate, so this might change before the official release</b>
4
4
 
5
5
  The best way to make a "command-suite" command-line application (for the best way to make a
6
6
  simpler command-line application, check out methadone[http://www.github.com/davetron5000/methadone]).
@@ -10,7 +10,7 @@ of syntax, but without restricting you in any way from the power of +OptionParse
10
10
 
11
11
  * {Overview}[http://davetron5000.github.com/gli]
12
12
  * {Source on Github}[http://github.com/davetron5000/gli]
13
- * RDoc[http://davetron5000.github.com/gli]
13
+ * RDoc[http://davetron5000.github.com/gli/rdoc/index.html]
14
14
 
15
15
  == Use
16
16
 
@@ -58,6 +58,13 @@ module GLI
58
58
  @skips_post = true
59
59
  end
60
60
 
61
+ # Use this if the following command should not have the around block executed.
62
+ # By default, the around block is executed, but for commands that might not want the
63
+ # setup to happen, this can be handy
64
+ def skips_around
65
+ @skips_around = true
66
+ end
67
+
61
68
  # Sets that this app uses a config file as well as the name of the config file.
62
69
  #
63
70
  # +filename+:: A String representing the path to the file to use for the config file. If it's an absolute
@@ -90,6 +97,25 @@ module GLI
90
97
  @post_block = a_proc
91
98
  end
92
99
 
100
+ # This inverts the pre/post concept. This is useful when you have a global shared resource that is governed by a block
101
+ # instead of separate open/close methods. The block you pass here will be given four parameters:
102
+ #
103
+ # global options:: the parsed global options
104
+ # command:: The GLI::Command that the user is going to invoke
105
+ # options:: the command specific options
106
+ # args:: unparsed command-line args
107
+ # code:: a block that you must +call+ to execute the command.
108
+ #
109
+ # #help_now! and #exit_now! work as expected; you can abort the command call by simply not calling it
110
+ #
111
+ # Note that if you declare an #around block, #pre and #post blocks will still work. The #pre is called first, followed by
112
+ # the around, followed by the #post.
113
+ #
114
+ # Call #skips_around before a command that should not have this hook fired
115
+ def around(&a_proc)
116
+ @around_block = a_proc
117
+ end
118
+
93
119
  # Define a block to run if an error occurs.
94
120
  # The block will receive any Exception that was caught.
95
121
  # It should evaluate to false to avoid the built-in error handling (which basically just
@@ -14,7 +14,7 @@ module GLI
14
14
  def reset # :nodoc:
15
15
  switches.clear
16
16
  flags.clear
17
- commands.clear
17
+ @commands = nil
18
18
  @version = nil
19
19
  @config_file = nil
20
20
  @use_openstruct = false
@@ -23,6 +23,7 @@ module GLI
23
23
  @pre_block = false
24
24
  @post_block = false
25
25
  @default_command = :help
26
+ @around_block = nil
26
27
  clear_nexts
27
28
  end
28
29
 
@@ -43,7 +44,7 @@ module GLI
43
44
 
44
45
  add_help_switch_if_needed(switches)
45
46
 
46
- global_options,command,options,arguments = GLIOptionParser.new(commands,flags,switches,accepts).parse_options(args)
47
+ global_options,command,options,arguments = GLIOptionParser.new(commands,flags,switches,accepts,@default_command).parse_options(args)
47
48
 
48
49
  copy_options_to_aliased_versions(global_options,command,options)
49
50
 
@@ -51,7 +52,6 @@ module GLI
51
52
  options = convert_to_openstruct_if_needed(options)
52
53
 
53
54
  if proceed?(global_options,command,options,arguments)
54
- command ||= commands[:help]
55
55
  call_command(command,global_options,options,arguments)
56
56
  end
57
57
  0
@@ -93,6 +93,7 @@ module GLI
93
93
  super
94
94
  @skips_post = false
95
95
  @skips_pre = false
96
+ @skips_around = false
96
97
  end
97
98
 
98
99
  def stderr
@@ -126,6 +127,12 @@ module GLI
126
127
  end
127
128
  end
128
129
 
130
+ def around_block
131
+ @around_block ||= Proc.new do |global,command,options,args,code|
132
+ code.call
133
+ end
134
+ end
135
+
129
136
  # Sets the default values for flags based on the configuration
130
137
  def override_defaults_based_on_config(config)
131
138
  override_default(flags,config)
@@ -216,7 +223,12 @@ module GLI
216
223
 
217
224
  def call_command(command,global_options,options,arguments)
218
225
  arguments = arguments.map { |arg| arg.dup } # unfreeze
219
- command.execute(global_options,options,arguments)
226
+ code = lambda { command.execute(global_options,options,arguments) }
227
+ if command.skips_around
228
+ code.call
229
+ else
230
+ around_block.call(global_options,command,options,arguments,code)
231
+ end
220
232
  unless command.skips_post
221
233
  post_block.call(global_options,command,options,arguments)
222
234
  end
@@ -42,11 +42,13 @@ module GLI
42
42
  # as a paragraph break. No other formatting is respected, though inner whitespace is maintained.
43
43
  # +skips_pre+:: if true, this command advertises that it doesn't want the pre block called first
44
44
  # +skips_post+:: if true, this command advertises that it doesn't want the post block called after it
45
+ # +skips_around+:: if true, this command advertises that it doesn't want the around block called
45
46
  def initialize(options)
46
47
  super(options[:names],options[:description],options[:long_desc])
47
48
  @arguments_description = options[:arguments_name] || ''
48
49
  @skips_pre = options[:skips_pre]
49
50
  @skips_post = options[:skips_post]
51
+ @skips_around = options[:skips_around]
50
52
  clear_nexts
51
53
  end
52
54
 
@@ -30,6 +30,11 @@ module GLI
30
30
  @skips_post
31
31
  end
32
32
 
33
+ # If true, this command doesn't want the around block called
34
+ def skips_around
35
+ @skips_around
36
+ end
37
+
33
38
  # Return the Array of the command's names
34
39
  def names
35
40
  all_forms
@@ -17,7 +17,8 @@ module GLI
17
17
  :arguments_name => 'command',
18
18
  :long_desc => 'Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function',
19
19
  :skips_pre => true,
20
- :skips_post => true)
20
+ :skips_post => true,
21
+ :skips_around => true)
21
22
  @app = app
22
23
  action do |global_options,options,arguments|
23
24
  show_help(global_options,options,arguments,output,error)
@@ -140,6 +140,7 @@ module GLI
140
140
  :long_desc => @next_long_desc,
141
141
  :skips_pre => @skips_pre,
142
142
  :skips_post => @skips_post,
143
+ :skips_around => @skips_around,
143
144
  }
144
145
  if names.first.kind_of? Hash
145
146
  command = GLI::Commands::CompoundCommand.new(self,
@@ -1,11 +1,12 @@
1
1
  module GLI
2
2
  # Parses the command-line options using an actual +OptionParser+
3
3
  class GLIOptionParser
4
- def initialize(commands,flags,switches,accepts)
4
+ def initialize(commands,flags,switches,accepts,default_command = nil)
5
5
  @commands = commands
6
6
  @flags = flags
7
7
  @switches = switches
8
8
  @accepts = accepts
9
+ @default_command = default_command
9
10
  end
10
11
 
11
12
  # Given the command-line argument array, returns and array of size 4:
@@ -87,10 +88,13 @@ module GLI
87
88
  names_to_commands[command_alias.to_s] = command
88
89
  end
89
90
  end
90
- name = name.to_s
91
- return names_to_commands[name] if names_to_commands[name]
92
- # Now try to match on partial names
93
- partial_matches = names_to_commands.keys.select { |command_name| command_name =~ /^#{name}/ }
91
+ names_to_commands.fetch(name.to_s) do |command_to_match|
92
+ find_command_by_partial_name(names_to_commands, command_to_match)
93
+ end
94
+ end
95
+
96
+ def find_command_by_partial_name(names_to_commands, command_to_match)
97
+ partial_matches = names_to_commands.keys.select { |command_name| command_name =~ /^#{command_to_match}/ }
94
98
  return names_to_commands[partial_matches[0]] if partial_matches.size == 1
95
99
  partial_matches
96
100
  end
@@ -1,5 +1,5 @@
1
1
  module GLI
2
2
  unless const_defined? :VERSION
3
- VERSION = '2.0.0.rc3' #:nodoc:
3
+ VERSION = '2.0.0.rc4' #:nodoc:
4
4
  end
5
5
  end
@@ -4,7 +4,15 @@ require 'tempfile'
4
4
  class TC_testCommand < Clean::Test::TestCase
5
5
  include TestHelper
6
6
  def setup
7
+ @fake_stdout = FakeStdOut.new
8
+ @fake_stderr = FakeStdOut.new
9
+ @original_stdout = $stdout
10
+ $stdout = @fake_stdout
11
+ @original_stderr = $stderr
12
+ $stderr = @fake_stderr
7
13
  @app = CLIApp.new
14
+ @app.error_device=@fake_stderr
15
+ ENV.delete('GLI_DEBUG')
8
16
  @app.reset
9
17
  @app.program_desc 'A super awesome program'
10
18
  @app.desc 'Some Global Option'
@@ -50,17 +58,9 @@ class TC_testCommand < Clean::Test::TestCase
50
58
  @app.command [:test_wrap] do |c|
51
59
  c.action {}
52
60
  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
61
  end
62
62
 
63
- def tear_down
63
+ def teardown
64
64
  $stdout = @original_stdout
65
65
  $stderr = @original_stderr
66
66
  FileUtils.rm_f "cruddo.rdoc"
@@ -100,6 +100,83 @@ class TC_testCommand < Clean::Test::TestCase
100
100
  end
101
101
  end
102
102
 
103
+ def test_around_filter
104
+ @around_block_called = false
105
+ @app.around do |global_options, command, options, arguments, code|
106
+ @around_block_called = true
107
+ code.call
108
+ end
109
+ @app.run(['bs'])
110
+ assert(@around_block_called, "Wrapper block should have been called")
111
+ end
112
+
113
+ def test_around_filter_can_be_skipped
114
+ # Given
115
+ @around_block_called = false
116
+ @action_called = false
117
+ @app.skips_around
118
+ @app.command :skips_around_filter do |c|
119
+ c.action do |g,o,a|
120
+ @action_called = true
121
+ end
122
+ end
123
+
124
+ @app.command :uses_around_filter do |c|
125
+ c.action do |g,o,a|
126
+ @action_called = true
127
+ end
128
+ end
129
+
130
+ @app.around do |global_options, command, options, arguments, code|
131
+ @around_block_called = true
132
+ code.call
133
+ end
134
+
135
+ # When
136
+ exit_status = @app.run(['skips_around_filter'])
137
+
138
+ # Then
139
+ assert_equal 0,exit_status
140
+ assert(!@around_block_called, "Wrapper block should have been skipped")
141
+ assert(@action_called,"Action should have been called")
142
+
143
+ # When
144
+ @around_block_called = false
145
+ @action_called = false
146
+ exit_status = @app.run(['uses_around_filter'])
147
+
148
+ # Then
149
+ assert_equal 0,exit_status
150
+ assert(@around_block_called, "Wrapper block should have been called")
151
+ assert(@action_called,"Action should have been called")
152
+ end
153
+
154
+ def test_around_filter_handles_exit_now
155
+ @around_block_called = false
156
+ @error_message = "OH NOES"
157
+ @app.around do |global_options, command, options, arguments, code|
158
+ @app.exit_now! @error_message
159
+ code.call
160
+ end
161
+ exit_code = @app.run(['bs'])
162
+ assert exit_code != 0
163
+ assert_contained(@fake_stderr,/#{@error_message}/)
164
+ assert_not_contained(@fake_stdout,/SYNOPSIS/)
165
+ end
166
+
167
+ def test_around_filter_handles_help_now
168
+ @around_block_called = false
169
+ @error_message = "OH NOES"
170
+ @app.around do |global_options, command, options, arguments, code|
171
+ @app.help_now! @error_message
172
+ code.call
173
+ end
174
+ exit_code = @app.run(['bs'])
175
+ assert exit_code != 0
176
+ assert_contained(@fake_stderr,/#{@error_message}/)
177
+ assert_contained(@fake_stdout,/SYNOPSIS/)
178
+ end
179
+
103
180
  def test_command_skips_pre
104
181
  @app.skips_pre
105
182
  @app.skips_post
@@ -17,10 +17,15 @@ class TC_testGLI < Clean::Test::TestCase
17
17
  include GLI
18
18
 
19
19
  def setup
20
+ @fake_stdout = FakeStdOut.new
21
+ @fake_stderr = FakeStdOut.new
22
+ @original_stdout = $stdout
23
+ $stdout = @fake_stdout
24
+ @original_stderr = $stderr
25
+ $stderr = @fake_stderr
20
26
  @app = CLIApp.new
21
27
  @config_file = File.expand_path(File.dirname(File.realpath(__FILE__)) + '/new_config.yaml')
22
28
  @gli_debug = ENV['GLI_DEBUG']
23
- @fake_stderr = FakeStdOut.new
24
29
  @app.error_device=@fake_stderr
25
30
  ENV.delete('GLI_DEBUG')
26
31
  end
@@ -29,6 +34,8 @@ class TC_testGLI < Clean::Test::TestCase
29
34
  File.delete(@config_file) if File.exist?(@config_file)
30
35
  ENV['GLI_DEBUG'] = @gli_debug
31
36
  @app.error_device=$stderr
37
+ $stdout = @original_stdout
38
+ $stderr = @original_stderr
32
39
  end
33
40
 
34
41
  def test_flag_create
@@ -51,6 +58,19 @@ class TC_testGLI < Clean::Test::TestCase
51
58
  assert @app.switches[:s].aliases.include? :'some-switch'
52
59
  end
53
60
 
61
+ def test_default_command
62
+ @app.reset
63
+ @called = false
64
+ @app.command :foo do |c|
65
+ c.action do |global, options, arguments|
66
+ @called = true
67
+ end
68
+ end
69
+ @app.default_command(:foo)
70
+ assert_equal 0, @app.run([]), "Expected exit status to be 0"
71
+ assert @called, "Expected default command to be executed"
72
+ end
73
+
54
74
  def test_flag_with_space_barfs
55
75
  @app.reset
56
76
  assert_raises(ArgumentError) { @app.flag ['some flag'] }
@@ -214,6 +234,18 @@ class TC_testGLI < Clean::Test::TestCase
214
234
  do_test_switch_create_twice(@app)
215
235
  do_test_switch_create_twice(Command.new(:names => :f))
216
236
  end
237
+
238
+ def test_non_negatable_negative_switch
239
+ @app.reset
240
+ @app.on_error { |ex| raise ex }
241
+ @app.switch 'no-color', :negatable => false
242
+ @app.command :smth do |c|
243
+ c.action do |global, *args|
244
+ assert global[:"no-color"], "Expected :'no-color' switch to be true"
245
+ end
246
+ end
247
+ @app.run(%w(--no-color smth))
248
+ end
217
249
 
218
250
  def test_all_aliases_in_options
219
251
  @app.reset
@@ -4,13 +4,25 @@ class TC_testSubCommand < Clean::Test::TestCase
4
4
  include TestHelper
5
5
 
6
6
  def setup
7
+ @fake_stdout = FakeStdOut.new
8
+ @fake_stderr = FakeStdOut.new
9
+
10
+ @original_stdout = $stdout
11
+ $stdout = @fake_stdout
12
+ @original_stderr = $stderr
13
+ $stderr = @fake_stderr
14
+
7
15
  @app = CLIApp.new
8
16
  @app.reset
9
- @fake_stderr = FakeStdOut.new
10
17
  @app.error_device=@fake_stderr
11
18
  ENV.delete('GLI_DEBUG')
12
19
  end
13
20
 
21
+ def teardown
22
+ $stdout = @original_stdout
23
+ $stderr = @original_stderr
24
+ end
25
+
14
26
  ['add','new'].each do |name|
15
27
  test_that "We run the 'add' subcommand using '#{name}'" do
16
28
  Given we_have_a_command_with_two_subcommands
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.rc3
4
+ version: 2.0.0.rc4
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-28 00:00:00.000000000 Z
12
+ date: 2012-06-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake