gli 2.1.0 → 2.2.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.
@@ -88,5 +88,3 @@ Feature: The GLI executable works as intended
88
88
  Given the file "gli.rdoc" doesn't exist
89
89
  When I run `gli _doc`
90
90
  Then a file named "gli.rdoc" should exist
91
-
92
-
@@ -2,6 +2,17 @@ Given /^todo's bin directory is in my path/ do
2
2
  add_to_path(File.expand_path(File.join(File.dirname(__FILE__),'..','..','test','apps','todo','bin')))
3
3
  end
4
4
 
5
+ Given /^the todo app is coded to avoid sorted help commands$/ do
6
+ ENV['TODO_SORT_HELP'] = 'manually'
7
+ end
8
+
9
+ Given /^the todo app is coded to avoid wrapping text$/ do
10
+ ENV['TODO_WRAP_HELP_TEXT'] = 'never'
11
+ end
12
+
13
+ Given /^the todo app is coded to wrap text only for tty$/ do
14
+ ENV['TODO_WRAP_HELP_TEXT'] = 'tty_only'
15
+ end
5
16
 
6
17
  Given /^a clean home directory$/ do
7
18
  FileUtils.rm_rf File.join(ENV['HOME'],'gli_test_todo.rc')
@@ -24,6 +24,7 @@ Before do
24
24
  FileUtils.rm_rf new_home
25
25
  FileUtils.mkdir new_home
26
26
  ENV['HOME'] = new_home
27
+ FileUtils.cp 'gli.rdoc','gli.rdoc.orig'
27
28
  end
28
29
 
29
30
  After do |scenario|
@@ -34,6 +35,9 @@ After do |scenario|
34
35
  end
35
36
  ENV['PATH'] = @original_path.join(File::PATH_SEPARATOR)
36
37
  ENV['HOME'] = @original_home
38
+ ENV['TODO_SORT_HELP'] = nil
39
+ ENV['TODO_WRAP_HELP_TEXT'] = nil
40
+ FileUtils.mv 'gli.rdoc.orig','gli.rdoc'
37
41
  end
38
42
 
39
43
  def add_to_path(dir)
@@ -15,6 +15,9 @@ Feature: The todo app has a nice user interface
15
15
  NAME
16
16
  todo - Manages tasks
17
17
 
18
+ A test program that has a sophisticated UI that can be used to exercise a
19
+ lot of GLI's power
20
+
18
21
  SYNOPSIS
19
22
  todo [global options] command [command options] [arguments...]
20
23
 
@@ -85,8 +88,44 @@ Feature: The todo app has a nice user interface
85
88
  contexts
86
89
  """
87
90
 
88
- Scenario: Getting Help for a top level command of todo
89
- When I successfully run `todo help list`
91
+ Scenario: Getting Help with self-ordered commands
92
+ Given the todo app is coded to avoid sorted help commands
93
+ When I successfully run `todo help`
94
+ Then the output should contain:
95
+ """
96
+ NAME
97
+ todo - Manages tasks
98
+
99
+ A test program that has a sophisticated UI that can be used to exercise a
100
+ lot of GLI's power
101
+
102
+ SYNOPSIS
103
+ todo [global options] command [command options] [arguments...]
104
+
105
+ VERSION
106
+ 0.0.1
107
+
108
+ GLOBAL OPTIONS
109
+ --flag=arg - (default: none)
110
+ --help - Show this message
111
+ --[no-]otherswitch -
112
+ --[no-]switch -
113
+ --version -
114
+
115
+ COMMANDS
116
+ help - Shows a list of commands or help for one command
117
+ initconfig - Initialize the config file using current global options
118
+ create, new - Create a new task or context
119
+ list - List things, such as tasks or contexts
120
+ ls - LS things, such as tasks or contexts
121
+ first -
122
+ second -
123
+ chained -
124
+ chained2, ch2 -
125
+ """
126
+
127
+ Scenario Outline: Getting Help for a top level command of todo
128
+ When I successfully run `todo <help_invocation>`
90
129
  Then the output should contain:
91
130
  """
92
131
  NAME
@@ -111,6 +150,59 @@ Feature: The todo app has a nice user interface
111
150
  tasks - List tasks (default)
112
151
  """
113
152
 
153
+ Examples:
154
+ | help_invocation |
155
+ | help list |
156
+ | list -h |
157
+ | list --help |
158
+
159
+
160
+ Scenario: Getting Help without wrapping
161
+ Given the todo app is coded to avoid wrapping text
162
+ When I successfully run `todo help list`
163
+ Then the output should contain:
164
+ """
165
+ NAME
166
+ list - List things, such as tasks or contexts
167
+
168
+ SYNOPSIS
169
+ todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
170
+ todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
171
+
172
+ DESCRIPTION
173
+ List a whole lot of things that you might be keeping track of in your overall todo list. This is your go-to place or finding all of the things that you might have stored in your todo databases.
174
+
175
+ COMMAND OPTIONS
176
+ -l, --[no-]long - Show long form
177
+
178
+ COMMANDS
179
+ contexts - List contexts
180
+ tasks - List tasks (default)
181
+ """
182
+
183
+ Scenario: Getting Help without wrapping
184
+ Given the todo app is coded to wrap text only for tty
185
+ When I successfully run `todo help list`
186
+ Then the output should contain:
187
+ """
188
+ NAME
189
+ list - List things, such as tasks or contexts
190
+
191
+ SYNOPSIS
192
+ todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
193
+ todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
194
+
195
+ DESCRIPTION
196
+ List a whole lot of things that you might be keeping track of in your overall todo list. This is your go-to place or finding all of the things that you might have stored in your todo databases.
197
+
198
+ COMMAND OPTIONS
199
+ -l, --[no-]long - Show long form
200
+
201
+ COMMANDS
202
+ contexts - List contexts
203
+ tasks - List tasks (default)
204
+ """
205
+
114
206
  Scenario: Getting Help for a sub command of todo list
115
207
  When I successfully run `todo help list tasks`
116
208
  Then the output should contain:
data/gli.gemspec CHANGED
@@ -29,5 +29,6 @@ spec = Gem::Specification.new do |s|
29
29
  s.add_development_dependency('clean_test')
30
30
  s.add_development_dependency('aruba')
31
31
  s.add_development_dependency('sdoc')
32
+ s.add_development_dependency('faker','1.0.0')
32
33
  end
33
34
 
data/gli.rdoc CHANGED
@@ -1,13 +1,12 @@
1
1
  == gli - create scaffolding for a GLI-powered application
2
2
 
3
- v2.0.0.rc8
3
+ v2.1.0
4
4
 
5
5
  === Global Options
6
- === -r arg
6
+ === -r|--root arg
7
7
 
8
8
  Root dir of project
9
9
 
10
- [Aliases] --root
11
10
  [Default Value] .
12
11
  This is the directory where the project''s directory will be made, so if you
13
12
  specify a project name ''foo'' and the root dir of ''.'', the directory
@@ -18,46 +17,43 @@ Show this message
18
17
 
19
18
 
20
19
 
21
-
22
20
  === -n
23
21
  Dry run; dont change the disk
24
22
 
25
23
 
26
24
 
27
-
28
25
  === -v
29
26
  Be verbose
30
27
 
31
28
 
32
29
 
33
-
34
30
  === --version
35
31
 
36
32
 
37
33
 
38
34
 
39
-
40
35
  === Commands
41
- ==== help command
36
+ ==== Command: <tt>help command</tt>
42
37
  Shows a list of commands or help for one command
43
38
 
44
-
45
39
  Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
46
- ==== init project_name [command[ command]*]
47
- Create a new GLI-based project
40
+ ===== Options
41
+ ===== -c
42
+ List commands one per line, to assist with shell completion
48
43
 
49
- [Aliases] scaffold
44
+
45
+
46
+ ==== Command: <tt>init|scaffold project_name [command[ command]*]</tt>
47
+ Create a new GLI-based project
50
48
 
51
49
  This will create a scaffold command line project that uses GLI
52
50
  for command line processing. Specifically, this will create
53
51
  an executable ready to go, as well as a lib and test directory, all
54
52
  inside the directory named for your project
55
53
  ===== Options
56
- ===== -e
54
+ ===== -e|--[no-]ext
57
55
  Create an ext dir
58
56
 
59
- [Aliases] --[no-]ext
60
-
61
57
 
62
58
 
63
59
  ===== --[no-]force
@@ -65,16 +61,13 @@ Overwrite/ignore existing files and directories
65
61
 
66
62
 
67
63
 
68
-
69
64
  ===== --notest
70
65
  Do not create a test or features dir
71
66
 
72
67
 
73
68
 
74
-
75
69
  ===== --[no-]rvmrc
76
70
  Create an .rvmrc based on your current RVM setup
77
71
 
78
72
 
79
73
 
80
-
data/lib/gli/app.rb CHANGED
@@ -23,7 +23,7 @@ module GLI
23
23
  $LOAD_PATH.each do |load_path|
24
24
  commands_path = File.join(load_path,path)
25
25
  if File.exists? commands_path
26
- Dir.entries(commands_path).each do |entry|
26
+ Dir.entries(commands_path).sort.each do |entry|
27
27
  file = File.join(commands_path,entry)
28
28
  if file =~ /\.rb$/
29
29
  require file
@@ -44,6 +44,17 @@ module GLI
44
44
  @program_desc
45
45
  end
46
46
 
47
+ # Provide a longer description of the program. This can be as long as needed, and use double-newlines
48
+ # for paragraphs. This will show up in the help output.
49
+ #
50
+ # description:: A String for the description
51
+ def program_long_desc(description=nil)
52
+ if description
53
+ @program_long_desc = description
54
+ end
55
+ @program_long_desc
56
+ end
57
+
47
58
  # Use this if the following command should not have the pre block executed.
48
59
  # By default, the pre block is executed before each command and can result in
49
60
  # aborting the call. Using this will avoid that behavior for the following command
@@ -77,6 +88,7 @@ module GLI
77
88
  @config_file = File.join(File.expand_path(ENV['HOME']),filename)
78
89
  end
79
90
  commands[:initconfig] = InitConfig.new(@config_file,commands,flags,switches)
91
+ @commands_declaration_order << commands[:initconfig]
80
92
  @config_file
81
93
  end
82
94
 
@@ -195,8 +207,8 @@ module GLI
195
207
 
196
208
  # Exit now, showing the user help for the command they executed. Use #exit_now! to just show the error message
197
209
  #
198
- # message:: message to indicate how the user has messed up the CLI invocation
199
- def help_now!(message)
210
+ # message:: message to indicate how the user has messed up the CLI invocation or nil to just simply show help
211
+ def help_now!(message=nil)
200
212
  exception = OptionParser::ParseError.new(message)
201
213
  class << exception
202
214
  def exit_code; 64; end
@@ -204,6 +216,25 @@ module GLI
204
216
  raise exception
205
217
  end
206
218
 
219
+ # Control how help commands are sorted. By default, the commands are sorted alphabetically.
220
+ #
221
+ # sort_type:: How you want help commands sorted:
222
+ # +:manually+:: help commands are ordered in the order declared.
223
+ # +:alpha+:: sort alphabetically (default)
224
+ def sort_help(sort_type)
225
+ @help_sort_type = sort_type
226
+ end
227
+
228
+ # Set how help text is wrapped.
229
+ #
230
+ # wrap_type:: Symbol indicating how you'd like text wrapped:
231
+ # +:to_terminal+:: Wrap text based on the width of the terminal (default)
232
+ # +:never+:: Do not wrap text at all. This will bring all help content onto one line, removing any newlines
233
+ # +:tty_only+:: Wrap like +:to_terminal+ if this output is going to a TTY, otherwise don't wrap (like +:never+)
234
+ def wrap_help_text(wrap_type)
235
+ @help_text_wrap_type = wrap_type
236
+ end
237
+
207
238
  def program_name(override=nil) #:nodoc:
208
239
  warn "#program_name has been deprecated"
209
240
  end
@@ -15,6 +15,7 @@ module GLI
15
15
  switches.clear
16
16
  flags.clear
17
17
  @commands = nil
18
+ @commands_declaration_order = []
18
19
  @version = nil
19
20
  @config_file = nil
20
21
  @use_openstruct = false
@@ -27,8 +28,13 @@ module GLI
27
28
  clear_nexts
28
29
  end
29
30
 
31
+ # Get an array of commands, ordered by when they were declared
32
+ def commands_declaration_order # :nodoc:
33
+ @commands_declaration_order
34
+ end
35
+
30
36
  # Get the version string
31
- def version_string #:nodoc
37
+ def version_string #:nodoc:
32
38
  @version
33
39
  end
34
40
 
@@ -119,7 +125,13 @@ module GLI
119
125
  end
120
126
 
121
127
  def commands # :nodoc:
122
- @commands ||= { :help => GLI::Commands::Help.new(self), :_doc => GLI::Commands::Doc.new(self) }
128
+ if !@commands
129
+ @commands = { :help => GLI::Commands::Help.new(self), :_doc => GLI::Commands::Doc.new(self) }
130
+ @commands_declaration_order ||= []
131
+ @commands_declaration_order << @commands[:help]
132
+ @commands_declaration_order << @commands[:_doc]
133
+ end
134
+ @commands
123
135
  end
124
136
 
125
137
  def pre_block
@@ -137,6 +149,14 @@ module GLI
137
149
  @around_blocks || []
138
150
  end
139
151
 
152
+ def help_sort_type
153
+ @help_sort_type || :alpha
154
+ end
155
+
156
+ def help_text_wrap_type
157
+ @help_text_wrap_type || :to_terminal
158
+ end
159
+
140
160
  # Sets the default values for flags based on the configuration
141
161
  def override_defaults_based_on_config(config)
142
162
  override_default(flags,config)
@@ -167,9 +187,8 @@ module GLI
167
187
 
168
188
  def handle_exception(ex,command)
169
189
  if regular_error_handling?(ex)
170
- stderr.puts error_message(ex)
190
+ output_error_message(ex)
171
191
  if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
172
- stderr.puts
173
192
  commands[:help] and commands[:help].execute({},{},command.nil? ? [] : [command.name.to_s])
174
193
  end
175
194
  end
@@ -180,6 +199,17 @@ module GLI
180
199
  ex.exit_code
181
200
  end
182
201
 
202
+ def output_error_message(ex)
203
+ stderr.puts error_message(ex) unless no_message_given?(ex)
204
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
205
+ stderr.puts unless no_message_given?(ex)
206
+ end
207
+ end
208
+
209
+ def no_message_given?(ex)
210
+ ex.message == ex.class.name
211
+ end
212
+
183
213
  # Possibly returns a copy of the passed-in Hash as an instance of GLI::Option.
184
214
  # By default, it will *not*. However by putting <tt>use_openstruct true</tt>
185
215
  # in your CLI definition, it will
data/lib/gli/command.rb CHANGED
@@ -50,6 +50,7 @@ module GLI
50
50
  @skips_pre = options[:skips_pre]
51
51
  @skips_post = options[:skips_post]
52
52
  @skips_around = options[:skips_around]
53
+ @commands_declaration_order = []
53
54
  clear_nexts
54
55
  end
55
56
 
@@ -132,6 +133,13 @@ module GLI
132
133
  @default_desc = desc
133
134
  end
134
135
 
136
+ # Returns true if this command has the given option defined
137
+ def has_option?(option) #:nodoc:
138
+ option = option.gsub(/^\-+/,'')
139
+ ((flags.values.map { |_| [_.name,_.aliases] }) +
140
+ (switches.values.map { |_| [_.name,_.aliases] })).flatten.map(&:to_s).include?(option)
141
+ end
142
+
135
143
  def self.name_as_string(name,negatable=false) #:nodoc:
136
144
  name.to_s
137
145
  end
@@ -44,6 +44,11 @@ module GLI
44
44
  all_forms
45
45
  end
46
46
 
47
+ # Get an array of commands, ordered by when they were declared
48
+ def commands_declaration_order # :nodoc:
49
+ @commands_declaration_order
50
+ end
51
+
47
52
  def flag(*names)
48
53
  new_flag = if parent.kind_of? Command
49
54
  parent.flag(*names)
@@ -32,7 +32,8 @@ module GLI
32
32
  # Generates documentation using the listener
33
33
  def document(document_listener)
34
34
  document_listener.beginning
35
- document_listener.program_desc(@app.program_desc)
35
+ document_listener.program_desc(@app.program_desc) unless @app.program_desc.nil?
36
+ document_listener.program_long_desc(@app.program_long_desc) unless @app.program_long_desc.nil?
36
37
  document_listener.version(@app.version_string)
37
38
  if any_options?(@app)
38
39
  document_listener.options
@@ -66,6 +67,11 @@ module GLI
66
67
  abstract!
67
68
  end
68
69
 
70
+ # Gives you the program long description
71
+ def program_long_desc(desc)
72
+ abstract!
73
+ end
74
+
69
75
  # Gives you the program version
70
76
  def version(version)
71
77
  abstract!
@@ -107,7 +113,7 @@ module GLI
107
113
  end
108
114
 
109
115
  # Gives you a command in the current context and creates a new context of this command
110
- def command(name,aliases,desc,long_desc,arg_name)
116
+ def command(name,aliases,desc,long_desc,arg_name,arg_options)
111
117
  abstract!
112
118
  end
113
119
 
@@ -136,11 +142,7 @@ module GLI
136
142
 
137
143
  def document_commands(document_listener,context)
138
144
  context.commands.values.reject {|_| _.nodoc }.sort(&by_name).each do |command|
139
- document_listener.command(command.name,
140
- Array(command.aliases),
141
- command.description,
142
- command.long_description,
143
- command.arguments_description)
145
+ call_command_method_being_backwards_compatible(document_listener,command)
144
146
  document_listener.options if any_options?(command)
145
147
  document_flags_and_switches(document_listener,command_flags(command),command_switches(command))
146
148
  document_listener.end_options if any_options?(command)
@@ -152,6 +154,18 @@ module GLI
152
154
  document_listener.default_command(context.get_default_command)
153
155
  end
154
156
 
157
+ def call_command_method_being_backwards_compatible(document_listener,command)
158
+ command_args = [command.name,
159
+ Array(command.aliases),
160
+ command.description,
161
+ command.long_description,
162
+ command.arguments_description]
163
+ if document_listener.method(:command).arity == 6
164
+ command_args << command.arguments_options
165
+ end
166
+ document_listener.command(*command_args)
167
+ end
168
+
155
169
  def by_name
156
170
  lambda { |a,b| a.name.to_s <=> b.name.to_s }
157
171
  end
@@ -170,7 +184,7 @@ module GLI
170
184
  Array(flag.aliases),
171
185
  flag.description,
172
186
  flag.long_description,
173
- flag.default_value,
187
+ flag.safe_default_value,
174
188
  flag.argument_name,
175
189
  flag.must_match,
176
190
  flag.type)