gli 2.0.0.rc5 → 2.0.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -9,3 +9,4 @@ cruddo.rdoc
9
9
  gli.wiki
10
10
  Gemfile.lock
11
11
  results.html
12
+ .rbx
@@ -1,9 +1,14 @@
1
+ notifications:
2
+ email:
3
+ on_success: always
1
4
  script: 'bundle exec rake test features'
2
5
  rvm:
3
6
  - 1.9.2
4
7
  - 1.9.3
5
8
  - 1.8.7
6
9
  - ree
10
+ - ruby-head
11
+ - rbx
7
12
  branches:
8
13
  only:
9
14
  - 'master'
@@ -103,3 +103,6 @@ License:: Distributes under the Apache License, see LICENSE.txt in the source di
103
103
  * [http://www.github.com/davetron5000/gli/wiki] - Documentation Wiki
104
104
  * [http://www.github.com/davetron5000/gli/wiki/Changelog] - Changelog
105
105
 
106
+ = <code>gli</code> CLI documentation
107
+
108
+ :include:gli.rdoc
data/bin/gli CHANGED
@@ -5,7 +5,7 @@ require 'gli/commands/scaffold'
5
5
 
6
6
  include GLI::App
7
7
 
8
- program_desc 'gli allows you to create the scaffolding for a GLI-powered application'
8
+ program_desc 'create scaffolding for a GLI-powered application'
9
9
 
10
10
  version GLI::VERSION
11
11
 
@@ -14,7 +14,12 @@ switch :v, :desc => 'Be verbose'
14
14
  switch :n, :desc => 'Dry run; don''t change the disk'
15
15
 
16
16
  desc 'Root dir of project'
17
- long_desc 'This is the directory where the project''s directory will be made, so if you specify a project name ''foo'' and the root dir of ''.'', the directory ''./foo'' will be created'
17
+ long_desc <<EOS
18
+ This is the directory where the project''s directory will be made, so if you
19
+ specify a project name ''foo'' and the root dir of ''.'', the directory
20
+ ''./foo'' will be created'
21
+ EOS
22
+
18
23
  flag :r,:root, :default_value => '.'
19
24
 
20
25
  desc 'Create a new GLI-based project'
@@ -13,7 +13,7 @@ Feature: The GLI executable works as intended
13
13
  And the output should contain:
14
14
  """
15
15
  NAME
16
- gli - gli allows you to create the scaffolding for a GLI-powered application
16
+ gli - create scaffolding for a GLI-powered application
17
17
 
18
18
  SYNOPSIS
19
19
  gli [global options] command [command options] [arguments...]
@@ -27,6 +27,7 @@ Feature: The GLI executable works as intended
27
27
  -n - Dry run; dont change the disk
28
28
  -r, --root=arg - Root dir of project (default: .)
29
29
  -v - Be verbose
30
+ --version -
30
31
 
31
32
  COMMANDS
32
33
  help - Shows a list of commands or help for one command
@@ -66,6 +66,7 @@ Feature: The scaffold GLI generates works
66
66
  the default)
67
67
  --help - Show this message
68
68
  -s, --[no-]switch - Describe some switch here
69
+ --version -
69
70
 
70
71
  COMMANDS
71
72
  add - Describe add here
@@ -91,6 +92,7 @@ Feature: The scaffold GLI generates works
91
92
  the default)
92
93
  --help - Show this message
93
94
  -s, --[no-]switch - Describe some switch here
95
+ --version -
94
96
 
95
97
  COMMANDS
96
98
  add - Describe add here
@@ -8,8 +8,8 @@ Feature: The todo app has a nice user interface
8
8
  And my terminal size is "80x24"
9
9
  And todo's bin directory is in my path
10
10
 
11
- Scenario: Getting Help for todo in general
12
- When I successfully run `todo help`
11
+ Scenario Outline: Getting Help for todo in general
12
+ When I successfully run `todo <help>`
13
13
  Then the output should contain:
14
14
  """
15
15
  NAME
@@ -26,6 +26,7 @@ Feature: The todo app has a nice user interface
26
26
  --help - Show this message
27
27
  --[no-]otherswitch -
28
28
  --[no-]switch -
29
+ --version -
29
30
 
30
31
  COMMANDS
31
32
  chained -
@@ -38,6 +39,10 @@ Feature: The todo app has a nice user interface
38
39
  ls - LS things, such as tasks or contexts
39
40
  second -
40
41
  """
42
+ Examples:
43
+ | help |
44
+ | help |
45
+ | --version |
41
46
 
42
47
  Scenario: Getting Help for a top level command of todo
43
48
  When I successfully run `todo help list`
@@ -169,6 +174,14 @@ Feature: The todo app has a nice user interface
169
174
  When I successfully run `todo --flag foo --switch --no-otherswitch initconfig`
170
175
  Then the config file should contain a section for each command and subcommand
171
176
 
177
+ Scenario: Init Config makes a reasonable config file if one is there and we force it
178
+ Given a clean home directory
179
+ And I successfully run `todo --flag foo --switch --no-otherswitch initconfig`
180
+ When I run `todo --flag foo --switch --no-otherswitch initconfig`
181
+ Then the exit status should not be 0
182
+ When I run `todo --flag foo --switch --no-otherswitch initconfig --force`
183
+ Then the exit status should be 0
184
+
172
185
  Scenario: Configuration percolates to the app
173
186
  Given a clean home directory
174
187
  And a config file that specifies defaults for some commands with subcommands
data/gli.rdoc CHANGED
@@ -1,51 +1,78 @@
1
- = <tt>gli</tt>
1
+ == gli - gli allows you to create the scaffolding for a GLI-powered application
2
2
 
3
- gli allows you to create the scaffolding for a GLI-powered application
4
- gli [global options] command_name [command-specific options] [--] arguments...
3
+ v2.0.0.rc5
5
4
 
6
- * Use the command +help+ to get a summary of commands
7
- * Use the command <tt>help command_name</tt> to get a help for +command_name+
8
- * Use <tt>--</tt> to stop command line argument processing; useful if your arguments have dashes in them
5
+ === Global Options
6
+ === -r arg
9
7
 
10
- == Global Options
11
- These options are available for any command and are specified before the name of the command
8
+ Root dir of project
12
9
 
13
- [<tt>-n</tt>] Dry run; dont change the disk
14
- [<tt>-r, --root=arg</tt>] Root dir of project <i>( default: <tt>.</tt>)</i>
10
+ [Aliases] --root
11
+ [Default Value] .
12
+ This is the directory where the projects directory will be made, so if you specify a project name foo and the root dir of ., the directory ./foo will be created
15
13
 
16
- This is the directory where the projects directory will be made, so if you specify a project name foo and the root dir of ., the directory ./foo will be created
14
+ === --help
15
+ Show this message
17
16
 
18
- [<tt>-v</tt>] Be verbose
19
- == Commands
20
- [<tt>help</tt>] Shows list of commands or help for one command
21
- [<tt>init</tt>] Create a new GLI-based project
22
17
 
23
- === <tt>help [command]</tt>
24
18
 
25
- Shows list of commands or help for one command
26
19
 
27
- Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
20
+ === -n
21
+ Dry run; dont change the disk
28
22
 
29
- ==== Options
30
- These options are specified *after* the command.
31
23
 
32
- [<tt>-c, --completion</tt>] List all commands one line at a time, for use with shell completion ([command] argument is partial command to match)
33
- === <tt>init project_name [command[ command]*]</tt>
34
24
 
25
+
26
+ === -v
27
+ Be verbose
28
+
29
+
30
+
31
+
32
+ === --version
33
+
34
+
35
+
36
+
37
+
38
+ === Commands
39
+ ==== help command
40
+ Shows a list of commands or help for one command
41
+
42
+
43
+ Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
44
+ ==== init project_name [command[ command]*]
35
45
  Create a new GLI-based project
36
46
 
37
- *Aliases*
38
- * <tt><b>scaffold</b></tt>
47
+ [Aliases] scaffold
39
48
 
40
49
  This will create a scaffold command line project that uses GLI
41
50
  for command line processing. Specifically, this will create
42
51
  an executable ready to go, as well as a lib and test directory, all
43
52
  inside the directory named for your project
53
+ ===== Options
54
+ ===== -e
55
+ Create an ext dir
56
+
57
+ [Aliases] --[no-]ext
58
+
59
+
60
+
61
+ ===== --[no-]force
62
+ Overwrite/ignore existing files and directories
63
+
64
+
65
+
66
+
67
+ ===== --notest
68
+ Do not create a test or features dir
69
+
70
+
71
+
72
+
73
+ ===== --[no-]rvmrc
74
+ Create an .rvmrc based on your current RVM setup
75
+
44
76
 
45
77
 
46
- ==== Options
47
- These options are specified *after* the command.
48
78
 
49
- [<tt>-e, --ext</tt>] Create an ext dir
50
- [<tt>--force</tt>] Overwrite/ignore existing files and directories
51
- [<tt>--notest</tt>] Do not create a test dir
data/lib/gli.rb CHANGED
@@ -16,6 +16,8 @@ require 'gli/version.rb'
16
16
  require 'gli/commands/help'
17
17
  require 'gli/commands/compound_command'
18
18
  require 'gli/commands/initconfig'
19
+ require 'gli/commands/rdoc_document_listener'
20
+ require 'gli/commands/doc'
19
21
 
20
22
  module GLI
21
23
  end
@@ -138,6 +138,7 @@ module GLI
138
138
  # +version+:: String containing the version of your application.
139
139
  def version(version)
140
140
  @version = version
141
+ switch :version, :negatable => false
141
142
  end
142
143
 
143
144
  # Call this with +true+ will cause the +global_options+ and
@@ -32,6 +32,11 @@ module GLI
32
32
  @version
33
33
  end
34
34
 
35
+ # Get the default command for the entire app
36
+ def get_default_command
37
+ @default_command
38
+ end
39
+
35
40
  # Runs whatever command is needed based on the arguments.
36
41
  #
37
42
  # +args+:: the command line ARGV array
@@ -113,7 +118,7 @@ module GLI
113
118
  end
114
119
 
115
120
  def commands # :nodoc:
116
- @commands ||= {:help => GLI::Commands::Help.new(self)}
121
+ @commands ||= { :help => GLI::Commands::Help.new(self), :_doc => GLI::Commands::Doc.new(self) }
117
122
  end
118
123
 
119
124
  def pre_block
@@ -13,11 +13,11 @@ module GLI
13
13
 
14
14
  check_for_unknown_commands!(base,command_names)
15
15
 
16
- @commands = command_names.map { |name| self.class.find_command(base,name) }
16
+ @wrapped_commands = command_names.map { |name| self.class.find_command(base,name) }
17
17
  end
18
18
 
19
19
  def execute(global_options,options,arguments) #:nodoc:
20
- @commands.each do |command|
20
+ @wrapped_commands.each do |command|
21
21
  command.execute(global_options,options,arguments)
22
22
  end
23
23
  end
@@ -0,0 +1,201 @@
1
+ module GLI
2
+ module Commands
3
+ # Takes a DocListener which will be called with all of the meta-data and documentation
4
+ # about your app, so as to create documentation in whatever format you want
5
+ class Doc < Command
6
+ FORMATS = {
7
+ 'rdoc' => GLI::Commands::RdocDocumentListener,
8
+ }
9
+ # Create the Doc generator based on the GLI app passed in
10
+ def initialize(app)
11
+ super(:names => "_doc",
12
+ :description => "Generate documentation of your application's UI",
13
+ :long_desc => "Introspects your application's UI meta-data to generate documentation in a variety of formats. This is intended to be extensible via the DocumentListener interface, so that you can provide your own documentation formats without them being a part of GLI",
14
+ :skips_pre => true, :skips_post => true, :skips_around => true, :hidden => true)
15
+
16
+ @app = app
17
+
18
+ desc 'The format name of the documentation to generate or the class name to use to generate it'
19
+ default_value 'rdoc'
20
+ arg_name 'name_or_class'
21
+ flag :format
22
+
23
+ action do |global_options,options,arguments|
24
+ self.document(format_class(options[:format]).new(global_options,options,arguments))
25
+ end
26
+ end
27
+
28
+ def nodoc
29
+ true
30
+ end
31
+
32
+ # Generates documentation using the listener
33
+ def document(document_listener)
34
+ document_listener.beginning
35
+ document_listener.program_desc(@app.program_desc)
36
+ document_listener.version(@app.version_string)
37
+ if any_options?(@app)
38
+ document_listener.options
39
+ end
40
+ document_flags_and_switches(document_listener,
41
+ @app.flags.values.sort(&by_name),
42
+ @app.switches.values.sort(&by_name))
43
+ if any_options?(@app)
44
+ document_listener.end_options
45
+ end
46
+ document_listener.commands
47
+ document_commands(document_listener,@app)
48
+ document_listener.end_commands
49
+ document_listener.ending
50
+ end
51
+
52
+ # Interface for a listener that is called during various parts of the doc process
53
+ class DocumentListener
54
+ # Called before processing begins
55
+ def beginning
56
+ abstract!
57
+ end
58
+
59
+ # Called when processing has completed
60
+ def ending
61
+ abstract!
62
+ end
63
+
64
+ # Gives you the program description
65
+ def program_desc(desc)
66
+ abstract!
67
+ end
68
+
69
+ # Gives you the program version
70
+ def version(version)
71
+ abstract!
72
+ end
73
+
74
+ # Called at the start of options for the current context
75
+ def options
76
+ abstract!
77
+ end
78
+
79
+ # Called when all options for the current context have been vended
80
+ def end_options
81
+ abstract!
82
+ end
83
+
84
+ # Called at the start of commands for the current context
85
+ def commands
86
+ abstract!
87
+ end
88
+
89
+ # Called when all commands for the current context have been vended
90
+ def end_commands
91
+ abstract!
92
+ end
93
+
94
+ # Gives you a flag in the current context
95
+ def flag(name,aliases,desc,long_desc,default_value,arg_name,must_match,type)
96
+ abstract!
97
+ end
98
+
99
+ # Gives you a switch in the current context
100
+ def switch(name,aliases,desc,long_desc,negetable)
101
+ abstract!
102
+ end
103
+
104
+ # Gives you the name of the current command in the current context
105
+ def default_command(name)
106
+ abstract!
107
+ end
108
+
109
+ # 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)
111
+ abstract!
112
+ end
113
+
114
+ # Ends a command, and "pops" you back up one context
115
+ def end_command(name)
116
+ abstract!
117
+ end
118
+
119
+ private
120
+ def abstract!
121
+ raise "Subclass must implement"
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def format_class(format_name)
128
+ FORMATS.fetch(format_name) {
129
+ begin
130
+ return format_name.split(/::/).reduce(Kernel) { |context,part| context.const_get(part) }
131
+ rescue => ex
132
+ raise IndexError,"Couldn't find formatter or class named #{format_name}"
133
+ end
134
+ }
135
+ end
136
+
137
+ def document_commands(document_listener,context)
138
+ 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)
144
+ document_listener.options if any_options?(command)
145
+ document_flags_and_switches(document_listener,command_flags(command),command_switches(command))
146
+ document_listener.end_options if any_options?(command)
147
+ document_listener.commands if any_commands?(command)
148
+ document_commands(document_listener,command)
149
+ document_listener.end_commands if any_commands?(command)
150
+ document_listener.end_command(command.name)
151
+ end
152
+ document_listener.default_command(context.get_default_command)
153
+ end
154
+
155
+ def by_name
156
+ lambda { |a,b| a.name.to_s <=> b.name.to_s }
157
+ end
158
+
159
+ def command_flags(command)
160
+ command.topmost_ancestor.flags.values.select { |flag| flag.associated_command == command }.sort(&by_name)
161
+ end
162
+
163
+ def command_switches(command)
164
+ command.topmost_ancestor.switches.values.select { |switch| switch.associated_command == command }.sort(&by_name)
165
+ end
166
+
167
+ def document_flags_and_switches(document_listener,flags,switches)
168
+ flags.each do |flag|
169
+ document_listener.flag(flag.name,
170
+ Array(flag.aliases),
171
+ flag.description,
172
+ flag.long_description,
173
+ flag.default_value,
174
+ flag.argument_name,
175
+ flag.must_match,
176
+ flag.type)
177
+ end
178
+ switches.each do |switch|
179
+ document_listener.switch(switch.name,
180
+ Array(switch.aliases),
181
+ switch.description,
182
+ switch.long_description,
183
+ switch.negatable)
184
+ end
185
+ end
186
+
187
+ def any_options?(context)
188
+ options = if context.kind_of?(Command)
189
+ command_flags(context) + command_switches(context)
190
+ else
191
+ context.flags.values + context.switches.values
192
+ end
193
+ !options.empty?
194
+ end
195
+
196
+ def any_commands?(command)
197
+ !command.commands.empty?
198
+ end
199
+ end
200
+ end
201
+ end