choosy 0.1.0 → 0.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.
Files changed (45) hide show
  1. data/README.markdown +229 -221
  2. data/Rakefile +21 -3
  3. data/examples/bar.rb +44 -0
  4. data/examples/foo.rb +198 -0
  5. data/examples/superfoo.rb +125 -0
  6. data/lib/VERSION +1 -1
  7. data/lib/choosy/argument.rb +51 -0
  8. data/lib/choosy/base_command.rb +22 -7
  9. data/lib/choosy/command.rb +12 -4
  10. data/lib/choosy/dsl/argument_builder.rb +88 -0
  11. data/lib/choosy/dsl/base_command_builder.rb +71 -56
  12. data/lib/choosy/dsl/command_builder.rb +14 -2
  13. data/lib/choosy/dsl/option_builder.rb +43 -83
  14. data/lib/choosy/dsl/super_command_builder.rb +37 -9
  15. data/lib/choosy/option.rb +13 -11
  16. data/lib/choosy/parse_result.rb +8 -27
  17. data/lib/choosy/parser.rb +20 -16
  18. data/lib/choosy/printing/color.rb +39 -21
  19. data/lib/choosy/printing/erb_printer.rb +12 -3
  20. data/lib/choosy/printing/formatting_element.rb +17 -0
  21. data/lib/choosy/printing/help_printer.rb +204 -117
  22. data/lib/choosy/printing/terminal.rb +53 -0
  23. data/lib/choosy/super_command.rb +6 -6
  24. data/lib/choosy/super_parser.rb +26 -15
  25. data/lib/choosy/verifier.rb +61 -6
  26. data/spec/choosy/base_command_spec.rb +27 -2
  27. data/spec/choosy/command_spec.rb +31 -9
  28. data/spec/choosy/dsl/argument_builder_spec.rb +180 -0
  29. data/spec/choosy/dsl/base_command_builder_spec.rb +87 -44
  30. data/spec/choosy/dsl/commmand_builder_spec.rb +15 -4
  31. data/spec/choosy/dsl/option_builder_spec.rb +101 -191
  32. data/spec/choosy/dsl/super_command_builder_spec.rb +34 -9
  33. data/spec/choosy/parser_spec.rb +30 -8
  34. data/spec/choosy/printing/color_spec.rb +19 -5
  35. data/spec/choosy/printing/help_printer_spec.rb +152 -73
  36. data/spec/choosy/printing/terminal_spec.rb +27 -0
  37. data/spec/choosy/super_command_spec.rb +17 -17
  38. data/spec/choosy/super_parser_spec.rb +20 -10
  39. data/spec/choosy/verifier_spec.rb +137 -47
  40. data/spec/integration/command-A_spec.rb +6 -6
  41. data/spec/integration/command-B_spec.rb +45 -0
  42. data/spec/integration/supercommand-A_spec.rb +33 -27
  43. data/spec/integration/supercommand-B_spec.rb +32 -0
  44. data/spec/spec_helpers.rb +8 -5
  45. metadata +95 -54
@@ -11,16 +11,16 @@ module Choosy::DSL
11
11
 
12
12
  describe :command do
13
13
  it "should add the command to the listing" do
14
- @builder.command :foo do |foo|
15
- foo.boolean :count, "The count"
14
+ @builder.command :foo do
15
+ boolean :count, "The count"
16
16
  end
17
17
 
18
18
  @command.listing.should have(1).item
19
19
  end
20
20
 
21
21
  it "should add the command builder to the command_builders" do
22
- o = @builder.command :foo do |foo|
23
- foo.integer :size, "The size"
22
+ o = @builder.command :foo do
23
+ integer :size, "The size"
24
24
  end
25
25
 
26
26
  @command.command_builders[:foo].should be(o.builder)
@@ -32,13 +32,31 @@ module Choosy::DSL
32
32
  end
33
33
 
34
34
  it "should be able to accept a new command as an argument" do
35
- cmd = Choosy::Command.new :cmd do |c|
36
- c.float :float, "Float"
35
+ cmd = Choosy::Command.new :cmd do
36
+ float :float, "Float"
37
37
  end
38
38
  @builder.command cmd
39
39
  @command.listing[0].should be(cmd)
40
40
  end
41
41
  end
42
+
43
+ describe :parsimonious do
44
+ it "should not be parsimonous by default" do
45
+ @command.parsimonious?.should be_false
46
+ end
47
+
48
+ it "should set parsimonious" do
49
+ @builder.parsimonious
50
+ @command.parsimonious?.should be_true
51
+ end
52
+ end
53
+
54
+ describe :metaname do
55
+ it "should set the super command's metaname for the subcommands" do
56
+ @builder.metaname 'META'
57
+ @command.metaname.should eql('META')
58
+ end
59
+ end
42
60
 
43
61
  describe "standard options" do
44
62
  it "should also be able to set flags" do
@@ -47,6 +65,13 @@ module Choosy::DSL
47
65
  end
48
66
  end
49
67
 
68
+ describe :finalize! do
69
+ it "should set the metaname if not already set" do
70
+ @builder.finalize!
71
+ @command.metaname.should eql('COMMAND')
72
+ end
73
+ end
74
+
50
75
  describe :help do
51
76
  it "should create a help command when asked" do
52
77
  h = @builder.help
@@ -67,14 +92,14 @@ module Choosy::DSL
67
92
  it "should return the super command name when called without arguments" do
68
93
  h = @builder.help
69
94
  attempting {
70
- h.argument_validation.call([])
71
- }.should raise_error(Choosy::HelpCalled, :superfoo)
95
+ h.arguments.validation_step.call([])
96
+ }.should raise_error(Choosy::HelpCalled, nil)
72
97
  end
73
98
 
74
99
  it "should return the name of the first argument when called, as a symbol" do
75
100
  h = @builder.help
76
101
  attempting {
77
- h.argument_validation.call(['foo'])
102
+ h.arguments.validation_step.call(['foo'])
78
103
  }.should raise_error(Choosy::HelpCalled, :foo)
79
104
  end
80
105
  end
@@ -5,6 +5,8 @@ require 'choosy/dsl/command_builder'
5
5
 
6
6
  module Choosy
7
7
  class ParserBuilder
8
+ attr_reader :command, :lazy
9
+
8
10
  def initialize
9
11
  @terminals = nil
10
12
  @lazy = false
@@ -26,17 +28,22 @@ module Choosy
26
28
  self
27
29
  end
28
30
 
29
- def boolean(sym, default=nil)
31
+ def boolean(sym, default=nil, negated=nil)
30
32
  default ||= false
31
- @command.alter do |c|
32
- c.boolean(sym, sym.to_s, :default => default)
33
+ negated ||= false
34
+ @command.alter do
35
+ boolean(sym, sym.to_s, :default => default) do
36
+ if negated
37
+ negate
38
+ end
39
+ end
33
40
  end
34
41
  self
35
42
  end
36
43
 
37
44
  def single(sym)
38
- @command.alter do |c|
39
- c.single(sym, sym.to_s)
45
+ @command.alter do
46
+ single(sym, sym.to_s)
40
47
  end
41
48
  self
42
49
  end
@@ -44,9 +51,9 @@ module Choosy
44
51
  def multiple(sym, min=nil, max=nil)
45
52
  min ||= 1
46
53
  max ||= 1000
47
- @command.alter do |c|
48
- c.multiple(sym, sym.to_s) do |m|
49
- m.count :at_least => min, :at_most => max
54
+ @command.alter do
55
+ multiple(sym, sym.to_s) do
56
+ count :at_least => min, :at_most => max
50
57
  end
51
58
  end
52
59
  self
@@ -149,6 +156,21 @@ module Choosy
149
156
  res.options.should eql({:o => true})
150
157
  end
151
158
 
159
+ it "should handle negated boolean arguments" do
160
+ @pb.boolean(:o, false, true)
161
+ @pb.build.flags['--no-o'].should_not be_nil
162
+ end
163
+
164
+ it "should handle a negated boolean argument" do
165
+ res = @pb.boolean(:o, false, true).parse!('--no-o')
166
+ res.options.should eql({:o => false})
167
+ end
168
+
169
+ it "should handle a negated boolean argument whose default is true" do
170
+ res = @pb.boolean(:o, true, true).parse!('--no-o')
171
+ res.options.should eql({:o => true})
172
+ end
173
+
152
174
  it "should handle a boolean flag whose default is true" do
153
175
  res = @pb.boolean(:opt, true).parse!('-o')
154
176
  res.options.should eql({:opt => false})
@@ -52,23 +52,37 @@ module Choosy::Printing
52
52
  end
53
53
 
54
54
  it "should return a formatted string for just the color code when no args are given" do
55
- @c.red.should eql("e31[m")
55
+ @c.red.should eql("\e[31m")
56
56
  end
57
57
 
58
58
  it "should return a formatted string with a reset if a full string is supplied" do
59
- @c.red("this").should eql("e31[mthise0[m")
59
+ @c.red("this").should eql("\e[31mthis\e[0m")
60
60
  end
61
61
 
62
62
  it "should recognize when the foreground is being set" do
63
- @c.red("this", :foreground).should eql("e31[mthise0[m")
63
+ @c.red("this", :foreground).should eql("\e[31mthis\e[0m")
64
64
  end
65
65
 
66
66
  it "should recognize when the background is being set" do
67
- @c.red("this", :background).should eql("e41[mthise0[m")
67
+ @c.red("this", :background).should eql("\e[41mthis\e[0m")
68
68
  end
69
69
 
70
70
  it "should also handle effects" do
71
- @c.blink("this").should eql("e5[mthise0[m")
71
+ @c.blink("this").should eql("\e[5mthis\e[0m")
72
+ end
73
+
74
+ it "should fail when effects are handed too many parameters" do
75
+ attempting {
76
+ @c.blink("this", :noarg)
77
+ }.should raise_error(ArgumentError, /max 1/)
78
+ end
79
+
80
+ it "should not add an extra reset to the end if decorated more than once" do
81
+ @c.red(@c.blink("this")).should eql("\e[31m\e[5mthis\e[0m")
82
+ end
83
+
84
+ it "should be able to format multiple items" do
85
+ @c.multiple("this", [:red, :blink]).should eql("\e[5m\e[31mthis\e[0m")
72
86
  end
73
87
  end
74
88
  end
@@ -1,117 +1,196 @@
1
1
  require 'spec_helpers'
2
2
  require 'choosy/command'
3
+ require 'choosy/super_command'
3
4
  require 'choosy/printing/help_printer'
4
5
 
5
6
  module Choosy::Printing
6
7
  describe HelpPrinter do
7
8
  before :each do
8
- @h = HelpPrinter.new
9
- @c = Choosy::Command.new :foo do |foo|
10
- foo.summary "This is a summary of a command."
11
- foo.desc <<EOF
12
- This is a description of this command that should span
13
-
14
- multiple lines and carry itself beyond the average line length when actually called out from the unit tests itself so that we can correctly guage the line wrapping.
15
- EOF
16
- foo.separator
17
- foo.boolean :evaluate, "The evaluation"
18
- foo.integer :count, "The count"
19
- foo.separator
20
-
21
- foo.separator 'Indifferent options:'
22
- foo.boolean :debug, "Debug output"
23
- foo.version "1.2"
24
- foo.help
9
+ @c = Choosy::Command.new :foo do
10
+ summary "This is a fairly long summary that should wrap around the whole screen at least once, so that I can test whether it's properly formatted"
11
+
12
+ header 'DESCRIPTION'
13
+ para 'This is a description of this command that should span'
14
+ para 'Multiple lines and carry itself beyond the average line length when actually called out from the unit tests itself so that we can correctly guage the line wrapping.'
15
+
16
+ header 'OPTIONS'
17
+ boolean :evaluate, "The evaluation of some boolean something or other that really should span at least 3 lines of continuous text for testing the output of the option command."
18
+ integer :count, "The count of something that should also really span multiple lines, if possible."
19
+ boolean_ :debug, "Debug output"
20
+ version "1.2"
21
+ help
22
+
23
+ arguments do
24
+ metaname 'FOOS'
25
+ count 0..3
26
+ end
25
27
  end
26
- end
27
28
 
28
- it "should know the width of the screen, if possible, or set a default [COULD BREAK ON YOUR MACHINE]" do
29
- @h.columns.should satisfy {|c| c >= HelpPrinter::DEFAULT_COLUMN_COUNT }
30
- end
29
+ @b = @c.builder
31
30
 
32
- it "should know the lenght the screen, if possible, or set a default [COULD BREAK ON YOUR MACHINE]" do
33
- @h.lines.should satisfy {|l| l >= HelpPrinter::DEFAULT_LINE_COUNT }
34
- end
31
+ @s = Choosy::SuperCommand.new :super do
32
+ printer :standard, :color => true, :header_styles => [:bold, :blue]
33
+ command @c
34
+ metaname 'CMDS'
35
35
 
36
- it "should now how to format a usage string" do
37
- o = capture :stdout do
38
- @h.print_usage(@c)
36
+ boolean :bold, "Bold"
39
37
  end
40
38
 
41
- o.should eql("USAGE: foo [OPTIONS]\n")
39
+ @h = @s.printer
42
40
  end
43
41
 
44
- it "should show the usage string if it accepts extra arguments" do
45
- @c.alter do |foo|
46
- foo.arguments do |args|
47
- end
42
+ describe :print_usage do
43
+ it "should know how to format a regular command" do
44
+ @h.color.disable!
45
+ @h.columns = 60
46
+ @h.print_usage(@c)
47
+
48
+ @h.buffer.should eql("Usage: foo [-e|--evaluate] [-c|--count=COUNT] [--debug]
49
+ [--version] [-h|--help] FOOS\n")
48
50
  end
49
51
 
50
- o = capture :stdout do
51
- @h.print_usage(@c)
52
+ it "should know how to format a super command" do
53
+ @h.color.disable!
54
+ @h.columns = 60
55
+ @h.print_usage(@s)
56
+
57
+ @h.buffer.should eql("Usage: super [-b|--bold] CMDS\n")
52
58
  end
59
+ end
53
60
 
54
- o.should eql("USAGE: foo [OPTIONS] [ARGS]\n")
61
+ it "should write a header, properly colored" do
62
+ @h.print_header("option")
63
+ @h.buffer.should eql("\e[34m\e[1moption\e[0m")
55
64
  end
56
65
 
57
- it "should print a newline on an empty separator" do
58
- o = capture :stdout do
59
- @h.print_separator("")
60
- end
66
+ it "should print out a formatted header" do
67
+ @h.print_element(@c.listing[0])
68
+ @h.buffer.should eql("\n\e[34m\e[1mDESCRIPTION\e[0m\n")
69
+ end
61
70
 
62
- o.should eql("\n\n")
71
+ it "should print out a formatting element correctly" do
72
+ @h.print_element(@c.listing[1])
73
+ @h.buffer.should eql("\n This is a description of this command that should span\n")
63
74
  end
64
75
 
65
- it "should print a line with separator text" do
66
- o = capture :stdout do
67
- @h.print_separator("this line")
68
- end
76
+ it "should wrap lines in a paragraph correctly" do
77
+ @h.columns = 70
78
+ @h.print_element(@c.listing[2])
79
+ @h.buffer.should eql("\n Multiple lines and carry itself beyond the average line length when
80
+ actually called out from the unit tests itself so that we can
81
+ correctly guage the line wrapping.\n")
82
+ end
83
+
84
+ it "should print out an option on multiple lines" do
85
+ @h.columns = 70
86
+ @h.print_option(@c.listing[4], '-e, --evaluate', ' ' * 20)
87
+ @h.buffer.should eql(' -e, --evaluate The evaluation of some boolean something or other
88
+ that really should span at least 3 lines of
89
+ continuous text for testing the output of the
90
+ option command.
91
+ ')
92
+ end
69
93
 
70
- o.should eql("\nthis line\n")
94
+ it "should print out any commands that are present" do
95
+ @h.columns = 70
96
+ @h.print_command(@c, 'foo', ' ')
97
+ @h.buffer.should eql(" foo This is a fairly long summary that should wrap around the
98
+ whole screen at least once, so that I can test whether it's
99
+ properly formatted\n")
71
100
  end
72
101
 
73
- it "should print out a command" do
74
- o = capture :stdout do
75
- @h.print_command(@c)
102
+ describe "for the usage line" do
103
+ it "should format a full boolean option" do
104
+ o = @b.boolean :bold, "bold"
105
+ @h.usage_option(o).should eql("[-b|--bold]")
76
106
  end
77
107
 
78
- o.should eql(" foo\tThis is a summary of a command.\n")
79
- end
108
+ it "should format a partial boolean option" do
109
+ o = @b.boolean_ :bold, "bold"
110
+ @h.usage_option(o).should eql('[--bold]')
111
+ end
80
112
 
81
- it "should print the summary, if present" do
82
- o = capture :stdout do
83
- @h.print_summary(@c.summary)
113
+ it "should format a short boolean option" do
114
+ o = @b.option :bold do
115
+ short '-b'
116
+ end
117
+ @h.usage_option(o).should eql('[-b]')
84
118
  end
85
119
 
86
- o.should eql(" This is a summary of a command.\n")
87
- end
120
+ it "should format a negation of a boolean option" do
121
+ o = @b.boolean :bold, "Bold!!" do
122
+ negate 'un'
123
+ end
124
+ @h.usage_option(o).should eql('[-b|--bold|--un-bold]')
125
+ end
126
+
127
+ it "should format a full single option" do
128
+ o = @b.single :color, "color"
129
+ @h.usage_option(o).should eql('[-c|--color=COLOR]')
130
+ end
88
131
 
89
- it "should print out an option on 2 lines." do
90
- o = capture :stdout do
91
- @h.print_option(@c.listing[2])
132
+ it "should format a parial boolean option" do
133
+ o = @b.single_ :color, "color"
134
+ @h.usage_option(o).should eql('[--color=COLOR]')
135
+ end
136
+
137
+ it "shoudl format a full multiple option" do
138
+ o = @b.multiple :colors, "c"
139
+ @h.usage_option(o).should eql('[-c|--colors COLORS+]')
92
140
  end
93
141
 
94
- o.should eql(" -c, --count COUNT\n The count\n")
142
+ it "should format a partial multiple option" do
143
+ o = @b.multiple_ :colors, "c"
144
+ @h.usage_option(o).should eql('[--colors COLORS+]')
145
+ end
95
146
  end
96
-
97
- it "should print out any commands that are present"
98
147
 
99
- it "should print out the description" do
100
- @h.columns = 80
101
- o = capture :stdout do
102
- @h.print_description(@c.description)
148
+ describe "for the option line" do
149
+ it "should format a full boolean option" do
150
+ o = @b.boolean :bold, "b"
151
+ @h.regular_option(o).should eql('-b, --bold')
103
152
  end
104
153
 
105
- o.should eql(<<EOF
154
+ it "should format a partial boolean option" do
155
+ o = @b.boolean_ :bold, "b"
156
+ @h.regular_option(o).should eql(' --bold')
157
+ end
106
158
 
107
- DESCRIPTION
108
- This is a description of this command that should span
159
+ it "should format a short boolean option" do
160
+ o = @b.option :bold do |b|
161
+ b.short '-b'
162
+ end
109
163
 
110
- multiple lines and carry itself beyond the average line length when actually
111
- called out from the unit tests itself so that we can correctly guage the line
112
- wrapping.
113
- EOF
114
- )
164
+ @h.regular_option(o).should eql('-b')
165
+ end
166
+
167
+ it "should format a negation of an option" do
168
+ o = @b.boolean :bold, "Bold" do
169
+ negate 'un'
170
+ end
171
+
172
+ @h.regular_option(o).should eql('-b, --[un-]bold')
173
+ end
174
+
175
+ it "should format a full single option" do
176
+ o = @b.single :color, "color"
177
+ @h.regular_option(o).should eql('-c, --color COLOR')
178
+ end
179
+
180
+ it "should format a partial single option" do
181
+ o = @b.single_ :color, "color"
182
+ @h.regular_option(o).should eql(' --color COLOR')
183
+ end
184
+
185
+ it "should format a full multiple option" do
186
+ o = @b.multiple :colors, "colors"
187
+ @h.regular_option(o).should eql('-c, --colors COLORS+')
188
+ end
189
+
190
+ it "should format a partial multiple option" do
191
+ o = @b.multiple_ :colors, "colors"
192
+ @h.regular_option(o).should eql(' --colors COLORS+')
193
+ end
115
194
  end
116
195
  end
117
196
  end