summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLee Jarvis <lee@jarvis.co>2011-04-16 19:36:14 +0100
committerLee Jarvis <lee@jarvis.co>2011-04-16 19:36:14 +0100
commit72a91e7f3ec795caea95b9f3dd805c4db3bb31ee (patch)
treeee90882b9506ab4b069ddcb4df151a079d2d5147
parent9869038dfcba7de5eeb9497d8aa1324085cf435e (diff)
parent37561c7dc1e370fdacef7642935a7929acadf380 (diff)
downloadslop-72a91e7f3ec795caea95b9f3dd805c4db3bb31ee.tar.gz
Merge branch 'commands'
-rw-r--r--README.md38
-rw-r--r--lib/slop.rb59
-rw-r--r--test/commands_test.rb72
-rw-r--r--test/slop_test.rb23
4 files changed, 180 insertions, 12 deletions
diff --git a/README.md b/README.md
index da253a8..f9acd7d 100644
--- a/README.md
+++ b/README.md
@@ -315,6 +315,44 @@ yields:
-n, --name Your name
-h, --help Print this help message
+Commands
+--------
+
+Slop allows you to nest more instances of Slop inside of `commands`. These
+instances will then be used to parse arguments if they're called upon.
+
+Slop will use the first argument in the list of items passed to `parse` to
+check if it is a `command`.
+
+ Slop.parse ['foo', '--bar', 'baz']
+
+Slop will look to see if the `foo` command exists, and if it does, it'll pass
+the options `['--bar', 'baz']` to the instance of Slop that belongs to `foo`.
+Here's how commands might look:
+
+ opts = Slop.new do
+ command :foo do
+ on :b, :bar, 'something', true
+ end
+
+ command :clean do
+ on :v, :verbose, do
+ puts 'Enabled verbose mode for clean'
+ end
+ end
+
+ # non-command specific options
+ on :v, :version do
+ puts 'version 1'
+ end
+ end
+
+* Run with `run.rb -v`
+* Output: `version 1`
+
+* Run with: `run.rb clean -v`
+* Output: `Enabled verbose mode for clean`
+
Woah woah, why you hating on OptionParser?
------------------------------------------
diff --git a/lib/slop.rb b/lib/slop.rb
index ae95559..02047b4 100644
--- a/lib/slop.rb
+++ b/lib/slop.rb
@@ -44,6 +44,9 @@ class Slop
# @return [Options]
attr_reader :options
+ # @return [Hash]
+ attr_reader :commands
+
attr_writer :banner
attr_accessor :longest_flag
@@ -54,6 +57,9 @@ class Slop
# @option opts [Boolean] :multiple_switches Allows `-abc` to be processed
# as the options 'a', 'b', 'c' and will force their argument values to
# true. By default Slop with parse this as 'a' with the argument 'bc'
+ # @option opts [String] :banner The banner text used for the help
+ # @option opts [Proc, #call] :on_empty Any object that respondes to `call`
+ # which is executed when Slop has no items to parse
def initialize(*opts, &block)
sloptions = {}
sloptions.merge! opts.pop if opts.last.is_a? Hash
@@ -61,11 +67,16 @@ class Slop
opts.each { |o| sloptions[o] = true }
@options = Options.new
+ @commands = {}
+
@longest_flag = 0
- @strict = sloptions[:strict]
@invalid_options = []
+
+ @banner ||= sloptions[:banner]
+ @strict = sloptions[:strict]
@multiple_switches = sloptions[:multiple_switches]
@on_empty = sloptions[:on_empty]
+ @sloptions = sloptions
if block_given?
block.arity == 1 ? yield(self) : instance_eval(&block)
@@ -119,7 +130,7 @@ class Slop
# @return [Object] Returns the value associated with that option.
def [](key)
option = @options[key]
- option.argument_value if option
+ option ? option.argument_value : @commands[key]
end
# Specify an option with a short or long version, description and type.
@@ -155,6 +166,37 @@ class Slop
alias :opt :option
alias :on :option
+
+ # Namespace options depending on what command is executed
+ #
+ # @param [Symbol, String] label
+ # @param [Hash] options
+ # @example
+ # opts = Slop.new do
+ # command :create do
+ # on :v, :verbose
+ # end
+ # end
+ #
+ # # ARGV is `create -v`
+ # opts.commands[:create].verbose? #=> true
+ # @return [Slop] a new instance of Slop namespaced to +label+
+ def command(label, options={}, &block)
+ if @commands[label]
+ raise ArgumentError, "command `#{label}` already exists"
+ end
+
+ options = @sloptions.merge(options)
+ slop = Slop.new(options)
+ @commands[label] = slop
+
+ if block_given?
+ block.arity == 1 ? yield(slop) : slop.instance_eval(&block)
+ end
+
+ slop
+ end
+
# Add an object to be called when Slop has no values to parse
#
# @param [Object, nil] proc The object (which can be anything
@@ -231,6 +273,8 @@ class Slop
return
end
+ return if execute_command(items, delete)
+
trash = []
items.each_with_index do |item, index|
@@ -341,6 +385,17 @@ class Slop
end
end
+ def execute_command(items, delete)
+ command = items[0]
+ command = @commands.keys.find { |cmd| cmd.to_s == command.to_s }
+ if @commands.key?(command)
+ items.shift
+ opts = @commands[command]
+ delete ? opts.parse!(items) : opts.parse(items)
+ true
+ end
+ end
+
def clean_options(args)
options = []
diff --git a/test/commands_test.rb b/test/commands_test.rb
new file mode 100644
index 0000000..4240a8a
--- /dev/null
+++ b/test/commands_test.rb
@@ -0,0 +1,72 @@
+require File.dirname(__FILE__) + '/helper'
+
+class CommandsTest < TestCase
+ test 'creating commands' do
+ slop = Slop.new do
+ command :foo do on :f, :foo, 'foo option' end
+ command :bar do on :f, :foo; on :b, :bar, true end
+ end
+
+ slop.commands.each_value do |command|
+ assert_kind_of Slop, command
+ end
+
+ assert 'foo option', slop.commands[:foo].options[:foo].description
+
+ slop.parse %w/bar --bar baz/
+ assert 'baz', slop.commands[:bar][:bar]
+ assert_nil slop.commands['bar']
+ end
+
+ test 'repeating existing commands' do
+ slop = Slop.new
+ assert slop.command :foo
+ assert_raises(ArgumentError) { slop.command :foo }
+ end
+
+ test 'commands inheriting options' do
+ slop = Slop.new :strict do
+ command :foo do end
+ end
+ assert slop.commands[:foo].instance_variable_get(:@strict)
+ end
+
+ test 'commands setting options' do
+ slop = Slop.new :strict => false do
+ command :foo, :strict => true do end
+ end
+ assert slop.commands[:foo].instance_variable_get(:@strict)
+ end
+
+ test 'inception' do
+ slop = Slop.new do
+ command(:foo) { command(:bar) { command(:baz) { on :f, 'D:' } } }
+ end
+ desc = slop.commands[:foo].commands[:bar].commands[:baz].options[:f].description
+ assert_equal 'D:', desc
+ end
+
+ test 'commands with banners' do
+ slop = Slop.new do
+ command(:foo, :banner => 'bar') { }
+ command(:bar) { banner 'bar' }
+ end
+ assert_equal 'bar', slop.commands[:foo].banner
+ assert_equal 'bar', slop.commands[:bar].banner
+ end
+
+ test 'executing on_empty on separate commands' do
+ incmd = inslop = false
+ slop = Slop.new do
+ command(:foo) { on(:bar) {}; on_empty { incmd = true }}
+ on_empty { inslop = true }
+ end
+ slop.parse %w//
+ assert inslop
+ refute incmd
+ inslop = false
+ slop.parse %w/foo/
+ assert incmd
+ refute inslop
+ end
+end \ No newline at end of file
diff --git a/test/slop_test.rb b/test/slop_test.rb
index 2ada9a9..5648ffc 100644
--- a/test/slop_test.rb
+++ b/test/slop_test.rb
@@ -1,5 +1,3 @@
-require File.dirname(__FILE__) + '/helper'
-
class SlopTest < TestCase
def clean_options(*args)
Slop.new.send(:clean_options, args)
@@ -19,7 +17,6 @@ class SlopTest < TestCase
test 'new accepts a hash or array of symbols' do
slop = Slop.new :strict, :multiple_switches => true
-
[ :@multiple_switches, :@strict ].each do |var|
assert slop.instance_variable_get var
end
@@ -94,10 +91,8 @@ class SlopTest < TestCase
test 'preserving order when yielding non-options' do
items = []
-
slop = Slop.new { on(:name, true) { |name| items << name } }
slop.parse(%w/foo --name bar baz/) { |value| items << value }
-
assert_equal %w/foo bar baz/, items
end
@@ -113,6 +108,9 @@ class SlopTest < TestCase
slop = Slop.new "foo bar"
assert_equal "foo bar", slop.banner
+
+ slop = Slop.new :banner => "foo bar"
+ assert_equal "foo bar", slop.banner
end
test 'storing long option lengths' do
@@ -128,7 +126,6 @@ class SlopTest < TestCase
opts = Slop.new do
on :name, true
end
-
assert_equal %w/a/, opts.parse!(%w/--name lee a/)
assert_equal %w/--name lee a/, opts.parse(%w/--name lee a/)
end
@@ -181,13 +178,21 @@ class SlopTest < TestCase
assert_equal(['c', nil, nil, false], clean_options(:c, false))
end
- test '[] returns an options argument value or nil' do
+ test '[] returns an options argument value or a command or nil (in that order)' do
slop = Slop.new
slop.opt :n, :name, true
- slop.parse %w/--name lee/
+ slop.opt :foo
+ slop.command(:foo) { }
+ slop.command(:bar) { }
+ slop.parse %w/--name lee --foo/
assert_equal 'lee', slop[:name]
assert_equal 'lee', slop[:n]
+
+ assert_equal true, slop[:foo]
+ assert_kind_of Slop, slop[:bar]
+
+ assert_nil slop[:baz]
end
test 'arguments ending ? test for option existance' do
@@ -246,7 +251,6 @@ class SlopTest < TestCase
slop = Slop.new
slop.banner = 'Usage: foo [options]'
slop.parse
-
assert slop.to_s =~ /^Usage: foo/
end
@@ -307,7 +311,6 @@ class SlopTest < TestCase
test 'parsing options with options as arguments' do
slop = Slop.new { on :f, :foo, true }
-
assert_raises(Slop::MissingArgumentError) { slop.parse %w/-f --bar/ }
end
end