class Pry::Slop
rubocop:disable Metrics/ClassLength
def [](key)
key - The Symbol or String option short or long flag.
Fetch an options argument value.
def [](key) option = fetch_option(key) option.value if option end
def add_callback(label, &block)
label - The Symbol identifier to attach this callback.
Add a callback.
def add_callback(label, &block) (@callbacks[label] ||= []) << block end
def autocreate(items, index)
index - The current Integer index for the item we're processing.
items - The Array of items we're parsing.
Autocreate an option on the fly. See the :autocreate Slop config option.
def autocreate(items, index) flag = items[index] return if fetch_option(flag) || @trash.include?(index) option = build_option(Array(flag)) argument = items[index + 1] option.config[:argument] = (argument && argument !~ /\A--?/) option.config[:autocreated] = true options << option end
def banner(banner = nil)
banner - The String to set the banner.
Get or set the banner.
def banner(banner = nil) config[:banner] = banner if banner config[:banner] end
def banner=(banner)
Set the banner.
def banner=(banner) config[:banner] = banner end
def build_option(objects, &block)
objects - An Array of objects used to build this option.
Build an option from a list of objects.
def build_option(objects, &block) config = {} config[:argument] = true if @config[:arguments] config[:optional_argument] = true if @config[:optional_arguments] if objects.last.is_a?(Hash) config.merge!(objects.last) objects.pop end short = extract_short_flag(objects, config) long = extract_long_flag(objects, config) desc = objects[0].respond_to?(:to_str) ? objects.shift : nil Option.new(self, short, long, desc, config, &block) end
def clean(object)
object - The Object we want to cast to a String and clean.
Remove any leading -- characters from a string.
def clean(object) object.to_s.sub(/\A--?/, '') end
def command(command, options = {}, &block)
options - A Hash of configuration options (see Slop::new)
command - The Symbol or String used to identify this command.
Add a new command.
def command(command, options = {}, &block) @commands[command.to_s] = Pry::Slop.new(options, &block) end
def commands_to_help
def commands_to_help padding = 0 @commands.each { |c, _| padding = c.size if c.size > padding } @commands.map do |cmd, opts| " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}" end.join("\n") end
def description(desc = nil)
desc - The String to set the description.
Get or set the description (used for commands).
def description(desc = nil) config[:description] = desc if desc config[:description] end
def description=(desc)
Set the description (used for commands).
def description=(desc) config[:description] = desc end
def each(&block)
def each(&block) options.each(&block) end
def execute_multiple_switches(option, argument, index)
index - The index of the current item being processed.
argument - The argument to this option. (Split into multiple Options).
option - The first Option object.
method is only executed if the multiple_switches argument is true.
Execute a `-abc` type option where a, b and c are all options. This
def execute_multiple_switches(option, argument, index) execute_option(option, nil, index) argument.split('').each do |key| next unless (opt = fetch_option(key)) opt.count += 1 execute_option(opt, nil, index, key) end end
def execute_option(option, argument, index, item = nil)
item - The optional String item we're processing.
index - The current Integer index of the object we're processing.
argument - The argument Object to assign to this option.
option - The Slop::Option object found by #process_item.
Execute an option, firing off callbacks and assigning arguments.
def execute_option(option, argument, index, item = nil) unless option if config[:multiple_switches] && strict? raise InvalidOptionError, "Unknown option -#{item}" end return end if argument unless item && item.end_with?("=#{argument}") @trash << index + 1 unless option.argument_in_value end option.value = argument else option.value = option.count > 0 end if option.match? && !argument.match(option.config[:match]) raise InvalidArgumentError, "#{argument} is an invalid argument" end option.call(option.value) end
def extract_long_flag(objects, config)
objects - The Array of objects passed from #build_option.
Extract the long flag from an item.
def extract_long_flag(objects, config) flag = objects.first.to_s return unless flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/ config[:argument] ||= true if flag.end_with?('=') config[:optional_argument] = true if flag.end_with?('=?') objects.shift clean(flag).sub(/\=\??\z/, '') end
def extract_option(flag)
flag - The flag key used to extract an option.
Extract an option from a flag.
def extract_option(flag) option = fetch_option(flag) option ||= fetch_option(flag.downcase) if config[:ignore_case] option ||= fetch_option(flag.gsub(/([^-])-/, '\1_')) unless option case flag when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/ option = fetch_option(Regexp.last_match(1)) argument = Regexp.last_match(2) || false option.argument_in_value = true if option end end [option, argument] end
def extract_short_flag(objects, config)
objects - The Array of objects passed from #build_option.
Extract the short flag from an item.
def extract_short_flag(objects, config) flag = clean(objects.first) if flag.size == 2 && flag.end_with?('=') config[:argument] ||= true flag.chop! end return unless flag.size == 1 objects.shift flag end
def fetch_command(command)
# ruby run.rb foo -v
end
on :v, :verbose, 'Enable verbose mode'
opts.command :foo do
Examples:
command - The String or Symbol name of the command.
Fetch a Slop object associated with this command.
def fetch_command(command) @commands[command.to_s] end
def fetch_option(key)
opt.accepts_optional_argument? #=> true
opt.class #=> Slop::Option
opt = opts.fetch_option(:foo)
opts.on(:foo, 'Something fooey', :argument => :optional)
Examples:
key - The Symbol or String option key.
Fetch a Slop::Option object.
def fetch_option(key) options.find { |option| [option.long, option.short].include?(clean(key)) } end
def initialize(config = {}, &block)
config - A Hash of configuration options.
Create a new instance of Slop and optionally build options via a block.
def initialize(config = {}, &block) @config = DEFAULT_OPTIONS.merge(config) @options = [] @commands = {} @trash = [] @triggered_options = [] @unknown_options = [] @callbacks = {} @separators = {} @runner = nil if block_given? block.arity == 1 ? yield(self) : instance_eval(&block) end return unless config[:help] on('-h', '--help', 'Display this help message.', tail: true) do warn help end end
def method_missing(method, *args, &block)
Returns true if this option is present. If this method does not end
opts.other? #=> false
opts.verbose? #=> true
opts.parse %( --verbose )
Examples:
Convenience method for present?(:option).
def method_missing(method, *args, &block) meth = method.to_s if meth.end_with?('?') meth = meth.chop present?(meth) || present?(meth.tr('_', '-')) else super end end
def missing
opts.missing #=> ['password']
opts.parse %w[ --name Lee ]
end
on :p, :password=
on :n, :name=
opts = Slop.new do
Examples:
Fetch a list of options which were missing from the parsed list.
def missing (options - @triggered_options).map(&:key) end
def on(*objects, &block)
on :v, :verbose, 'Enable verbose mode'
on '-u', '--username=', 'Your username'
Examples:
objects - An Array with an optional Hash as the last element.
Add an Option.
def on(*objects, &block) option = build_option(objects, &block) options << option option end
def optspec(string, config = {})
opts.fetch_option(:name).description #=> "Your name"
SPEC
p,passcode= Your secret pass code
A,auth Sign in with auth
a,age= Your age
n,name= Your name
---
ruby foo.rb [options]
opts = Slop.optspec(<<-SPEC)
Examples:
config - A Hash of configuration options to pass to Slop.new
string - The optspec String
options.
unable to pass any advanced options to the on() method when creating
than programatically. Do note though that with this method, you're
This allows you to design your options via a simple String rather
Build a Slop object from a option specification.
def optspec(string, config = {}) config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/] lines = optspec.split("\n").reject(&:empty?) opts = Slop.new(config) lines.each do |line| opt, description = line.split(' ', 2) short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') } opt = opts.on(short, long, description) if long && long.end_with?('=') long.sub!(/\=$/, '') opt.config[:argument] = true end end opts end
def parse(items = ARGV, config = {}, &block)
end
on '-n', '--name', 'Your username', :argument => true
Slop.parse(ARGV, :help => true) do
Examples:
block - An optional block used to add options.
config - The Hash of configuration options to send to Slop.new().
items - The Array of items to extract options from (default: ARGV).
def parse(items = ARGV, config = {}, &block) parse! items.dup, config, &block end
def parse(items = ARGV, &block)
block - An optional block which when used will yield non options.
items - The Array of items to extract options from (default: ARGV).
Parse a list of items, executing and gathering options along the way.
def parse(items = ARGV, &block) parse! items.dup, &block items end
def parse!(items = ARGV, config = {}, &block)
block - An optional block used to add options.
config - The Hash of configuration options to send to Slop.new().
items - The Array of items to extract options from (default: ARGV).
def parse!(items = ARGV, config = {}, &block) if items.is_a?(Hash) && config.empty? config = items items = ARGV end slop = Pry::Slop.new config, &block slop.parse! items slop end
def parse!(items = ARGV, &block)
block - An optional block which when used will yield non options.
items - The Array of items to extract options from (default: ARGV).
from the original Array.
unlike parse() this method will remove any options and option arguments
Parse a list of items, executing and gathering options along the way.
def parse!(items = ARGV, &block) if items.empty? && @callbacks[:empty] @callbacks[:empty].each { |cb| cb.call(self) } return items end if (cmd = @commands[items[0]]) return cmd.parse! items[1..-1] end items.each_with_index do |item, index| @trash << index && break if item == '--' autocreate(items, index) if config[:autocreate] process_item(items, index, &block) unless @trash.include?(index) end items.reject!.with_index { |_item, index| @trash.include?(index) } missing_options = options.select { |opt| opt.required? && opt.count < 1 } if missing_options.any? raise MissingOptionError, "Missing required option(s): #{missing_options.map(&:key).join(', ')}" end if @unknown_options.any? raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}" end if @triggered_options.empty? && @callbacks[:no_options] @callbacks[:no_options].each { |cb| cb.call(self) } end @runner.call(self, items) if @runner.respond_to?(:call) items end
def present?(*keys)
opts.present?(:bar) #=> false
opts.present?(:foo) #=> true
opts.parse %w( --foo )
Examples:
Check for an options presence.
def present?(*keys) keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 } end
def process_item(items, index, &block)
block - An optional block which when passed will yield non options.
index - The current Integer index of the item we want to process.
items - The Array of items to process.
callbacks, assign any option arguments, and do some sanity checks.
Process a list item, figure out if it's an option, execute any
def process_item(items, index, &block) return unless (item = items[index]) option, argument = extract_option(item) if item.start_with?('-') if option option.count += 1 unless item.start_with?('--no-') option.count += 1 if option.key[0, 3] == "no-" @trash << index @triggered_options << option if option.expects_argument? argument ||= items.at(index + 1) if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/ raise MissingArgumentError, "#{option.key} expects an argument" end execute_option(option, argument, index, item) elsif option.accepts_optional_argument? argument ||= items.at(index + 1) if argument && argument =~ /\A([^\-?]|-\d)+/ execute_option(option, argument, index, item) else option.call(nil) end elsif config[:multiple_switches] && argument execute_multiple_switches(option, argument, index) else option.value = option.count > 0 option.call(nil) end else @unknown_options << item if strict? && item =~ /\A--?/ yield(item) if block && !@trash.include?(index) end end
def respond_to_missing?(method_name, include_all = false)
Override this method so we can check if an option? method exists.
def respond_to_missing?(method_name, include_all = false) options.any? { |o| o.key == method_name.to_s.chop } || super end
def run(callable = nil, &block)
end
puts "Arguments: #{args.inspect}" if opts.verbose?
run do |opts, args|
on :v, :verbose
Slop.parse do
Example:
An Array of unparsed arguments
yields - The instance of Slop parsing these options
callable - An object responding to a call method.
Specify code to be executed when these options are parsed.
def run(callable = nil, &block) @runner = callable || block return if @runner.respond_to?(:call) raise ArgumentError, "You must specify a callable object or a block to #run" end
def separator(text)
Add string separators between options.
def separator(text) if @separators[options.size] @separators[options.size] << "\n#{text}" else @separators[options.size] = text end end
def strict?
Is strict mode enabled?
def strict? config[:strict] end
def to_hash(include_commands = false)
Returns a new Hash with option flags as keys and option values as values.
def to_hash(include_commands = false) hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }] if include_commands @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) } end hash end
def to_s
Print a handy Slop help string.
def to_s heads = options.reject(&:tail?) tails = (options - heads) opts = (heads + tails).select(&:help).map(&:to_s) optstr = opts.each_with_index.map do |o, i| (str = @separators[i + 1]) ? [o, str].join("\n") : o end.join("\n") if @commands.any? optstr << "\n" unless optstr.empty? optstr << "\nAvailable commands:\n\n" optstr << commands_to_help optstr << "\n\nSee `<command> --help` for more information on a specific command." end banner = config[:banner] banner ||= "Usage: #{File.basename($PROGRAM_NAME, '.*')}" \ "#{' [command]' if @commands.any?} [options]" if banner "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}" else optstr end end