shebang 0.1
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.
- data/.gems +3 -0
- data/.gitignore +10 -0
- data/.rvmrc +1 -0
- data/.travis.yml +16 -0
- data/LICENSE +19 -0
- data/README.md +154 -0
- data/Rakefile +11 -0
- data/example/basic.rb +35 -0
- data/lib/.gitkeep +0 -0
- data/lib/shebang.rb +104 -0
- data/lib/shebang/.gitkeep +0 -0
- data/lib/shebang/command.rb +274 -0
- data/lib/shebang/option.rb +95 -0
- data/lib/shebang/spec/bacon/color_output.rb +39 -0
- data/lib/shebang/spec/helper.rb +65 -0
- data/lib/shebang/version.rb +4 -0
- data/pkg/.gitkeep +0 -0
- data/shebang.gemspec +21 -0
- data/spec/.gitkeep +0 -0
- data/spec/fixtures/command.rb +21 -0
- data/spec/helper.rb +2 -0
- data/spec/shebang/command.rb +39 -0
- data/spec/shebang/option.rb +23 -0
- data/spec/shebang/shebang.rb +66 -0
- data/task/build.rake +33 -0
- data/task/test.rake +6 -0
- metadata +113 -0
data/.gems
ADDED
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use --create 1.9.2@shebang
|
data/.travis.yml
ADDED
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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/example/basic.rb
ADDED
@@ -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
|
data/lib/.gitkeep
ADDED
File without changes
|
data/lib/shebang.rb
ADDED
@@ -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
|
data/pkg/.gitkeep
ADDED
File without changes
|
data/shebang.gemspec
ADDED
@@ -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
|
data/spec/.gitkeep
ADDED
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
|
data/spec/helper.rb
ADDED
@@ -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
|
data/task/build.rake
ADDED
@@ -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
|
+
|
data/task/test.rake
ADDED
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
|
+
|