Use modules in Ruby implementation.
Options, arguments and the help text are now also avaialble as EasyOptions.options, EasyOptions.arguments and EasyOptions.documentation. The global variables $options, $arguments and $documentation are still present.
This commit is contained in:
parent
02ac5bfeba
commit
6b1b678e2e
13
README.md
13
README.md
|
@ -41,25 +41,26 @@ The above comments work both as source code documentation and as help text, as w
|
|||
gem install easyoptions
|
||||
```
|
||||
|
||||
After writing your documentation, you simply require this script. Then all command line options will get parsed into the `$options` hash, as described above. You can then check their values for reacting to them. All regular arguments will get stored into the `$arguments` array. Here is an example for parsing the comments above:
|
||||
After writing your documentation, you simply require this script. Then all command line options will get parsed into the `EasyOptions.options` hash, as described above. You can then check their values for reacting to them. All regular arguments will get stored into the `EasyOptions.arguments` array. Both the options and arguments can be accessed at once with `EasyOptions.all`. Here is an example for parsing the comments above:
|
||||
|
||||
```ruby
|
||||
require "easyoptions"
|
||||
options, arguments = EasyOptions.all
|
||||
|
||||
# Boolean options
|
||||
puts "Option specified: --some-option" if $options[:some_option]
|
||||
puts "Option specified: --some-boolean" if $options[:some_boolean]
|
||||
puts "Option specified: --some-option" if options[:some_option]
|
||||
puts "Option specified: --some-boolean" if options[:some_boolean]
|
||||
|
||||
# Parameter option
|
||||
value = $options[:some_value]
|
||||
value = options[:some_value]
|
||||
if value
|
||||
type = value.is_a?(Fixnum)? "number" : "string"
|
||||
puts "Option specified: --some-value is #{value} (a #{type})"
|
||||
end
|
||||
|
||||
# Arguments
|
||||
exit if $arguments.empty?
|
||||
$arguments.each do |argument|
|
||||
exit if arguments.empty?
|
||||
arguments.each do |argument|
|
||||
puts "Argument specified: #{argument}"
|
||||
end
|
||||
```
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Gem::Specification.new do |spec|
|
||||
spec.name = "easyoptions"
|
||||
spec.version = "2014.9.15"
|
||||
spec.version = "2014.12.2"
|
||||
spec.license = "GPLv2"
|
||||
spec.author = "Renato Silva"
|
||||
spec.email = "br.renatosilva@gmail.com"
|
||||
|
|
367
easyoptions.rb
367
easyoptions.rb
|
@ -2,7 +2,7 @@
|
|||
# Encoding: ISO-8859-1
|
||||
|
||||
##
|
||||
## EasyOptions 2014.9.15
|
||||
## EasyOptions 2014.12.2
|
||||
## Copyright (c) 2013, 2014 Renato Silva
|
||||
## GNU GPLv2 licensed
|
||||
##
|
||||
|
@ -22,22 +22,22 @@
|
|||
## ## it shows this double-hash documentation.
|
||||
## ##
|
||||
## ## -o, --option This option will get stored as true value
|
||||
## ## under $options[:option]. Long version is
|
||||
## ## mandatory, and can be specified before or
|
||||
## ## after short version.
|
||||
## ## under EasyOptions.options[:option]. Long
|
||||
## ## version is mandatory, and can be specified
|
||||
## ### before or after short version.
|
||||
## ##
|
||||
## ## --some-boolean This will get stored as true value under
|
||||
## ## $options[:some_boolean].
|
||||
## ## EasyOptions.options[:some_boolean].
|
||||
## ##
|
||||
## ## --some-value=VALUE This is going to store the VALUE specified
|
||||
## ## under $options[:some_value]. The equal
|
||||
## ## sign is optional and can be replaced with
|
||||
## ## blank space when running the target
|
||||
## ## script. If VALUE is composed of digits, it
|
||||
## ## will be converted into an integer,
|
||||
## ## otherwise it will get stored as a string.
|
||||
## ## Short version is not available in this
|
||||
## ## format.
|
||||
## ## under EasyOptions.options[:some_value].
|
||||
## ## The equal sign is optional and can be
|
||||
## ## replaced with blank space when running the
|
||||
## ## target script. If VALUE is composed of
|
||||
## ## digits, it will be converted into an
|
||||
## ## integer, otherwise it will get stored as a
|
||||
## ## string. Short version is not available in
|
||||
## ## this format.
|
||||
##
|
||||
## The above comments work both as source code documentation and as help
|
||||
## text, as well as define the options supported by your script. There is no
|
||||
|
@ -45,9 +45,9 @@
|
|||
## replaced with the actual script name.
|
||||
##
|
||||
## After writing your documentation, you simply require this script. Then all
|
||||
## command line options will get parsed into the $options hash, as described
|
||||
## above. You can then check their values for reacting to them. All regular
|
||||
## arguments will get stored into the $arguments array.
|
||||
## command line options will get parsed into the EasyOptions.options hash, as
|
||||
## described above. You can then check their values for reacting to them. All
|
||||
## regular arguments will get stored into the EasyOptions.arguments array.
|
||||
##
|
||||
## In fact, this script is an example of itself. You are seeing this help
|
||||
## message either because you are reading the source code, or you have called
|
||||
|
@ -72,159 +72,196 @@
|
|||
## arguments
|
||||
##
|
||||
|
||||
class Option
|
||||
def initialize(long_version, short_version, boolean=true)
|
||||
raise ArgumentError.new("Long version is mandatory") if not long_version or long_version.length < 2
|
||||
@short = short_version.to_sym if short_version
|
||||
@long = long_version.to_s.gsub("-", "_").to_sym
|
||||
@boolean = boolean
|
||||
module EasyOptions
|
||||
|
||||
class Option
|
||||
def initialize(long_version, short_version, boolean=true)
|
||||
raise ArgumentError.new("Long version is mandatory") if not long_version or long_version.length < 2
|
||||
@short = short_version.to_sym if short_version
|
||||
@long = long_version.to_s.gsub("-", "_").to_sym
|
||||
@boolean = boolean
|
||||
end
|
||||
def to_s
|
||||
"--#{long_dashed}"
|
||||
end
|
||||
def in?(string)
|
||||
string =~ /^--#{long_dashed}$/ or (@short and string =~ /^-#{@short}$/)
|
||||
end
|
||||
def in_with_value?(string)
|
||||
string =~ /^--#{long_dashed}=.*$/
|
||||
end
|
||||
def long_dashed
|
||||
@long.to_s.gsub("_", "-")
|
||||
end
|
||||
attr_accessor :short
|
||||
attr_accessor :long
|
||||
attr_accessor :boolean
|
||||
end
|
||||
def to_s
|
||||
"--#{long_dashed}"
|
||||
|
||||
class Parser
|
||||
def initialize
|
||||
@known_options = [Option.new(:help, :h)]
|
||||
@documentation = parse_doc
|
||||
@arguments = []
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def parse_doc
|
||||
begin
|
||||
doc = File.readlines($0)
|
||||
rescue Errno::ENOENT
|
||||
exit false
|
||||
end
|
||||
doc = doc.find_all do |line|
|
||||
line =~ /^##[^#]*/
|
||||
end
|
||||
doc = doc.map do |line|
|
||||
line.strip!
|
||||
line.sub!(/^## ?/, "")
|
||||
line.gsub!(/@script.name/, File.basename($0))
|
||||
line.gsub(/@#/, "@")
|
||||
end
|
||||
end
|
||||
|
||||
def parse
|
||||
# Parse known options from documentation
|
||||
@documentation.map do |line|
|
||||
line = line.strip
|
||||
case line
|
||||
when /^-h, --help.*/ then next
|
||||
when /^--help, -h.*/ then next
|
||||
when /^-.*, --.*/ then line = line.split(/(^-|,\s--|\s)/); @known_options << Option.new(line[4], line[2])
|
||||
when /^--.*, -.*/ then line = line.split(/(--|,\s-|\s)/); @known_options << Option.new(line[2], line[4])
|
||||
when /^--.*=.*/ then line = line.split(/(--|=|\s)/); @known_options << Option.new(line[2], nil, false)
|
||||
when /^--.* .*/ then line = line.split(/(--|\s)/); @known_options << Option.new(line[2], nil)
|
||||
end
|
||||
end
|
||||
|
||||
# Format arguments input
|
||||
raw_arguments = ARGV.map do |argument|
|
||||
if argument =~ /^-[^-].*$/i then
|
||||
argument.split("")[1..-1].map { |char| "-#{char}" }
|
||||
else
|
||||
argument
|
||||
end
|
||||
end.flatten
|
||||
|
||||
# Parse the provided options
|
||||
raw_arguments.each_with_index do |argument, index|
|
||||
unknown_option = true
|
||||
@known_options.each do |known_option|
|
||||
|
||||
# Boolean option
|
||||
if known_option.in?(argument) and known_option.boolean then
|
||||
@options[known_option.long] = true
|
||||
unknown_option = false
|
||||
break
|
||||
|
||||
# Option with value in next parameter
|
||||
elsif known_option.in?(argument) and not known_option.boolean then
|
||||
value = raw_arguments[index + 1]
|
||||
Parser.finish("you must specify a value for #{known_option}") if not value or value.start_with?("-")
|
||||
value = value.to_i if value =~ /^[0-9]+$/
|
||||
@options[known_option.long] = value
|
||||
unknown_option = false
|
||||
break
|
||||
|
||||
# Option with value after equal sign
|
||||
elsif known_option.in_with_value?(argument) and not known_option.boolean then
|
||||
value = argument.split("=")[1]
|
||||
value = value.to_i if value =~ /^[0-9]+$/
|
||||
@options[known_option.long] = value
|
||||
unknown_option = false
|
||||
break
|
||||
|
||||
# Long option with unnecessary value
|
||||
elsif known_option.in_with_value?(argument) and known_option.boolean then
|
||||
value = argument.split("=")[1]
|
||||
Parser.finish("#{known_option} does not accept a value (you specified \"#{value}\")")
|
||||
end
|
||||
end
|
||||
|
||||
# Unrecognized option
|
||||
Parser.finish("unrecognized option \"#{argument}\"") if unknown_option and argument.start_with?("-")
|
||||
end
|
||||
|
||||
# Help option
|
||||
if @options[:help]
|
||||
if BashOutput then
|
||||
print "printf '"
|
||||
puts @documentation
|
||||
puts "'"
|
||||
puts "exit"
|
||||
else
|
||||
puts @documentation
|
||||
end
|
||||
exit -1
|
||||
end
|
||||
|
||||
# Regular arguments
|
||||
next_is_value = false
|
||||
raw_arguments.each do |argument|
|
||||
if argument.start_with?("-") then
|
||||
known_option = @known_options.find { |known_option| known_option.in?(argument) }
|
||||
next_is_value = (known_option and not known_option.boolean)
|
||||
else
|
||||
arguments << argument if not next_is_value
|
||||
next_is_value = false
|
||||
end
|
||||
end
|
||||
|
||||
# Bash support
|
||||
if BashOutput then
|
||||
@options.keys.each do |name|
|
||||
puts "#{name}=\"#{@options[name].to_s.sub("true", "yes")}\""
|
||||
end
|
||||
puts "unset arguments"
|
||||
arguments.each do |argument|
|
||||
puts "arguments+=(\"#{argument}\")"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.finish(error)
|
||||
$stderr.puts "Error: #{error}."
|
||||
$stderr.puts "See --help for usage and options."
|
||||
puts "exit 1" if BashOutput
|
||||
exit false
|
||||
end
|
||||
|
||||
def self.check_bash_output
|
||||
$0 = ENV["from"] || $0
|
||||
$0 == ENV["from"]
|
||||
end
|
||||
|
||||
BashOutput = check_bash_output
|
||||
attr_accessor :documentation
|
||||
attr_accessor :arguments
|
||||
attr_accessor :options
|
||||
end
|
||||
def in?(string)
|
||||
string =~ /^--#{long_dashed}$/ or (@short and string =~ /^-#{@short}$/)
|
||||
end
|
||||
def in_with_value?(string)
|
||||
string =~ /^--#{long_dashed}=.*$/
|
||||
end
|
||||
def long_dashed
|
||||
@long.to_s.gsub("_", "-")
|
||||
end
|
||||
attr_accessor :short
|
||||
attr_accessor :long
|
||||
attr_accessor :boolean
|
||||
end
|
||||
|
||||
def finish(error)
|
||||
$stderr.puts "Error: #{error}."
|
||||
$stderr.puts "See --help for usage and options."
|
||||
puts "exit 1" if BashOutput
|
||||
exit false
|
||||
end
|
||||
|
||||
def parse_doc
|
||||
begin
|
||||
doc = File.readlines($0)
|
||||
rescue Errno::ENOENT
|
||||
exit false
|
||||
end
|
||||
doc = doc.find_all do |line|
|
||||
line =~ /^##[^#]*/
|
||||
end
|
||||
doc = doc.map do |line|
|
||||
line.strip!
|
||||
line.sub!(/^## ?/, "")
|
||||
line.gsub!(/@script.name/, File.basename($0))
|
||||
line.gsub(/@#/, "@")
|
||||
end
|
||||
end
|
||||
|
||||
def check_bash_output
|
||||
$0 = ENV["from"] || $0
|
||||
$0 == ENV["from"]
|
||||
end
|
||||
|
||||
# Initialization
|
||||
known_options = [ Option.new(:help, :h)]
|
||||
BashOutput = check_bash_output
|
||||
$documentation = parse_doc
|
||||
$arguments = []
|
||||
$options = {}
|
||||
|
||||
# Parse known options from documentation
|
||||
$documentation.map do |line|
|
||||
line = line.strip
|
||||
case line
|
||||
when /^-h, --help.*/ then next
|
||||
when /^--help, -h.*/ then next
|
||||
when /^-.*, --.*/ then line = line.split(/(^-|,\s--|\s)/); known_options << Option.new(line[4], line[2])
|
||||
when /^--.*, -.*/ then line = line.split(/(--|,\s-|\s)/); known_options << Option.new(line[2], line[4])
|
||||
when /^--.*=.*/ then line = line.split(/(--|=|\s)/); known_options << Option.new(line[2], nil, false)
|
||||
when /^--.* .*/ then line = line.split(/(--|\s)/); known_options << Option.new(line[2], nil)
|
||||
end
|
||||
end
|
||||
|
||||
# Format arguments input
|
||||
arguments = ARGV.map do |argument|
|
||||
if argument =~ /^-[^-].*$/i then
|
||||
argument.split("")[1..-1].map { |char| "-#{char}" }
|
||||
else
|
||||
argument
|
||||
end
|
||||
end.flatten
|
||||
|
||||
# Parse the provided options
|
||||
arguments.each_with_index do |argument, index|
|
||||
unknown_option = true
|
||||
known_options.each do |known_option|
|
||||
|
||||
# Boolean option
|
||||
if known_option.in?(argument) and known_option.boolean then
|
||||
$options[known_option.long] = true
|
||||
unknown_option = false
|
||||
break
|
||||
|
||||
# Option with value in next parameter
|
||||
elsif known_option.in?(argument) and not known_option.boolean then
|
||||
value = arguments[index + 1]
|
||||
finish("you must specify a value for #{known_option}") if not value or value.start_with?("-")
|
||||
value = value.to_i if value =~ /^[0-9]+$/
|
||||
$options[known_option.long] = value
|
||||
unknown_option = false
|
||||
break
|
||||
|
||||
# Option with value after equal sign
|
||||
elsif known_option.in_with_value?(argument) and not known_option.boolean then
|
||||
value = argument.split("=")[1]
|
||||
value = value.to_i if value =~ /^[0-9]+$/
|
||||
$options[known_option.long] = value
|
||||
unknown_option = false
|
||||
break
|
||||
|
||||
# Long option with unnecessary value
|
||||
elsif known_option.in_with_value?(argument) and known_option.boolean then
|
||||
value = argument.split("=")[1]
|
||||
finish("#{known_option} does not accept a value (you specified \"#{value}\")")
|
||||
class << self
|
||||
@@parser = Parser.new
|
||||
@@parser.parse
|
||||
def options
|
||||
@@parser.options
|
||||
end
|
||||
def arguments
|
||||
@@parser.arguments
|
||||
end
|
||||
def documentation
|
||||
@@parser.documentation
|
||||
end
|
||||
def all
|
||||
[options, arguments, documentation]
|
||||
end
|
||||
def finish(error)
|
||||
Parser.finish(error)
|
||||
end
|
||||
end
|
||||
|
||||
# Unrecognized option
|
||||
finish("unrecognized option \"#{argument}\"") if unknown_option and argument.start_with?("-")
|
||||
end
|
||||
|
||||
# Help option
|
||||
if $options[:help]
|
||||
if BashOutput then
|
||||
print "printf '"
|
||||
puts $documentation
|
||||
puts "'"
|
||||
puts "exit"
|
||||
else
|
||||
puts $documentation
|
||||
end
|
||||
exit -1
|
||||
end
|
||||
|
||||
# Regular arguments
|
||||
next_is_value = false
|
||||
arguments.each do |argument|
|
||||
if argument.start_with?("-") then
|
||||
known_option = known_options.find { |known_option| known_option.in?(argument) }
|
||||
next_is_value = (known_option and not known_option.boolean)
|
||||
else
|
||||
$arguments << argument if not next_is_value
|
||||
next_is_value = false
|
||||
end
|
||||
end
|
||||
|
||||
# Bash support
|
||||
if BashOutput then
|
||||
$options.keys.each do |name|
|
||||
puts "#{name}=\"#{$options[name].to_s.sub("true", "yes")}\""
|
||||
end
|
||||
puts "unset arguments"
|
||||
$arguments.each do |argument|
|
||||
puts "arguments+=(\"#{argument}\")"
|
||||
end
|
||||
# This is supposed to be eventually removed
|
||||
$documentation = @@parser.documentation
|
||||
$arguments = @@parser.arguments
|
||||
$options = @@parser.options
|
||||
end
|
||||
|
|
10
example.rb
10
example.rb
|
@ -22,20 +22,20 @@
|
|||
## format.
|
||||
|
||||
require_relative "easyoptions"
|
||||
options, arguments = EasyOptions.all
|
||||
|
||||
# Boolean options
|
||||
puts "Option specified: --some-option" if $options[:option]
|
||||
puts "Option specified: --some-boolean" if $options[:some_boolean]
|
||||
puts "Option specified: --some-option" if options[:option]
|
||||
puts "Option specified: --some-boolean" if options[:some_boolean]
|
||||
|
||||
# Parameter option
|
||||
value = $options[:some_value]
|
||||
value = options[:some_value]
|
||||
if value
|
||||
value = $options[:some_value]
|
||||
type = value.is_a?(Fixnum)? "number" : "string"
|
||||
puts "Option specified: --some-value is #{value} (a #{type})"
|
||||
end
|
||||
|
||||
# Arguments
|
||||
$arguments.each do |argument|
|
||||
arguments.each do |argument|
|
||||
puts "Argument specified: #{argument}"
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue