git-style-binaries 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,223 @@
1
+ module GitStyleBinary
2
+ class Parser < Trollop::Parser
3
+ attr_reader :runs, :callbacks
4
+ attr_reader :short_desc
5
+ attr_accessor :command
6
+
7
+ def initialize *a, &b
8
+ super
9
+ @runs = []
10
+ setup_callbacks
11
+ end
12
+
13
+ def setup_callbacks
14
+ @callbacks = {}
15
+ %w(run).each do |event|
16
+ %w(before after).each do |time|
17
+ @callbacks["#{time}_#{event}".to_sym] = []
18
+ instance_eval "def #{time}_#{event}(&block);@callbacks[:#{time}_#{event}] << block;end"
19
+ end
20
+ end
21
+ end
22
+
23
+ def run_callbacks(at, from)
24
+ @callbacks[at].each {|c| c.call(from) }
25
+ end
26
+
27
+ def banner s=nil; @banner = s if s; @banner end
28
+ def short_desc s=nil; @short_desc = s if s; @short_desc end
29
+ def name_desc s=nil; @name_desc = s if s; @name_desc end
30
+
31
+ # Set the theme. Valid values are +:short+ or +:long+. Default +:long+
32
+ attr_writer :theme
33
+
34
+ def theme
35
+ @theme ||= :long
36
+ end
37
+
38
+ ## Adds text to the help display.
39
+ def text s; @order << [:text, s] end
40
+
41
+ def spec_names
42
+ @specs.collect{|name, spec| spec[:long]}
43
+ end
44
+
45
+ # should probably be somewhere else
46
+ def load_all_commands
47
+ GitStyleBinary.subcommand_names.each do |name|
48
+ cmd_file = GitStyleBinary.binary_filename_for(name)
49
+ GitStyleBinary.load_command_file(name, cmd_file)
50
+ end
51
+ end
52
+
53
+ ## Print the help message to 'stream'.
54
+ def educate(stream=$stdout)
55
+ load_all_commands
56
+ width # just calculate it now; otherwise we have to be careful not to
57
+ # call this unless the cursor's at the beginning of a line.
58
+ GitStyleBinary::Helpers::Pager.run_pager
59
+ self.send("educate_#{theme}", stream)
60
+ end
61
+
62
+ def educate_long(stream=$stdout)
63
+ left = {}
64
+
65
+ @specs.each do |name, spec|
66
+ left[name] =
67
+ ((spec[:short] ? "-#{spec[:short]}, " : "") +
68
+ "--#{spec[:long]}" +
69
+ case spec[:type]
70
+ when :flag; ""
71
+ when :int; "=<i>"
72
+ when :ints; "=<i+>"
73
+ when :string; "=<s>"
74
+ when :strings; "=<s+>"
75
+ when :float; "=<f>"
76
+ when :floats; "=<f+>"
77
+ end).colorize(:red)
78
+ end
79
+
80
+ leftcol_width = left.values.map { |s| s.length }.max || 0
81
+ rightcol_start = leftcol_width + 6 # spaces
82
+ leftcol_start = 6
83
+ leftcol_spaces = " " * leftcol_start
84
+
85
+ unless @order.size > 0 && @order.first.first == :text
86
+
87
+ if @name_desc
88
+ stream.puts "NAME".colorize(:red)
89
+ stream.puts "#{leftcol_spaces}"+ colorize_known_words(eval(%Q["#{@name_desc}"])) + "\n"
90
+ stream.puts
91
+ end
92
+
93
+ if @version
94
+ stream.puts "VERSION".colorize(:red)
95
+ stream.puts "#{leftcol_spaces}#@version\n"
96
+ end
97
+
98
+ stream.puts
99
+
100
+ banner = colorize_known_words_array(wrap(eval(%Q["#{@banner}"]) + "\n", :prefix => leftcol_start)) if @banner # lazy banner
101
+ stream.puts banner
102
+
103
+ stream.puts
104
+ stream.puts "OPTIONS".colorize(:red)
105
+ else
106
+ stream.puts "#@banner\n" if @banner
107
+ end
108
+
109
+ @order.each do |what, opt|
110
+ if what == :text
111
+ stream.puts wrap(opt)
112
+ next
113
+ end
114
+
115
+ spec = @specs[opt]
116
+ stream.printf " %-#{leftcol_width}s\n", left[opt]
117
+ desc = spec[:desc] +
118
+ if spec[:default]
119
+ if spec[:desc] =~ /\.$/
120
+ " (Default: #{spec[:default]})"
121
+ else
122
+ " (default: #{spec[:default]})"
123
+ end
124
+ else
125
+ ""
126
+ end
127
+ stream.puts wrap(" %s" % [desc], :prefix => leftcol_start, :width => width - rightcol_start - 1 )
128
+ stream.puts
129
+ stream.puts
130
+ end
131
+
132
+ end
133
+
134
+ def educate_short(stream=$stdout)
135
+ left = {}
136
+
137
+ @specs.each do |name, spec|
138
+ left[name] = "--#{spec[:long]}" +
139
+ (spec[:short] ? ", -#{spec[:short]}" : "") +
140
+ case spec[:type]
141
+ when :flag; ""
142
+ when :int; " <i>"
143
+ when :ints; " <i+>"
144
+ when :string; " <s>"
145
+ when :strings; " <s+>"
146
+ when :float; " <f>"
147
+ when :floats; " <f+>"
148
+ end
149
+ end
150
+
151
+ leftcol_width = left.values.map { |s| s.length }.max || 0
152
+ rightcol_start = leftcol_width + 6 # spaces
153
+ leftcol_start = 0
154
+
155
+ unless @order.size > 0 && @order.first.first == :text
156
+ stream.puts "#@version\n" if @version
157
+ stream.puts colorize_known_words_array(wrap(eval(%Q["#{@banner}"]) + "\n", :prefix => leftcol_start)) if @banner # jit banner
158
+ stream.puts "Options:"
159
+ else
160
+ stream.puts "#@banner\n" if @banner
161
+ end
162
+
163
+ @order.each do |what, opt|
164
+ if what == :text
165
+ stream.puts wrap(opt)
166
+ next
167
+ end
168
+
169
+ spec = @specs[opt]
170
+ stream.printf " %#{leftcol_width}s: ", left[opt]
171
+ desc = spec[:desc] +
172
+ if spec[:default]
173
+ if spec[:desc] =~ /\.$/
174
+ " (Default: #{spec[:default]})"
175
+ else
176
+ " (default: #{spec[:default]})"
177
+ end
178
+ else
179
+ ""
180
+ end
181
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
182
+ end
183
+
184
+ end
185
+
186
+
187
+ def colorize_known_words_array(txts)
188
+ txts.collect{|txt| colorize_known_words(txt)}
189
+ end
190
+
191
+ def colorize_known_words(txt)
192
+ txt = txt.gsub(/^([A-Z]+\s*)$/, '\1'.colorize(:red)) # all caps words on their own line
193
+ txt = txt.gsub(/\b(#{bin_name})\b/, '\1'.colorize(:light_blue)) # the current command name
194
+ txt = txt.gsub(/\[([^\s]+)\]/, "[".colorize(:magenta) + '\1'.colorize(:green) + "]".colorize(:magenta)) # synopsis options
195
+ end
196
+
197
+ def consume(&block)
198
+ cloaker(&block).bind(self).call
199
+ end
200
+
201
+ def consume_all(blocks)
202
+ blocks.each {|b| consume(&b)}
203
+ end
204
+
205
+ def bin_name
206
+ GitStyleBinary.full_current_command_name
207
+ end
208
+
209
+ def all_options_string
210
+ # '#{spec_names.collect(&:to_s).collect{|name| "[".colorize(:magenta) + "--" + name + "]".colorize(:magenta)}.join(" ")} COMMAND [ARGS]'
211
+ '#{spec_names.collect(&:to_s).collect{|name| "[" + "--" + name + "]"}.join(" ")} COMMAND [ARGS]'
212
+ end
213
+
214
+ def run(&block)
215
+ @runs << block
216
+ end
217
+
218
+ def action(name = :action, &block)
219
+ block.call(self) if block
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../../lib")
3
+ VERSION="0.0.2" # just to test it
4
+ require 'git-style-binary/command'
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../../lib")
3
+ require 'git-style-binary/command'
4
+
5
+ GitStyleBinary.command do
6
+ short_desc "download a flickr image"
7
+ banner <<-EOS
8
+ SYNOPSIS
9
+ #{command.full_name} #{all_options_string} url
10
+
11
+ Downloads an image from flickr
12
+
13
+ EOS
14
+ run do |command|
15
+ puts "would download: #{command.argv.inspect}"
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../../lib")
3
+
4
+ require 'git-style-binary/command'
5
+ GitStyleBinary.primary do
6
+ version "0.0.1 (c) 2009 Nate Murray - local"
7
+ opt :test_primary, "test an option on the primary", :type => String
8
+
9
+ action do
10
+ @categories = ["sports", "news"]
11
+ end
12
+
13
+ before_run do |cmd|
14
+ puts "before_run command #{cmd}"
15
+ end
16
+
17
+ after_run do |cmd|
18
+ puts "after_run command #{cmd}"
19
+ end
20
+
21
+ run do |command|
22
+ puts "Primary Options: #{command.opts.inspect}"
23
+ end
24
+ end
25
+
26
+ # OR
27
+
28
+ # require 'git-style-binary/primary'
29
+ # command = GitStyleBinary::primary("wordpress") do
30
+ # version "#{$0} 0.0.1 (c) 2009 Nate Murray"
31
+ # banner <<-EOS
32
+ # usage: #{$0} #{all_options.collect(:&to_s).join(" ")} COMMAND [ARGS]
33
+ #
34
+ # The wordpress subcommands commands are:
35
+ # {subcommand_names.pretty_print}
36
+ #
37
+ # See 'wordpress help COMMAND' for more information on a specific command.
38
+ # EOS
39
+ # opt :verbose, "verbose", :default => false
40
+ # opt :dry, "dry run", :default => false
41
+ # opt :test_global, "a basic global string option", :type => String
42
+ # end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../../lib")
3
+ require 'git-style-binary/command'
4
+
5
+ GitStyleBinary.command do
6
+ short_desc "do something with categories"
7
+ banner <<-EOS
8
+ SYNOPSIS
9
+ #{command.full_name} #{all_options_string}
10
+
11
+ Does something with categories
12
+
13
+ EOS
14
+ run do |command|
15
+ puts "does something with categories"
16
+ puts @categories.join(" ")
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../../lib")
3
+ require 'git-style-binary/command'
4
+
5
+ GitStyleBinary.command do
6
+ short_desc "list blog postings"
7
+ banner <<-EOS
8
+ SYNOPSIS
9
+ #{command.full_name} #{all_options_string}
10
+
11
+ Lists the posts on the blog
12
+
13
+ EOS
14
+ run do |command|
15
+ puts "listing blog posts"
16
+ end
17
+ end
18
+
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../../lib")
3
+ require 'git-style-binary/command'
4
+
5
+ GitStyleBinary.command do
6
+ short_desc "create a blog post"
7
+ banner <<-EOS
8
+ SYNOPSIS
9
+ #{command.full_name} #{all_options_string} {content|STDIN}
10
+
11
+ EOS
12
+ opt :blog, "short name of the blog to use", :default => 'default'
13
+ opt :category, "tag/category. specify multiple times for multiple categories", :type => String, :multi => true
14
+ opt :title, "title for the post", :required => true, :type => String
15
+ opt :type, "type of the content [html|xhtml|text]", :default => 'html', :type => String
16
+
17
+ run do |command|
18
+ command.die :type, "type must be one of [html|xhtml|text]" unless command.opts[:type] =~ /^(x?html|text)$/i
19
+
20
+ puts "Subcommand name: #{command.name.inspect}"
21
+ puts "Options: #{command.opts.inspect}"
22
+ puts "Remaining arguments: #{command.argv.inspect}"
23
+ end
24
+ end
25
+
26
+
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + "/../test_helper.rb"
2
+ require 'git-style-binary/command'
3
+
4
+ class CommandTest < Test::Unit::TestCase
5
+ context "cmd" do
6
+ setup do
7
+ @c = GitStyleBinary::Command.new
8
+ end
9
+
10
+ should "be able to easily work with constraints" do
11
+ assert_equal @c.constraints, []
12
+ @c.constraints << "foo"
13
+ assert_equal @c.constraints, ["foo"]
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + "/test_helper.rb"
2
+
3
+ class GitStyleBinariesTest < Test::Unit::TestCase
4
+ context "parsing basenames" do
5
+ should "accurately parse basenames" do
6
+ assert_equal "wordpress", GitStyleBinary.basename("bin/wordpress")
7
+ assert_equal "wordpress", GitStyleBinary.basename("bin/wordpress-post")
8
+ assert_equal "wordpress", GitStyleBinary.basename("wordpress-post")
9
+ end
10
+
11
+ should "get the current command name" do
12
+ # doesn't really apply any more b/c it calls 'current' which is never the
13
+ # current when your running rake_test_loader.rb
14
+ #
15
+ # assert_equal "wordpress", GitStyleBinary.current_command_name("bin/wordpress", ["--help"])
16
+ # assert_equal "post", GitStyleBinary.current_command_name("bin/wordpress-post", ["--help"])
17
+ # assert_equal "post", GitStyleBinary.current_command_name("bin/wordpress post", ["--help"])
18
+ #assert_equal "post", GitStyleBinary.current_command_name("bin/wordpress post", [])
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,224 @@
1
+ require File.dirname(__FILE__) + "/test_helper.rb"
2
+
3
+ THIS_YEAR=Time.now.year # todo
4
+
5
+ class RunningBinariesTest < Test::Unit::TestCase
6
+ include RunsBinaryFixtures
7
+
8
+ context "when running primary" do
9
+ ["wordpress -h", "wordpress help"].each do |format|
10
+ context "and getting help as a '#{format}'" do
11
+ setup { @stdout, @stderr = bin(format) }
12
+
13
+ should "have the command name and short description" do
14
+ unless format == "wordpress -h" # doesn't apply to wordpress -h
15
+ output_matches /NAME\n\s*wordpress\-help \- get help for a specific command/m
16
+ end
17
+ end
18
+
19
+ should "have a local (not default) version string" do
20
+ output_matches /0\.0\.1 \(c\) 2009 Nate Murray - local/
21
+ end
22
+
23
+ should "get a list of subcommands" do
24
+ output_matches /subcommands/mi
25
+ end
26
+
27
+ should "have subcommand short descriptions" do
28
+ output_matches /post\s*create a blog post/
29
+ output_matches /categories\s*do something with categories/
30
+ output_matches /help\s*get help for a specific command/
31
+ output_matches /list\s*list blog postings/
32
+ end
33
+
34
+ should "have a usage" do
35
+ output_matches /SYNOPSIS/i
36
+ output_matches /wordpress(\-help)? \[/
37
+ end
38
+
39
+ should "be able to ask for help about help"
40
+ end
41
+ end
42
+
43
+ context "and getting help as subcommand" do
44
+ # ["wordpress -h", "wordpress help"].each do |format|
45
+ ["wordpress help"].each do |format|
46
+ context "'#{format}'" do
47
+ should "get help on subcommand post"
48
+ end
49
+ end
50
+ end
51
+
52
+ context "with no options" do
53
+ setup { @stdout, @stderr = bin("wordpress") }
54
+
55
+ should "output the options" do
56
+ output_matches /Primary Options:/
57
+ end
58
+
59
+ should "have the test_primary option" do
60
+ output_matches /test_primary=>nil/
61
+ end
62
+ end
63
+ should "be able to require 'primary' and run just fine"
64
+ end
65
+
66
+ context "when running with an action" do
67
+ # should be the same for both formats
68
+ ["wordpress-categories", "wordpress categories"].each do |bin_format|
69
+ context "#{bin_format}" do
70
+
71
+ context "with action block" do
72
+ setup { @stdout, @stderr = bin("#{bin_format}") }
73
+ should "have the parsed action items in the help output" do
74
+ output_matches /sports news/m
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ context "callbacks" do
82
+ context "on a binary" do
83
+ setup { @stdout, @stderr = bin("wordpress") }
84
+
85
+ %w(before after).each do |time|
86
+ should "run the callback #{time}_run}" do
87
+ assert @stdout.match(/#{time}_run command/)
88
+ end
89
+ end
90
+ end
91
+
92
+ context "on help" do
93
+ setup { @stdout, @stderr = bin("wordpress -h") }
94
+
95
+ %w(before after).each do |time|
96
+ should "not run the callback #{time}_run" do
97
+ assert_nil @stdout.match(/#{time}_run command/)
98
+ end
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+
105
+ context "when running the subcommand" do
106
+ # should be the same for both formats
107
+ ["wordpress-post", "wordpress post"].each do |bin_format|
108
+ context "#{bin_format}" do
109
+
110
+ context "with no options" do
111
+ setup { @stdout, @stderr = bin("#{bin_format}") }
112
+ should "fail because title is required" do
113
+ output_matches /Error: option 'title' must be specified.\s*Try --help for help/m
114
+ end
115
+ end
116
+
117
+ context "with options" do
118
+ setup { @stdout, @stderr = bin("#{bin_format} --title='glendale'") }
119
+ should "be running the subcommand's run block" do
120
+ output_matches /Subcommand name/
121
+ end
122
+ should "have some default options" do
123
+ output_matches /version=>false/
124
+ output_matches /help=>false/
125
+ end
126
+ should "have some primary options" do
127
+ output_matches /test_primary=>nil/
128
+ end
129
+ should "have some local options" do
130
+ output_matches /title=>"glendale"/
131
+ output_matches /type=>"html"/
132
+ end
133
+ end
134
+
135
+ context "testing die statements" do
136
+ setup { @stdout, @stderr = bin("#{bin_format} --title='glendale' --type=yaml") }
137
+
138
+ should "die on invalid options" do
139
+ output_matches /argument \-\-type type must be one of \[html\|xhtml\|text\]/
140
+ end
141
+ end
142
+
143
+ end # end bin_format
144
+ end # end #each
145
+ end
146
+
147
+ ["wordpress help post", "wordpress post -h"].each do |format|
148
+ context "when calling '#{format}'" do
149
+
150
+ setup { @stdout, @stderr = bin(format) }
151
+ should "have a description" do
152
+ output_matches /create a blog post/
153
+ end
154
+
155
+ should "have the proper usage line" do
156
+ output_matches /SYNOPSIS\n\s*wordpress\-post/m
157
+ output_matches /\[--title\]/
158
+ end
159
+
160
+ should "have option flags" do
161
+ output_matches /\-\-title(.*)<s>/
162
+ end
163
+
164
+ should "have primary option flags" do
165
+ output_matches /\-\-test-primary(.*)<s>/
166
+ end
167
+
168
+ should "have default option flags" do
169
+ output_matches /\-\-verbose/
170
+ end
171
+
172
+ should "have trollop default option flags" do
173
+ output_matches /\-e, \-\-version/
174
+ end
175
+
176
+ should "have the correct binary name and short description" do
177
+ output_matches /NAME\n\s*wordpress\-post \- create a blog post/m
178
+ end
179
+
180
+ should "have a the primaries version string" do
181
+ output_matches /0\.0\.1 \(c\) 2009 Nate Murray - local/
182
+ end
183
+
184
+ should "have options" do
185
+ output_matches /Options/i
186
+
187
+ output_matches /\-b, \-\-blog=<s>/
188
+ output_matches /short name of the blog to use/
189
+
190
+ output_matches /-i, \-\-title=<s>/
191
+ output_matches /title for the post/
192
+ end
193
+
194
+ end
195
+ end
196
+
197
+ context "when running a bare primary" do
198
+ ["flickr -h", "flickr help"].each do |format|
199
+ context format do
200
+ setup { @stdout, @stderr = bin(format) }
201
+
202
+ should "have the name and short description" do
203
+ unless format == "flickr -h" # hmm
204
+ output_matches /NAME\n\s*flickr\-help \- get help for a specific command/m
205
+ end
206
+ end
207
+
208
+ should "have a local (not default) version string" do
209
+ output_matches /0\.0\.2 \(c\) 2009/
210
+ end
211
+ end
212
+ end
213
+ ["flickr-download -h", "flickr download -h"].each do |format|
214
+ context format do
215
+ setup { @stdout, @stderr = bin(format) }
216
+
217
+ should "match on usage" do
218
+ output_matches /SYNOPSIS\n\s*flickr\-download/m
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ end