choosy 0.4.9 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/Gemfile.lock +13 -1
  2. data/README.md +259 -0
  3. data/Rakefile +28 -17
  4. data/lib/VERSION.yml +4 -4
  5. data/lib/choosy.rb +38 -10
  6. data/lib/choosy/argument.rb +0 -2
  7. data/lib/choosy/base_command.rb +9 -8
  8. data/lib/choosy/command.rb +0 -6
  9. data/lib/choosy/converter.rb +0 -5
  10. data/lib/choosy/dsl/argument_builder.rb +0 -5
  11. data/lib/choosy/dsl/base_builder.rb +0 -2
  12. data/lib/choosy/dsl/base_command_builder.rb +54 -42
  13. data/lib/choosy/dsl/command_builder.rb +0 -6
  14. data/lib/choosy/dsl/option_builder.rb +4 -6
  15. data/lib/choosy/dsl/super_command_builder.rb +0 -4
  16. data/lib/choosy/errors.rb +10 -2
  17. data/lib/choosy/option.rb +0 -3
  18. data/lib/choosy/parse_result.rb +0 -2
  19. data/lib/choosy/parser.rb +0 -4
  20. data/lib/choosy/printing/base_printer.rb +0 -3
  21. data/lib/choosy/printing/color.rb +21 -33
  22. data/lib/choosy/printing/erb_printer.rb +0 -2
  23. data/lib/choosy/printing/formatting_element.rb +0 -2
  24. data/lib/choosy/printing/help_printer.rb +0 -3
  25. data/lib/choosy/printing/manpage.rb +5 -2
  26. data/lib/choosy/printing/manpage_printer.rb +1 -6
  27. data/lib/choosy/rake.rb +33 -31
  28. data/lib/choosy/super_command.rb +1 -7
  29. data/lib/choosy/super_parser.rb +0 -6
  30. data/lib/choosy/terminal.rb +0 -3
  31. data/lib/choosy/verifier.rb +0 -3
  32. data/lib/choosy/version.rb +0 -2
  33. data/spec/choosy/argument_spec.rb +0 -3
  34. data/spec/choosy/base_command_spec.rb +0 -3
  35. data/spec/choosy/command_spec.rb +0 -4
  36. data/spec/choosy/converter_spec.rb +0 -4
  37. data/spec/choosy/dsl/argument_builder_spec.rb +0 -3
  38. data/spec/choosy/dsl/base_builder_spec.rb +0 -3
  39. data/spec/choosy/dsl/base_command_builder_spec.rb +18 -5
  40. data/spec/choosy/dsl/commmand_builder_spec.rb +0 -6
  41. data/spec/choosy/dsl/option_builder_spec.rb +0 -3
  42. data/spec/choosy/dsl/super_command_builder_spec.rb +0 -4
  43. data/spec/choosy/option_spec.rb +0 -3
  44. data/spec/choosy/parser_spec.rb +0 -5
  45. data/spec/choosy/printing/base_printer_spec.rb +0 -5
  46. data/spec/choosy/printing/color_spec.rb +1 -4
  47. data/spec/choosy/printing/help_printer_spec.rb +0 -5
  48. data/spec/choosy/printing/manpage_printer_spec.rb +0 -4
  49. data/spec/choosy/printing/manpage_spec.rb +2 -3
  50. data/spec/choosy/super_command_spec.rb +0 -3
  51. data/spec/choosy/super_parser_spec.rb +0 -5
  52. data/spec/choosy/terminal_spec.rb +0 -3
  53. data/spec/choosy/verifier_spec.rb +0 -7
  54. data/spec/choosy/version_spec.rb +0 -2
  55. metadata +127 -121
  56. data/README.markdown +0 -463
  57. data/lib/choosy/printing.rb +0 -1
  58. data/spec/integration/command-A_spec.rb +0 -37
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- choosy (0.4.8)
4
+ choosy (0.4.9)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -11,7 +11,17 @@ GEM
11
11
  ZenTest (>= 4.4.1)
12
12
  autotest-notification (2.3.1)
13
13
  autotest (~> 4.3)
14
+ builder (3.0.0)
15
+ cucumber (1.1.9)
16
+ builder (>= 2.1.2)
17
+ diff-lcs (>= 1.1.2)
18
+ gherkin (~> 2.9.0)
19
+ json (>= 1.4.6)
20
+ term-ansicolor (>= 1.0.6)
14
21
  diff-lcs (1.1.2)
22
+ gherkin (2.9.3)
23
+ json (>= 1.4.6)
24
+ json (1.6.6)
15
25
  rspec (2.5.0)
16
26
  rspec-core (~> 2.5.0)
17
27
  rspec-expectations (~> 2.5.0)
@@ -20,6 +30,7 @@ GEM
20
30
  rspec-expectations (2.5.0)
21
31
  diff-lcs (~> 1.1.2)
22
32
  rspec-mocks (2.5.0)
33
+ term-ansicolor (1.0.7)
23
34
 
24
35
  PLATFORMS
25
36
  ruby
@@ -29,4 +40,5 @@ DEPENDENCIES
29
40
  autotest
30
41
  autotest-notification
31
42
  choosy!
43
+ cucumber
32
44
  rspec
@@ -0,0 +1,259 @@
1
+ # Choosy: Picking your Arguments Carefully
2
+
3
+ This is a small DSL library for creating command line clients in Ruby. It is largely inspired by the <a href="https://github.com/defunkt/choice">choice</a>, <a href="https://github.com/visionmedia/commander">commander</a>, and <a href="http://furius.ca/optcomplete/">optcomplete.py</a> libraries, though it makes some different design decisions than they do. It is opinionated software.
4
+
5
+ This library should:
6
+
7
+ - Make creating command line clients relatively easy.
8
+ - Make creating supercommands like git, subversion, and gem easier.
9
+ - Allow you to add validation logic for your arguments within the parsing phase.
10
+ - Allowing for dependencies between options, so that you can more easily validate related options (i.e. if the<code>--bold</code> flag requires the <code>--font Arial</code> flag, then you should be able to ask for the <code>--font</code> option to be validated first, and then the <code>--bold</code> option.
11
+ - Allow you to customize its output using your own formatting system, or provide several convenient defaults when you don't want to provide your own.
12
+
13
+ This library should never:
14
+
15
+ - Interact with your execution logic. You can attach executors to commands for convenience, but the execution phase should be delegated to you, not the parsing library. Separation of concerns, people.
16
+ - Rely on display or user interface libraries like Highline, since this is only for parsing command lines.
17
+ - Pollute your namespaces with my DSL function names. (I really, really hate it when libraries do this.)
18
+
19
+ # Examples
20
+
21
+ This example is a simple date and time tool.
22
+
23
+ ```ruby
24
+ #!/usr/bin/env ruby
25
+
26
+ # date-time
27
+ require 'choosy'
28
+ require 'time'
29
+
30
+ class DateTimeCLI
31
+ attr_reader :command
32
+
33
+ def run!(args)
34
+ @command = Choosy::Command.new :'date-time' do
35
+ summary "This is a command that prints out the current time"
36
+
37
+ executor do |args, options| # An executor can just be a simple block.
38
+ if options[:utc]
39
+ puts Time.now.utc.strftime(options[:format])
40
+ else
41
+ puts Time.now.strftime(options[:format])
42
+ end
43
+ end
44
+
45
+ section "Description" do
46
+ para "This tool prints out the current time, with some added effects"
47
+ end
48
+
49
+ section "Options" do
50
+ boolean :utc, "Prints it using the UTC format"
51
+ string :format, "The format of the output" do
52
+ default "%Y-%m-%d %H:%M:%S %z"
53
+ end
54
+ end
55
+
56
+ help # Enables the '-h|--help' option.
57
+ version "1.0" # Enables the '--version' option.
58
+ end.execute!(args)
59
+ end
60
+ end
61
+
62
+ if __FILE__ == $0
63
+ DateTimeCLI.new.run!(ARGV)
64
+ end
65
+ ```
66
+
67
+ Another simple tool that lists directories.
68
+
69
+ ```ruby
70
+ #!/usr/bin/env ruby
71
+
72
+ # list-files
73
+ require 'choosy'
74
+
75
+ class ListFilesCLI
76
+ attr_reader :command
77
+
78
+ def run!(args)
79
+ exe = self # The following block will change 'self', so we capture context here.
80
+
81
+ @command = Choosy::Command.new :'date-time' do
82
+ summary "This is a command that lists files in directories"
83
+ executor exe # Commands need executors that implement 'execute!'.
84
+ # We're using 'self'.
85
+
86
+ section "Description" do
87
+ para "This tool prints out some information about directories"
88
+ end
89
+
90
+ section "Options" do
91
+ integer :limit, "Limit the printed listing to a given number of files."
92
+ boolean :dirs_only, "Only print out directories"
93
+ end
94
+
95
+ help # Enables the '-h|--help' option.
96
+ version "1.0" # Enables the '--version' option.
97
+
98
+ arguments do
99
+ metaname 'PATH' # What to display on the manpage
100
+ count 0..1 # The number of items allowed, restricted here by a range.
101
+ end
102
+ end.execute!(args)
103
+ end
104
+
105
+ # Called by the command at the end of the 'run!'
106
+ def execute!(args, options)
107
+ limit = options[:limit] || 0
108
+ paths = if args.empty?
109
+ ['.']
110
+ else
111
+ args
112
+ end
113
+
114
+ paths.each do |path|
115
+ Dir["#{path}/*"].each do |fname|
116
+ puts fname
117
+
118
+ limit -= 1
119
+ if limit == 0
120
+ return
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ if __FILE__ == $0
128
+ ListFilesCLI.new.run!(ARGV)
129
+ end
130
+ ```
131
+
132
+ ### Super Commands
133
+
134
+ You can also combine multiple choices into an uber-choice, creating commands that look a lot like git or subversion.
135
+
136
+ ```ruby
137
+ #!/usr/bin/env ruby
138
+
139
+ # git-wrapper
140
+ require 'choosy'
141
+
142
+ class GitExecutor
143
+ def initialize(type)
144
+ @type = type
145
+ end
146
+
147
+ def execute!(args, options)
148
+ exec("git #{@type}")
149
+ end
150
+ end
151
+
152
+ class GitWrapperCLI
153
+ attr_reader :command
154
+
155
+ def run!(args)
156
+ exe = self # The following block will change 'self', so we capture context here.
157
+
158
+ @command = Choosy::SuperCommand.new :'git-wrapper' do
159
+ summary "This is a command that lists files in directories"
160
+
161
+ section "Description" do
162
+ para "This tool prints out some information about directories"
163
+ end
164
+
165
+ section "Commands" do
166
+ command :status do
167
+ summary "Prints out the 'git status'"
168
+ executor GitExecutor.new(:status)
169
+
170
+ help
171
+ end
172
+
173
+ command :diff do
174
+ summary "Prints out the 'git diff'"
175
+ executor GitExecutor.new(:diff)
176
+
177
+ help
178
+ end
179
+ end
180
+
181
+ help # Enables the '-h|--help' option.
182
+ version "1.0" # Enables the '--version' option.
183
+ end.execute!(args)
184
+ end
185
+ end
186
+
187
+ if __FILE__ == $0
188
+ GitWrapperCLI.new.run!(ARGV)
189
+ end
190
+ ```
191
+
192
+ # Output Printing
193
+
194
+ Choosy allows you to customize the output printing of your documentation. It exposes the internal object model to any class that implements a <code>print!(command)</code> method.
195
+
196
+ The <code>:standard</code> printer that is the default for any command can also be customized to meet some of your needs:
197
+
198
+ ``` ruby
199
+ Choosy::Command.new :foo do
200
+ printer :standard, # The default printer
201
+ :max_width => 80, # Defaults to the column witdh of the terminal
202
+ :color => true, # Default is true
203
+ :header_styles => [:bold, :green], # Defaults to [:bold, :blue]
204
+ :indent => ' ', # Defaults to this width
205
+ :offset => ' ' # Defaults to this width
206
+
207
+ help "Show this help command."
208
+ end
209
+ ```
210
+
211
+ This above example sets some useful properties for the printer. First, the <code>:max\_width</code> limits the wrapping size on the console. By default, choosy tries to be smart and wrap to the currend column width, but you can introduce this hash parameter as a default max. Next, you can turn off and on color printing by setting <code>:color</code>. Color is on by default, so it's actually superfluous in this example -- just a demonstration of the syntax. The <code>:header\_styles</code> is an array of styles that will be applied to the headers for this document. By default, headers are <code>[:bold, :blue]</code>. Most of the ANSI colors and codes are supported, but check <code>lib/choosy/printing/color.rb</code> for additional options. The last two options given are actually formatting spacers in the output that you can customize: <code>:indent</code> is the default indent for commands and options; <code>:offset</code> is the distance between the options and commands to their associated descriptive text.
212
+
213
+ For those who want the nice, manpage experience, there's also the <code>:manpage</code> printer:
214
+
215
+ ``` ruby
216
+ Choosy::Command.new :foo do
217
+ printer :manpage,
218
+ :max_width => 80, # Same as :standard
219
+ :color => true, # Same as :standard
220
+ :header_styles => [:bold, :green], # Same as :standard
221
+ :option_sytles => [:bold], # Same as :standard
222
+ :indent => ' ', # Same as :standard
223
+ :offset => ' ', # Same as :standard
224
+ :version => FOO_VERSION, # Will use the version name you specify, see below.
225
+ :section => 1, # Default is always '1'
226
+ :date => '03/24/2011', # Date you want displayed
227
+ :manual => 'Foo Co.' # The manual page group
228
+
229
+ version FOO_VERSION # If you don't supply a version above, this will be used
230
+ end
231
+ ```
232
+
233
+ Because the library is super-awesome, the manpage will even be in color when piped to <code>less -R</code> (the default)! If you don't like the format of my manpage, feel free to implement your own using the <code>choosy/printing/manpage</code> class, a useful utility class for formatting manpage output correctly.
234
+
235
+ If you already have some templates that you'd like to use, there is also the <code>:erb</code> template that can be customized by writing a template of your choice:
236
+
237
+ ``` ruby
238
+ Choosy::Command.new :foo do
239
+ printer :erb,
240
+ :color => true, # Defaults to true
241
+ :template => 'path/to/file.erb' # Required
242
+ end
243
+ ```
244
+
245
+ The ERB printer also accepts the <code>:color</code> option. The color is exposed via a <code>color</code> property in the template; the command is exposed by the <code>command</code> property.
246
+
247
+ Finally, because I don't want to tell you how to print your help, I also give you the option of supplying your own printer. Just create a class with a <code>print!(command)</code> method on that class, and it will be passed in the command that it should print the help for. I have supplied some code you may find useful in <code>choosy/terminal</code> that will help with things like finding commands and determining the column width of the terminal.
248
+
249
+ ``` ruby
250
+ class CustomPrinter
251
+ def print!(command)
252
+ puts "I got called on help for #{command.name}"
253
+ end
254
+ end
255
+
256
+ Choosy::Command.new :foo do
257
+ printer CustomPrinter.new
258
+ end
259
+ ```
data/Rakefile CHANGED
@@ -1,35 +1,49 @@
1
- require 'rubygems'
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
2
+
2
3
  require 'rake'
3
4
  require 'rspec/core/rake_task'
4
-
5
- $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
5
+ require 'cucumber/rake/task'
6
6
  require 'choosy/rake'
7
7
 
8
8
  desc "Default task"
9
9
  task :default => [:spec]
10
10
 
11
- desc "Run the RSpec tests"
12
- RSpec::Core::RakeTask.new :spec
11
+ desc "Run all of the specifications."
12
+ task :spec => ['spec:default']
13
+ namespace :spec do
14
+ task :default => [:rspec, :cucumber]
15
+
16
+ desc "Run the RSpec tests."
17
+ RSpec::Core::RakeTask.new :rspec
13
18
 
14
- desc "Build documentation"
15
- task :doc => [ :rdoc ] do
16
- File.open 'docs/README.markdown', 'r' do |template|
17
- File.open 'README.markdown', 'w' do |output|
19
+ desc "Runs all of the cucumber tasks. Add 'tag=TAG' to run only matching tags."
20
+ Cucumber::Rake::Task.new :cucumber do |cucumber|
21
+ cucumber.cucumber_opts = ["-t #{ENV['tag'] || "all"}", "features"]
22
+ end
23
+ end
24
+
25
+ desc "Rebuilds the README.md file."
26
+ task :readme do
27
+ File.open 'docs/README.md', 'r' do |template|
28
+ File.open 'README.md', 'w' do |output|
18
29
  template.each do |line|
19
- if line =~ /^>>> (.*)/
20
- puts $1
30
+ if line =~ /^INCLUDE\((.*?),\s*(.*?)\)/
31
+ output.puts " ```#{$2}"
32
+
21
33
  File.open $1, 'r' do |toinsert|
22
34
  exclude = false
23
35
  toinsert.each_line do |ins|
24
- if ins =~ /^##-/
36
+ if ins =~ /^#=begin/
25
37
  exclude = true
26
38
  end
27
39
  output.puts " #{ins}" unless exclude
28
- if ins =~ /^##\+/
40
+ if ins =~ /^#=end/
29
41
  exclude = false
30
42
  end
31
43
  end
32
44
  end
45
+
46
+ output.puts " ```"
33
47
  else
34
48
  output.puts line
35
49
  end
@@ -38,11 +52,8 @@ task :doc => [ :rdoc ] do
38
52
  end
39
53
  end
40
54
 
41
- desc "Create RDocs: TODO"
42
- task :rdoc
43
-
44
55
  desc "Cleans the generated files."
45
56
  task :clean => ['gem:clean']
46
57
 
47
58
  desc "Runs the full deploy"
48
- task :push => [:release, :clean]
59
+ task :push => [:readme, 'choosy:release', :clean]
@@ -1,6 +1,6 @@
1
- ---
2
- date: 07/04/2011
3
- version:
4
- tiny: 9
1
+ ---
2
+ date: 24/04/2012
3
+ version:
4
+ tiny: 10
5
5
  major: 0
6
6
  minor: 4
@@ -1,16 +1,44 @@
1
- # $:.unshift File.dirname(__FILE__)
1
+ require 'tsort'
2
+ require 'time'
3
+ require 'date'
4
+ require 'yaml'
2
5
 
3
- require 'choosy/command'
4
- require 'choosy/super_command'
5
- require 'choosy/converter'
6
6
  require 'choosy/errors'
7
- require 'choosy/option'
8
- require 'choosy/version'
7
+ require 'choosy/base_command'
8
+ require 'choosy/parse_result'
9
9
  require 'choosy/parser'
10
10
  require 'choosy/verifier'
11
+ require 'choosy/command'
12
+ require 'choosy/argument'
13
+ require 'choosy/option'
14
+ require 'choosy/terminal'
15
+ require 'choosy/converter'
16
+
17
+ require 'choosy/printing/base_printer'
18
+ require 'choosy/printing/color'
19
+
20
+ require 'choosy/dsl/base_builder'
21
+ require 'choosy/dsl/argument_builder'
22
+ require 'choosy/dsl/base_command_builder'
11
23
  require 'choosy/dsl/command_builder'
12
- require 'choosy/dsl/super_command_builder'
13
24
  require 'choosy/dsl/option_builder'
14
- require 'choosy/printing/color'
15
- require 'choosy/printing/erb_printer'
16
- require 'choosy/printing/help_printer'
25
+
26
+ module Choosy
27
+ autoload :SuperCommand, 'choosy/super_command'
28
+ autoload :SuperParser, 'choosy/super_parser'
29
+ autoload :Version, 'choosy/version'
30
+
31
+ module Printing
32
+ autoload :ERBPrinter, 'choosy/printing/erb_printer'
33
+ autoload :FormattingElement, 'choosy/printing/formatting_element'
34
+ autoload :HelpPrinter, 'choosy/printing/help_printer'
35
+ autoload :Manpage, 'choosy/printing/manpage'
36
+ autoload :ManpageFormatter, 'choosy/printing/manpage'
37
+ autoload :ManpagePrinter, 'choosy/printing/manpage_printer'
38
+ end
39
+
40
+ module DSL
41
+ autoload :SuperCommandBuilder, 'choosy/dsl/super_command_builder'
42
+ end
43
+ end
44
+
@@ -1,5 +1,3 @@
1
- require 'choosy/errors'
2
-
3
1
  module Choosy
4
2
  class Argument
5
3
  ZERO_ARITY = (0 .. 0)
@@ -1,6 +1,3 @@
1
- require 'choosy/errors'
2
- require 'tsort'
3
-
4
1
  module Choosy
5
2
  class OptionBuilderHash < Hash
6
3
  include TSort
@@ -53,12 +50,16 @@ module Choosy
53
50
  end
54
51
  end
55
52
 
56
- def execute!(args)
57
- begin
53
+ def execute!(args, propagate=false)
54
+ if propagate
58
55
  execute(args)
59
- rescue Choosy::ClientExecutionError => e
60
- $stderr << "#{@name}: #{e.message}\n"
61
- exit 1
56
+ else
57
+ begin
58
+ execute(args)
59
+ rescue Choosy::ClientExecutionError => e
60
+ $stderr << "#{@name}: #{e.message}\n"
61
+ exit 1
62
+ end
62
63
  end
63
64
  end
64
65