shebang 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gems ADDED
@@ -0,0 +1,3 @@
1
+ bacon
2
+ yard
3
+ rdiscount
@@ -0,0 +1,10 @@
1
+ # Ignore build related files
2
+ doc
3
+ pkg/*.gem
4
+
5
+ # Ignore common crap
6
+ .DS_Store
7
+ Thumbs.db
8
+
9
+ # Keep those funky gitkeep files
10
+ !.gitkeep
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create 1.9.2@shebang
@@ -0,0 +1,16 @@
1
+ script: "rvm gemset import .gems; rake test"
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ree
7
+ - rbx
8
+ - rbx-2.0
9
+ - jruby
10
+
11
+ notifications:
12
+ email: false
13
+
14
+ branches:
15
+ only:
16
+ - master
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011, Yorick Peterse
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,154 @@
1
+ # README
2
+
3
+ Shebang is a nice wrapper around OptionParser that makes it easier to write
4
+ commandline executables. I wrote it after getting fed up of having to re-invent
5
+ the same wheel every time I wanted to write a commandline executable. For
6
+ example, a relatively simple command using OptionParser directly may look like
7
+ the following:
8
+
9
+ require 'optparse'
10
+
11
+ @options = {:force => false, :f => false, :name => nil, :n => nil}
12
+ parser = OptionParser.new do |opt|
13
+ opt.banner = <<-TXT.strip
14
+ Runs an example command.
15
+
16
+ Usage:
17
+ $ foobar.rb [OPTIONS]
18
+ TXT
19
+
20
+ opt.summary_indent = ' '
21
+
22
+ opt.separator "\nOptions:\n"
23
+
24
+ opt.on('-h', '--help', 'Shows this help message') do
25
+ puts parser
26
+ exit
27
+ end
28
+
29
+ opt.on('-v', '--version', 'Shows the current version') do
30
+ puts '0.1'
31
+ exit
32
+ end
33
+
34
+ opt.on('-f', '--force', 'Forces the command to run') do
35
+ @options[:force] = @options[:f] = true
36
+ end
37
+
38
+ opt.on('-n', '--name NAME', 'A person\'s name') do |name|
39
+ @options[:name] = @options[:n] = name
40
+ end
41
+ end
42
+
43
+ parser.parse!
44
+
45
+ puts "Your name is #{@options[:name]}"
46
+
47
+ Using Shebang this can be done as following:
48
+
49
+ require 'shebang'
50
+
51
+ class Greet < Shebang::Command
52
+ command :default
53
+ banner 'Runs an example command.'
54
+ usage '$ foobar.rb [OPTIONS]'
55
+
56
+ o :h, :help , 'Shows this help message' , :method => :help
57
+ o :v, :version, 'Shows the current version', :method => :version
58
+ o :f, :force , 'Forces the command to run'
59
+ o :n, :name , 'A person\'s name', :type => String
60
+
61
+ def version
62
+ puts '0.1'
63
+ exit
64
+ end
65
+
66
+ def index
67
+ puts "Your name is #{@options[:n]}"
68
+ end
69
+ end
70
+
71
+ Shebang.run
72
+
73
+ ## Usage
74
+
75
+ As shown in the example above commands can be created by extending the class
76
+ ``Shebang::Command`` and calling the class method ``command()``. Each command
77
+ required an instance method called ``index()`` to be defined, this method is
78
+ called once OptionParser has been set up and the commandline arguments have
79
+ been parsed:
80
+
81
+ require 'shebang'
82
+
83
+ class Greet < Shebang::Command
84
+ command :default
85
+
86
+ def index
87
+
88
+ end
89
+ end
90
+
91
+ Options can be retrieved using the method ``option()`` which takes either the
92
+ short or long name of an option, in both cases it will result in the same value
93
+ (given the option names belong to the same option):
94
+
95
+ option(:h) === option(:help) # => true
96
+
97
+ Options can be added using the class method ``option()`` or it's alias ``o()``.
98
+ Besides the features offered by OptionParser options can specify a method to
99
+ execute in case that particular option has been specified. This can be done by
100
+ passing the ``:method`` key to the option method:
101
+
102
+ option :f, :foobar, 'Calls a method', :method => :foobar
103
+
104
+ Now whenever the ``-f`` of ``--foobar`` option is set the method ``foobar()``
105
+ will be executed **without** stopping the rest of the command. This means that
106
+ you'll have to manually call ``Kernel.exit()`` if you want to stop the execution
107
+ process if a certain option is specified.
108
+
109
+ Shebang comes with support for defining sub commands. Sub commands are nothing
110
+ more than different methods than the default one. Say you have a class
111
+ ``Git::Submodule``, to run the command itself you'd invoke the default method on
112
+ this class (index by default):
113
+
114
+ cmd = Git::Submodule.new
115
+
116
+ cmd.parse([...])
117
+ cmd.index
118
+
119
+ To show the status of a submodule you'd invoke ``git submodule status``, this
120
+ translates to the following code:
121
+
122
+ cmd = Git::Submodule.new
123
+
124
+ cmd.parse([...])
125
+ cmd.status
126
+
127
+ Shebang takes care of this using ``Shebang.run()``. The first commandline
128
+ argument that does not start with ``-`` (and this isn't an option) is considered
129
+ the command name. If there's another argument that's not a switch following that
130
+ one will be used as the method name. This means that in order to invoke
131
+ ``Git::Submodule#index`` you'd type the following into your terminal:
132
+
133
+ git.rb submodule
134
+
135
+ If you want to invoke a method other than the default one you'd do the
136
+ following:
137
+
138
+ git.rb submodule status
139
+
140
+ In other words, the syntax of a Shebang command is the following:
141
+
142
+ script [COMMAND] [METHOD] [ARGS] [OPTIONS]
143
+
144
+ ## Configuration
145
+
146
+ Various options of Shebang can be configured by modifying the hash
147
+ ``Shebang::Config``. For example, if you want to change the format of all the
148
+ headers in the help message you can do so as following:
149
+
150
+ Shebang::Config[:heading] = "\n== %s:\n"
151
+
152
+ You can also change the name of the default command:
153
+
154
+ Shebang::Config[:default_command] = :my_default_command
@@ -0,0 +1,11 @@
1
+ require File.expand_path('../lib/shebang', __FILE__)
2
+
3
+ module Shebang
4
+ Gemspec = Gem::Specification::load(File.expand_path('../shebang.gemspec', __FILE__))
5
+ end
6
+
7
+ task_dir = File.expand_path('../task', __FILE__)
8
+
9
+ Dir.glob("#{task_dir}/*.rake").each do |f|
10
+ import(f)
11
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path('../../lib/shebang', __FILE__)
2
+
3
+ class Greet < Shebang::Command
4
+ command :default
5
+ banner 'Runs an example command.'
6
+ usage '$ ruby example/basic.rb [OPTIONS]'
7
+
8
+ o :h, :help , 'Shows this help message' , :method => :help
9
+ o :v, :version, 'Shows the current version', :method => :version
10
+ o :f, :force , 'Forces the command to run'
11
+ o :n, :name , 'A person\'s name', :type => String,
12
+ :required => true, :default => 'Shebang'
13
+
14
+ # $ ruby example/basic.rb
15
+ # $ ruby example/basic.rb default
16
+ # $ ruby example/basic.rb default index
17
+ def index
18
+ puts "Your name is #{option(:n)}"
19
+ end
20
+
21
+ # $ ruby example/basic.rb test
22
+ # $ ruby example/basic.rb default test
23
+ def test
24
+ puts 'This is a test method'
25
+ end
26
+
27
+ protected
28
+
29
+ def version
30
+ puts Shebang::Version
31
+ exit
32
+ end
33
+ end
34
+
35
+ Shebang.run
File without changes
@@ -0,0 +1,104 @@
1
+ require 'rubygems'
2
+ require 'optparse'
3
+ require File.expand_path('../shebang/version', __FILE__)
4
+ require File.expand_path('../shebang/command', __FILE__)
5
+ require File.expand_path('../shebang/option' , __FILE__)
6
+
7
+ ##
8
+ # Shebang is a nice wrapper around OptionParser that makes it easier to write
9
+ # commandline executables.
10
+ #
11
+ # @author Yorick Peterse
12
+ # @since 0.1
13
+ #
14
+ module Shebang
15
+ #:nodoc:
16
+ class Error < StandardError; end
17
+
18
+ # Hash containing various configuration options.
19
+ Config = {
20
+ # The name of the default command to invoke when no command is specified.
21
+ :default_command => :default,
22
+
23
+ # The name of the default method to invoke.
24
+ :default_method => :index,
25
+
26
+ # The amount of spaces to insert before each option.
27
+ :indent => ' ',
28
+
29
+ # The format for each header for help topics, options, etc.
30
+ :heading => "\n%s:\n",
31
+
32
+ # When set to true Shebang will raise an exception for errors instead of
33
+ # just printing a message.
34
+ :raise => true
35
+ }
36
+
37
+ # Hash containing the names of all commands and their classes.
38
+ Commands = {}
39
+
40
+ class << self
41
+ ##
42
+ # Runs a command based on the command line arguments. If no command is given
43
+ # this method will try to invoke the default command.
44
+ #
45
+ # @author Yorick Peterse
46
+ # @since 0.1
47
+ # @param [Array] argv Array containing the command line arguments to parse.
48
+ #
49
+ def run(argv = ARGV)
50
+ self.error("No commands have been registered") if Commands.empty?
51
+
52
+ command = Config[:default_command].to_sym
53
+ method = Config[:default_method].to_sym
54
+
55
+ if !argv.empty?
56
+ # Get the command name
57
+ if argv[0][0] != '-' and Commands.key?(argv[0].to_sym)
58
+ command = argv.delete_at(0).to_sym
59
+ end
60
+ end
61
+
62
+ if Commands.key?(command)
63
+ klass = Commands[command].new
64
+
65
+ # Get the method to call.
66
+ if argv[0] and argv[0][0] != '-' and klass.respond_to?(argv[0].to_sym)
67
+ method = argv.delete_at(0).to_sym
68
+ end
69
+
70
+ # Parse the arguments and prepare all the options.
71
+ argv = klass.parse(argv)
72
+
73
+ # Call the method and pass the commandline arguments to it.
74
+ if klass.respond_to?(method)
75
+ if klass.class.instance_method(method).arity != 0
76
+ klass.send(method, argv)
77
+ else
78
+ klass.send(method)
79
+ end
80
+ else
81
+ error("The command #{command} does not have a #{method}() method")
82
+ end
83
+ else
84
+ error("The command #{command} does not exist")
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Raises an exception or prints a regular error message to STDERR based on
90
+ # the :raise configuration option.
91
+ #
92
+ # @author Yorick Peterse
93
+ # @since 0.1
94
+ # @param [String] message The message to display.
95
+ #
96
+ def error(message)
97
+ if Config[:raise] === true
98
+ raise(Error, message)
99
+ else
100
+ abort "\e[0;31mError:\e[0m #{message}"
101
+ end
102
+ end
103
+ end # class << self
104
+ end # Shebang
File without changes
@@ -0,0 +1,274 @@
1
+ module Shebang
2
+ ##
3
+ # Shebang::Command is where the party really starts. By extending this class
4
+ # other classes can become fully fledged commands with their own options,
5
+ # banners, callbacks, and so on. In it's most basic form a command looks like
6
+ # the following:
7
+ #
8
+ # class MyCommand < Shebang::Command
9
+ # command 'my-command'
10
+ #
11
+ # def index
12
+ #
13
+ # end
14
+ # end
15
+ #
16
+ # The class method command() is used to register the class to the specified
17
+ # name, without this Shebang would be unable to call it.
18
+ #
19
+ # Defining options can be done by calling the class method option() or it's
20
+ # alias o():
21
+ #
22
+ # class MyCommand < Shebang::Command
23
+ # command 'my-command'
24
+ #
25
+ # o :h, :help, 'Shows this help message', :method => :help
26
+ #
27
+ # def index
28
+ #
29
+ # end
30
+ # end
31
+ #
32
+ # If you're going to define a help option, and you most likely will, you don't
33
+ # have to manually add a method that shows the message as the Command class
34
+ # already comes with an instance method for this, simply called help().
35
+ #
36
+ # For more information on options see Shebang::Option#initialize().
37
+ #
38
+ # @author Yorick Peterse
39
+ # @since 0.1
40
+ #
41
+ class Command
42
+ # Several methods that become available as class methods once
43
+ # Shebang::Command is extended by another class.
44
+ module ClassMethods
45
+ ##
46
+ # Binds a class to the specified command name.
47
+ #
48
+ # @author Yorick Peterse
49
+ # @since 0.1
50
+ # @param [#to_sym] name The name of the command.
51
+ # @param [Hash] options Hash containing various options for the command.
52
+ # @option options :parent The name of the parent command.
53
+ #
54
+ def command(name, options = {})
55
+ name = name.to_sym
56
+
57
+ if Shebang::Commands.key?(name)
58
+ Shebang.error("The command #{name} has already been registered")
59
+ end
60
+
61
+ Shebang::Commands[name] = self
62
+ end
63
+
64
+ ##
65
+ # Sets the banner for the command, trailing or leading newlines will
66
+ # be removed.
67
+ #
68
+ # @author Yorick Peterse
69
+ # @since 0.1
70
+ # @param [String] text The content of the banner.
71
+ #
72
+ def banner(text)
73
+ @__banner = text.strip
74
+ end
75
+
76
+ ##
77
+ # A small shortcut for defining the syntax of a command. This method is
78
+ # just a shortcut for the following:
79
+ #
80
+ # help('Usage', 'foobar [OPTIONS]'
81
+ #
82
+ # @author Yorick Peterse
83
+ # @since 0.1
84
+ # @param [String] text The content of the usage block.
85
+ #
86
+ def usage(text)
87
+ help('Usage', text)
88
+ end
89
+
90
+ ##
91
+ # Sets a general "help topic" with a custom title and content.
92
+ #
93
+ # @example
94
+ # help('License', 'MIT License')
95
+ #
96
+ # @author Yorick Peterse
97
+ # @since 0.1
98
+ # @param [String] title The title of the topic.
99
+ # @param [String] text The content of the topic.
100
+ #
101
+ def help(title, text)
102
+ @__help_topics ||= {}
103
+ @__help_topics[title] = text.strip
104
+ end
105
+
106
+ ##
107
+ # Creates a new option for a command.
108
+ #
109
+ # @example
110
+ # o :h, :help, 'Shows this help message', :method => :help
111
+ # o :l, :list, 'A list of numbers' , :type => Array
112
+ #
113
+ # @author Yorick Peterse
114
+ # @since 0.1
115
+ # @see Shebang::Option#initialize
116
+ #
117
+ def option(short, long, desc = nil, options = {})
118
+ @__options ||= []
119
+ option = Shebang::Option.new(short, long, desc, options)
120
+
121
+ @__options.push(option)
122
+ end
123
+ alias :o :option
124
+ end # ClassMethods
125
+
126
+ ##
127
+ # Modifies the class that inherits this class so that the module
128
+ # Shebang::Comand::ClassMethods extends the class.
129
+ #
130
+ # @author Yorick Peterse
131
+ # @since 09-08-2011
132
+ # @param [Class] by The class that inherits from Shebang::Command.
133
+ #
134
+ def self.inherited(by)
135
+ by.extend(Shebang::Command::ClassMethods)
136
+ end
137
+
138
+ ##
139
+ # Creates a new instance of the command and sets up OptionParser.
140
+ #
141
+ # @author Yorick Peterse
142
+ # @since 0.1
143
+ #
144
+ def initialize
145
+ @option_parser = OptionParser.new do |opt|
146
+ opt.banner = banner
147
+ opt.summary_indent = Shebang::Config[:indent]
148
+
149
+ # Process each help topic
150
+ help_topics.each do |title, text|
151
+ opt.separator "#{Shebang::Config[:heading]}#{
152
+ Shebang::Config[:indent]}#{text}" % title
153
+ end
154
+
155
+ opt.separator "#{Shebang::Config[:heading]}" % 'Options'
156
+
157
+ # Add all the options
158
+ options.each do |option|
159
+ opt.on(*option.option_parser) do |value|
160
+ option.value = value
161
+
162
+ # Run a method?
163
+ if !option.options[:method].nil? \
164
+ and respond_to?(option.options[:method])
165
+ # Pass the value to the method?
166
+ if self.class.instance_method(option.options[:method]).arity != 0
167
+ send(option.options[:method], value)
168
+ else
169
+ send(option.options[:method])
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ ##
178
+ # Parses the command line arguments using OptionParser.
179
+ #
180
+ # @author Yorick Peterse
181
+ # @since 0.1
182
+ # @param [Array] argv Array containing the command line arguments to parse.
183
+ # @return [Array] argv Array containing all the command line arguments after
184
+ # it has been processed.
185
+ #
186
+ def parse(argv = [])
187
+ @option_parser.parse!(argv)
188
+
189
+ options.each do |option|
190
+ if option.required? and !option.has_value?
191
+ Shebang.error("The -#{option.short} option is required")
192
+ end
193
+ end
194
+
195
+ return argv
196
+ end
197
+
198
+ ##
199
+ # Returns the banner of the current class.
200
+ #
201
+ # @author Yorick Peterse
202
+ # @since 0.1
203
+ # @return [String]
204
+ #
205
+ def banner
206
+ self.class.instance_variable_get(:@__banner)
207
+ end
208
+
209
+ ##
210
+ # Returns all help topics for the current class.
211
+ #
212
+ # @author Yorick Peterse
213
+ # @since 0.1
214
+ # @return [Hash]
215
+ #
216
+ def help_topics
217
+ self.class.instance_variable_get(:@__help_topics) || {}
218
+ end
219
+
220
+ ##
221
+ # Returns an array of all the options for the current class.
222
+ #
223
+ # @author Yorick Peterse
224
+ # @since 0.1
225
+ # @return [Array]
226
+ #
227
+ def options
228
+ self.class.instance_variable_get(:@__options) || []
229
+ end
230
+
231
+ ##
232
+ # Method that is called whenever a command has to be executed.
233
+ #
234
+ # @author Yorick Peterse
235
+ # @since 0.1
236
+ #
237
+ def index
238
+ raise(NotImplementedError, "You need to define your own index() method")
239
+ end
240
+
241
+ ##
242
+ # Returns the value of a given option. The option can be specified using
243
+ # either the short or long name.
244
+ #
245
+ # @example
246
+ # puts "Hello #{option(:name)}
247
+ #
248
+ # @author Yorick Peterse
249
+ # @since 0.1
250
+ # @param [#to_sym] opt The name of the option.
251
+ # @return [Mixed]
252
+ #
253
+ def option(opt)
254
+ opt = opt.to_sym
255
+
256
+ options.each do |op|
257
+ if op.short === opt or op.long === opt
258
+ return op.value
259
+ end
260
+ end
261
+ end
262
+
263
+ ##
264
+ # Shows the help message for the current class.
265
+ #
266
+ # @author Yorick Peterse
267
+ # @since 0.1
268
+ #
269
+ def help
270
+ puts @option_parser
271
+ exit
272
+ end
273
+ end # Command
274
+ end # Shebang
@@ -0,0 +1,95 @@
1
+ module Shebang
2
+ ##
3
+ # Class that represents a single option that's passed to OptionParser.
4
+ #
5
+ # @author Yorick Peterse
6
+ # @since 0.1
7
+ #
8
+ class Option
9
+ attr_reader :short, :long, :description, :options
10
+ attr_accessor :value
11
+
12
+ ##
13
+ # Creates a new instance of the Option class.
14
+ #
15
+ # @author Yorick Peterse
16
+ # @since 0.1
17
+ # @param [#to_sym] short The short option name such as :h.
18
+ # @param [#to_sym] long The long option name such as :help.
19
+ # @param [String] desc The description of the option.
20
+ # @param [Hash] options Hash containing various configuration options for
21
+ # the OptionParser option.
22
+ # @option options :type The type of value for the option, set to TrueClass
23
+ # by default.
24
+ # @option options :key The key to use to indicate a value whenever the type
25
+ # of an option is something else than TrueClass or FalseClass. This option
26
+ # is set to "VALUE" by default.
27
+ # @option options :method A symbol that refers to a method that should be
28
+ # called whenever the option is specified.
29
+ # @option options :required Indicates that the option has to be specified.
30
+ # @option options :default The default value of the option.
31
+ #
32
+ def initialize(short, long, desc = nil, options = {})
33
+ @short, @long = short.to_sym, long.to_sym
34
+ @description = desc
35
+ @options = {
36
+ :type => TrueClass,
37
+ :key => 'VALUE',
38
+ :method => nil,
39
+ :required => false,
40
+ :default => nil
41
+ }.merge(options)
42
+
43
+ @value = @options[:default]
44
+ end
45
+
46
+ ##
47
+ # Builds an array containing all the required parameters for
48
+ # OptionParser#on().
49
+ #
50
+ # @author Yorick Peterse
51
+ # @since 0.1
52
+ # @return [Array]
53
+ #
54
+ def option_parser
55
+ params = ["-#{@short}", "--#{@long}", nil, @options[:type]]
56
+
57
+ if !@description.nil? and !@description.empty?
58
+ params[2] = @description
59
+ end
60
+
61
+ # Set the correct format for the long/short option based on the type.
62
+ if ![TrueClass, FalseClass].include?(@options[:type])
63
+ params[1] += " #{@options[:key]}"
64
+ end
65
+
66
+ return params
67
+ end
68
+
69
+ ##
70
+ # Checks if the value of an option is not nil and not empty.
71
+ #
72
+ # @author Yorick Peterse
73
+ # @since 0.1
74
+ # @return [TrueClass|FalseClass]
75
+ #
76
+ def has_value?
77
+ if !@value.nil? and !@value.empty?
78
+ return true
79
+ else
80
+ return false
81
+ end
82
+ end
83
+
84
+ ##
85
+ # Indicates whether or not the option requires a value.
86
+ #
87
+ # @author Yorick Peterse
88
+ # @since 0.1
89
+ # @return [TrueClass|FalseClass]
90
+ #
91
+ def required?
92
+ return @options[:required]
93
+ end
94
+ end # Option
95
+ end # Shebang
@@ -0,0 +1,39 @@
1
+ #:nodoc:
2
+ module Bacon
3
+ #:nodoc:
4
+ module ColorOutput
5
+ #:nodoc:
6
+ def handle_specification(name)
7
+ puts spaces + name
8
+ yield
9
+ puts if Counter[:context_depth] == 1
10
+ end
11
+
12
+ #:nodoc:
13
+ def handle_requirement(description)
14
+ error = yield
15
+
16
+ if !error.empty?
17
+ puts "#{spaces} \e[31m- #{description} [FAILED]\e[0m"
18
+ else
19
+ puts "#{spaces} \e[32m- #{description}\e[0m"
20
+ end
21
+ end
22
+
23
+ #:nodoc:
24
+ def handle_summary
25
+ print ErrorLog if Backtraces
26
+ puts "%d specifications (%d requirements), %d failures, %d errors" %
27
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
28
+ end
29
+
30
+ #:nodoc:
31
+ def spaces
32
+ if Counter[:context_depth] === 0
33
+ Counter[:context_depth] = 1
34
+ end
35
+
36
+ return ' ' * (Counter[:context_depth] - 1)
37
+ end
38
+ end # ColorOutput
39
+ end # Bacon
@@ -0,0 +1,65 @@
1
+ require 'bacon'
2
+ require 'stringio'
3
+ require File.expand_path('../bacon/color_output', __FILE__)
4
+
5
+ Bacon.extend(Bacon::ColorOutput)
6
+ Bacon.summary_on_exit
7
+
8
+ ##
9
+ # Runs the block in a new thread and redirects $stdout and $stderr. The output
10
+ # normally stored in these variables is stored in an instance of StringIO which
11
+ # is returned as a hash.
12
+ #
13
+ # @example
14
+ # out = catch_output do
15
+ # puts 'hello'
16
+ # end
17
+ #
18
+ # puts out # => {:stdout => "hello\n", :stderr => ""}
19
+ #
20
+ # @author Yorick Peterse
21
+ # @return [Hash]
22
+ #
23
+ def catch_output
24
+ data = {
25
+ :stdout => nil,
26
+ :stderr => nil
27
+ }
28
+
29
+ Thread.new do
30
+ $stdout, $stderr = StringIO.new, StringIO.new
31
+
32
+ yield
33
+
34
+ $stdout.rewind
35
+ $stderr.rewind
36
+
37
+ data[:stdout], data[:stderr] = $stdout.read, $stderr.read
38
+
39
+ $stdout, $stderr = STDOUT, STDERR
40
+ end.join
41
+
42
+ return data
43
+ end
44
+
45
+ ##
46
+ # Allows developers to create stubbed objects similar to Mocha's stub() method.
47
+ #
48
+ # @example
49
+ # obj = stub(:language => 'Ruby')
50
+ # puts obj.language # => "Ruby"
51
+ #
52
+ # @author Yorick Peterse
53
+ # @param [Hash] attributes A hash containing all the attributes to set and
54
+ # their values.
55
+ # @return [Class]
56
+ #
57
+ def stub(attributes)
58
+ obj = Struct.new(*attributes.keys).new
59
+
60
+ attributes.each do |k, v|
61
+ obj.send("#{k}=", v)
62
+ end
63
+
64
+ return obj
65
+ end
@@ -0,0 +1,4 @@
1
+ module Shebang
2
+ #:nodoc:
3
+ Version = '0.1'
4
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../lib/shebang/version', __FILE__)
2
+
3
+ path = File.expand_path('../', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'shebang'
7
+ s.version = Shebang::Version
8
+ s.date = '09-08-2011'
9
+ s.authors = ['Yorick Peterse']
10
+ s.email = 'yorickpeterse@gmail.com'
11
+ s.summary = 'Shebang is a nice wrapper around OptionParser that makes it
12
+ easier to write commandline executables.'
13
+ s.homepage = 'https://github.com/yorickpeterse/shebang'
14
+ s.description = s.summary
15
+ s.files = `cd #{path}; git ls-files`.split("\n").sort
16
+ s.has_rdoc = 'yard'
17
+
18
+ s.add_development_dependency('rake' , ['~> 0.9.2'])
19
+ s.add_development_dependency('yard' , ['~> 0.7.2'])
20
+ s.add_development_dependency('bacon', ['~> 1.1.0'])
21
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ class SpecCommand < Shebang::Command
2
+ command :default
3
+ banner 'The default command.'
4
+ usage 'shebang.rb [COMMAND] [OPTIONS]'
5
+
6
+ o :v, :version, 'Shows the current version', :method => :version
7
+
8
+ def index
9
+ puts 'index method'
10
+ end
11
+
12
+ def test
13
+ puts 'test method'
14
+ end
15
+
16
+ protected
17
+
18
+ def version
19
+ puts '0.1'
20
+ end
21
+ end # SpecCommand
@@ -0,0 +1,2 @@
1
+ require File.expand_path('../../lib/shebang', __FILE__)
2
+ require File.expand_path('../../lib/shebang/spec/helper', __FILE__)
@@ -0,0 +1,39 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+ require File.expand_path('../../fixtures/command', __FILE__)
3
+
4
+ describe('Shebang::Command') do
5
+ it('The name should be registered') do
6
+ Shebang::Commands[:default].should == SpecCommand
7
+ end
8
+
9
+ it('The banner should be set') do
10
+ Shebang::Commands[:default].instance_variable_get(:@__banner).should \
11
+ === 'The default command.'
12
+
13
+ Shebang::Commands[:default].new.banner.should === 'The default command.'
14
+ end
15
+
16
+ it('A help topic should be set') do
17
+ Shebang::Commands[:default].instance_variable_get(
18
+ :@__help_topics
19
+ )['Usage'].should === 'shebang.rb [COMMAND] [OPTIONS]'
20
+
21
+ Shebang::Commands[:default].new.help_topics['Usage'].should \
22
+ === 'shebang.rb [COMMAND] [OPTIONS]'
23
+ end
24
+
25
+ it('An option should be set') do
26
+ option = Shebang::Commands[:default].instance_variable_get(:@__options)[0]
27
+
28
+ option.short.should === :v
29
+ option.long.should === :version
30
+ option.description.should === 'Shows the current version'
31
+ option.options[:method].should === :version
32
+ option.value = '0.1'
33
+
34
+ Shebang::Commands[:default].new.options[0].short.should === option.short
35
+
36
+ Shebang::Commands[:default].new.option(:v).should === option.value
37
+ Shebang::Commands[:default].new.option(:version).should === option.value
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ describe('Shebang::Option') do
4
+ it('Create a new option') do
5
+ option = Shebang::Option.new(:h, :help, 'help message', :method => :test)
6
+
7
+ option.short.should === :h
8
+ option.long.should === :help
9
+ option.description.should === 'help message'
10
+
11
+ option.options[:method].should === :test
12
+ option.options[:type].should == TrueClass
13
+
14
+ option.required?.should === false
15
+ option.has_value?.should === false
16
+ end
17
+
18
+ it('Convert to OptionParser arguments') do
19
+ option = Shebang::Option.new(:h, :help, 'help message', :method => :test)
20
+
21
+ option.option_parser.should === ['-h', '--help', 'help message', TrueClass]
22
+ end
23
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+ require File.expand_path('../../fixtures/command', __FILE__)
3
+
4
+ module Kernel
5
+ def abort(*args)
6
+ $stderr.puts(*args)
7
+ end
8
+
9
+ def exit(*args); end
10
+ end
11
+
12
+ describe('Shebang') do
13
+ it('Raise an error message') do
14
+ should.raise?(Shebang::Error) do
15
+ Shebang.error('test')
16
+ end
17
+ end
18
+
19
+ it('Display an error message') do
20
+ Shebang::Config[:raise] = false
21
+
22
+ output = catch_output do
23
+ Shebang.error('test')
24
+ end
25
+
26
+ output[:stderr].include?('test').should === true
27
+ end
28
+
29
+ it('Invoke the default command') do
30
+ [[], ['default'], ['default', 'index']].each do |argv|
31
+ output = catch_output do
32
+ Shebang.run(argv)
33
+ end
34
+
35
+ output[:stdout].strip.should === 'index method'
36
+ end
37
+ end
38
+
39
+ it('Invoke the default command with an alternative method') do
40
+ [['test'], ['default', 'test']].each do |argv|
41
+ output = catch_output do
42
+ Shebang.run(argv)
43
+ end
44
+
45
+ output[:stdout].strip.should === 'test method'
46
+ end
47
+ end
48
+
49
+ it('Show a help message') do
50
+ output = catch_output do
51
+ Shebang.run(['--help'])
52
+ end
53
+
54
+ output[:stdout].include?('The default command').should === true
55
+ output[:stdout].include?('shebang.rb [COMMAND] [OPTIONS]').should === true
56
+ output[:stdout].include?('Options').should === true
57
+ end
58
+
59
+ it('Shows the current version') do
60
+ output = catch_output do
61
+ Shebang.run(['--version'])
62
+ end
63
+
64
+ output[:stdout].include?('0.1').should === true
65
+ end
66
+ end # describe
@@ -0,0 +1,33 @@
1
+ # Task group used for building various elements such as the Gem and the
2
+ # documentation.
3
+ namespace :build do
4
+ desc 'Builds the documentation using YARD'
5
+ task :doc do
6
+ gem_path = File.expand_path('../../', __FILE__)
7
+ command = "yard doc #{gem_path}/lib -m markdown -M rdiscount -o #{gem_path}/doc "
8
+ command += "-r #{gem_path}/README.md --private --protected"
9
+
10
+ sh(command)
11
+ end
12
+
13
+ desc 'Builds a new Gem'
14
+ task :gem do
15
+ gem_path = File.expand_path('../../', __FILE__)
16
+ gemspec_path = File.join(
17
+ gem_path,
18
+ "#{Shebang::Gemspec.name}-#{Shebang::Gemspec.version.version}.gem"
19
+ )
20
+
21
+ pkg_path = File.join(
22
+ gem_path,
23
+ 'pkg',
24
+ "#{Shebang::Gemspec.name}-#{Shebang::Gemspec.version.version}.gem"
25
+ )
26
+
27
+ # Build and install the gem
28
+ sh('gem', 'build' , File.join(gem_path, 'shebang.gemspec'))
29
+ sh('mv' , gemspec_path, pkg_path)
30
+ sh('gem', 'install' , pkg_path)
31
+ end
32
+ end # namespace :build
33
+
@@ -0,0 +1,6 @@
1
+ desc 'Runs all the tests'
2
+ task :test do
3
+ Dir.glob(File.expand_path('../../spec/shebang/*.rb', __FILE__)).each do |f|
4
+ require(f)
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shebang
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: "0.1"
6
+ platform: ruby
7
+ authors:
8
+ - Yorick Peterse
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-09 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rake
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 0.9.2
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 0.7.2
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: bacon
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 1.1.0
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: Shebang is a nice wrapper around OptionParser that makes it easier to write commandline executables.
50
+ email: yorickpeterse@gmail.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gems
59
+ - .gitignore
60
+ - .rvmrc
61
+ - .travis.yml
62
+ - LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - example/basic.rb
66
+ - lib/.gitkeep
67
+ - lib/shebang.rb
68
+ - lib/shebang/.gitkeep
69
+ - lib/shebang/command.rb
70
+ - lib/shebang/option.rb
71
+ - lib/shebang/spec/bacon/color_output.rb
72
+ - lib/shebang/spec/helper.rb
73
+ - lib/shebang/version.rb
74
+ - pkg/.gitkeep
75
+ - shebang.gemspec
76
+ - spec/.gitkeep
77
+ - spec/fixtures/command.rb
78
+ - spec/helper.rb
79
+ - spec/shebang/command.rb
80
+ - spec/shebang/option.rb
81
+ - spec/shebang/shebang.rb
82
+ - task/build.rake
83
+ - task/test.rake
84
+ has_rdoc: yard
85
+ homepage: https://github.com/yorickpeterse/shebang
86
+ licenses: []
87
+
88
+ post_install_message:
89
+ rdoc_options: []
90
+
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.6.2
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Shebang is a nice wrapper around OptionParser that makes it easier to write commandline executables.
112
+ test_files: []
113
+