slop 3.3.2 → 3.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ 3.3.3 (2012-08-29)
2
+ ------------------
3
+
4
+ * Ensure autocreate arguments are not created as options (#77)
5
+ * Ensure options are not swallowed when using short options with argument
6
+ included (#74)
7
+
1
8
  3.3.2 (2012-06-26)
2
9
  ------------------
3
10
 
@@ -4,7 +4,7 @@ require 'slop/commands'
4
4
  class Slop
5
5
  include Enumerable
6
6
 
7
- VERSION = '3.3.2'
7
+ VERSION = '3.3.3'
8
8
 
9
9
  # The main Error class, all Exception classes inherit from this class.
10
10
  class Error < StandardError; end
@@ -433,7 +433,7 @@ class Slop
433
433
  elsif option.accepts_optional_argument?
434
434
  argument ||= items.at(index + 1)
435
435
 
436
- if argument && argument =~ /\A([^-\-?]|-\d)+/
436
+ if argument && argument =~ /\A([^\-?]|-\d)+/
437
437
  execute_option(option, argument, index, item)
438
438
  else
439
439
  option.call(nil)
@@ -467,7 +467,9 @@ class Slop
467
467
  end
468
468
 
469
469
  if argument
470
- @trash << index + 1 unless item && item.end_with?("=#{argument}")
470
+ unless item && item.end_with?("=#{argument}")
471
+ @trash << index + 1 unless option.argument_in_value
472
+ end
471
473
  option.value = argument
472
474
  else
473
475
  option.value = option.count > 0
@@ -511,6 +513,7 @@ class Slop
511
513
  case flag
512
514
  when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
513
515
  option, argument = fetch_option($1), ($2 || false)
516
+ option.argument_in_value = true if option
514
517
  end
515
518
  end
516
519
 
@@ -525,7 +528,7 @@ class Slop
525
528
  # Returns nothing.
526
529
  def autocreate(items, index)
527
530
  flag = items[index]
528
- unless present?(flag)
531
+ if !fetch_option(flag) && !@trash.include?(index)
529
532
  option = build_option(Array(flag))
530
533
  argument = items[index + 1]
531
534
  option.config[:argument] = (argument && argument !~ /\A--?/)
@@ -2,7 +2,7 @@ class Slop
2
2
  class Commands
3
3
  include Enumerable
4
4
 
5
- attr_reader :config, :commands
5
+ attr_reader :config, :commands, :arguments
6
6
  attr_writer :banner
7
7
 
8
8
  # Create a new instance of Slop::Commands and optionally build
@@ -37,6 +37,7 @@ class Slop
37
37
  @config = config
38
38
  @commands = {}
39
39
  @banner = nil
40
+ @triggered_command = nil
40
41
 
41
42
  if block_given?
42
43
  block.arity == 1 ? yield(self) : instance_eval(&block)
@@ -94,6 +95,19 @@ class Slop
94
95
  end
95
96
  alias get []
96
97
 
98
+ # Check for a command presence.
99
+ #
100
+ # Examples:
101
+ #
102
+ # cmds.parse %w( foo )
103
+ # cmds.present?(:foo) #=> true
104
+ # cmds.present?(:bar) #=> false
105
+ #
106
+ # Returns true if the given key is present in the parsed arguments.
107
+ def present?(key)
108
+ key.to_s == @triggered_command
109
+ end
110
+
97
111
  # Parse a list of items.
98
112
  #
99
113
  # items - The Array of items to parse.
@@ -149,7 +163,8 @@ class Slop
149
163
  # Returns the Array of items (with options removed if bang == true).
150
164
  def parse_items(items, bang = false)
151
165
  if opts = commands[items[0].to_s]
152
- items.shift
166
+ @triggered_command = items.shift
167
+ execute_arguments(items, bang)
153
168
  bang ? opts.parse!(items) : opts.parse(items)
154
169
  execute_global_opts(items, bang)
155
170
  else
@@ -165,6 +180,13 @@ class Slop
165
180
  items
166
181
  end
167
182
 
183
+ # Returns nothing.
184
+ def execute_arguments(items, bang)
185
+ @arguments = items.take_while { |arg| !arg.start_with?('-') }
186
+ items.shift(@arguments.size) if bang
187
+ end
188
+
189
+ # Returns nothing.
168
190
  def execute_global_opts(items, bang)
169
191
  if global_opts = commands['global']
170
192
  bang ? global_opts.parse!(items) : global_opts.parse(items)
@@ -18,8 +18,7 @@ class Slop
18
18
  }
19
19
 
20
20
  attr_reader :short, :long, :description, :config, :types
21
- attr_accessor :count
22
- attr_writer :value
21
+ attr_accessor :count, :argument_in_value
23
22
 
24
23
  # Incapsulate internal option information, mainly used to store
25
24
  # option specific configuration data, most of the meat of this
@@ -39,13 +38,13 @@ class Slop
39
38
  @config = DEFAULT_OPTIONS.merge(config)
40
39
  @count = 0
41
40
  @callback = block_given? ? block : config[:callback]
41
+ @value = nil
42
42
 
43
43
  @types = {
44
44
  :string => proc { |v| v.to_s },
45
45
  :symbol => proc { |v| v.to_sym },
46
46
  :integer => proc { |v| value_to_integer(v) },
47
47
  :float => proc { |v| value_to_float(v) },
48
- :array => proc { |v| v.split(@config[:delimiter], @config[:limit]) },
49
48
  :range => proc { |v| value_to_range(v) },
50
49
  :count => proc { |v| @count }
51
50
  }
@@ -55,7 +54,10 @@ class Slop
55
54
  end
56
55
 
57
56
  @config.each_key do |key|
58
- self.class.send(:define_method, "#{key}?") { !!@config[key] }
57
+ predicate = :"#{key}?"
58
+ unless self.class.method_defined? predicate
59
+ self.class.send(:define_method, predicate) { !!@config[key] }
60
+ end
59
61
  end
60
62
  end
61
63
 
@@ -81,6 +83,23 @@ class Slop
81
83
  @callback.call(*objects) if @callback.respond_to?(:call)
82
84
  end
83
85
 
86
+ # Set the new argument value for this option.
87
+ #
88
+ # We use this setter method to handle concatenating lists. That is,
89
+ # when an array type is specified and used more than once, values from
90
+ # both options will be grouped together and flattened into a single array.
91
+ def value=(new_value)
92
+ if config[:as].to_s.downcase == 'array'
93
+ @value ||= []
94
+
95
+ if new_value.respond_to?(:split)
96
+ @value.concat new_value.split(config[:delimiter], config[:limit])
97
+ end
98
+ else
99
+ @value = new_value
100
+ end
101
+ end
102
+
84
103
  # Fetch the argument value for this option.
85
104
  #
86
105
  # Returns the Object once any type conversions have taken place.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'slop'
3
- s.version = '3.3.2'
3
+ s.version = '3.3.3'
4
4
  s.summary = 'Option gathering made easy'
5
5
  s.description = 'A simple DSL for gathering options and parsing the command line'
6
6
  s.author = 'Lee Jarvis'
@@ -27,6 +27,12 @@ class CommandsTest < TestCase
27
27
  assert_equal 'Force creation', @commands[:new].fetch_option(:force).description
28
28
  end
29
29
 
30
+ test "checking for a command presence" do
31
+ @commands.parse %w( new --force )
32
+ assert @commands.present?(:new)
33
+ refute @commands.present?(:version)
34
+ end
35
+
30
36
  test "to_hash" do
31
37
  assert_equal({
32
38
  :new => { :force => nil, :outdir => nil },
@@ -77,14 +83,20 @@ class CommandsTest < TestCase
77
83
  items = %w( foo bar baz )
78
84
  assert_equal items, @commands.parse(items)
79
85
 
80
- items = %w( new --force )
86
+ items = %w( new file --force )
81
87
  assert_equal items, @commands.parse(items)
82
88
  end
83
89
 
84
90
  test "parse! removes options/arguments" do
85
- items = %w( new --outdir foo )
91
+ items = %w( new file --outdir foo )
86
92
  @commands.parse!(items)
87
93
  assert_equal [], items
88
94
  end
89
95
 
90
- end
96
+ test "command arguments" do
97
+ items = %w( new file1 file2 --outdir foo )
98
+ @commands.parse(items)
99
+ assert_equal %w( file1 file2 ), @commands.arguments
100
+ end
101
+
102
+ end
@@ -1,3 +1,5 @@
1
+ $VERBOSE = true
2
+
1
3
  unless Object.const_defined? 'Slop'
2
4
  $:.unshift File.expand_path('../../lib', __FILE__)
3
5
  require 'slop'
@@ -71,14 +71,14 @@ class OptionTest < TestCase
71
71
  end
72
72
 
73
73
  test "range type cast" do
74
- assert_equal (1..10), option_value(%w/-r 1..10/, :r=, :as => Range)
75
- assert_equal (1..10), option_value(%w/-r 1-10/, :r=, :as => Range)
76
- assert_equal (1..10), option_value(%w/-r 1,10/, :r=, :as => Range)
77
- assert_equal (1...10), option_value(%w/-r 1...10/, :r=, :as => Range)
78
- assert_equal (-1..10), option_value(%w/-r -1..10/, :r=, :as => Range)
79
- assert_equal (1..-10), option_value(%w/-r 1..-10/, :r=, :as => Range)
80
- assert_equal (1..1), option_value(%w/-r 1/, :r=, :as => Range)
81
- assert_equal (-1..10), option_value(%w/-r -1..10/, :r, :as => Range, :optional_argument => true)
74
+ assert_equal((1..10), option_value(%w/-r 1..10/, :r=, :as => Range))
75
+ assert_equal((1..10), option_value(%w/-r 1-10/, :r=, :as => Range))
76
+ assert_equal((1..10), option_value(%w/-r 1,10/, :r=, :as => Range))
77
+ assert_equal((1...10), option_value(%w/-r 1...10/, :r=, :as => Range))
78
+ assert_equal((-1..10), option_value(%w/-r -1..10/, :r=, :as => Range))
79
+ assert_equal((1..-10), option_value(%w/-r 1..-10/, :r=, :as => Range))
80
+ assert_equal((1..1), option_value(%w/-r 1/, :r=, :as => Range))
81
+ assert_equal((-1..10), option_value(%w/-r -1..10/, :r, :as => Range, :optional_argument => true))
82
82
 
83
83
  opts = Slop.new(:strict => true) { on :r=, :as => Range }
84
84
  assert_raises(Slop::InvalidArgumentError) { opts.parse %w/-r abc/ }
@@ -86,6 +86,7 @@ class OptionTest < TestCase
86
86
 
87
87
  test "array type cast" do
88
88
  assert_equal %w/lee john bill/, option_value(%w/-p lee,john,bill/, :p=, :as => Array)
89
+ assert_equal %w/lee john bill jeff jill/, option_value(%w/-p lee,john,bill -p jeff,jill/, :p=, :as => Array)
89
90
  assert_equal %w/lee john bill/, option_value(%w/-p lee:john:bill/, :p=, :as => Array, :delimiter => ':')
90
91
  assert_equal %w/lee john,bill/, option_value(%w/-p lee,john,bill/, :p=, :as => Array, :limit => 2)
91
92
  assert_equal %w/lee john:bill/, option_value(%w/-p lee:john:bill/, :p=, :as => Array, :limit => 2, :delimiter => ':')
@@ -109,7 +110,7 @@ class OptionTest < TestCase
109
110
 
110
111
  test "using a default value as fallback" do
111
112
  opts = Slop.new
112
- opt = opts.on :f, :argument => :optional, :default => 'foo'
113
+ opts.on :f, :argument => :optional, :default => 'foo'
113
114
  opts.parse %w'-f'
114
115
  assert_equal 'foo', opts[:f]
115
116
  end
@@ -47,6 +47,11 @@ class SlopTest < TestCase
47
47
  slop = Slop.new { on :foo= }
48
48
  slop.parse %w' --foo=bar '
49
49
  assert_equal 'bar', slop[:foo]
50
+
51
+ slop = Slop.new(:multiple_switches => false) { on :f=; on :b= }
52
+ slop.parse %w' -fabc -bdef '
53
+ assert_equal 'abc', slop[:f]
54
+ assert_equal 'def', slop[:b]
50
55
  end
51
56
 
52
57
  test "fetch_option" do
@@ -175,6 +180,14 @@ class SlopTest < TestCase
175
180
  assert opts.fetch_option(:foo).autocreated?
176
181
  assert_equal 'bar', opts.fetch_option(:foo).value
177
182
  refute opts.fetch_option(:baz).expects_argument?
183
+ assert_equal nil, opts.fetch_option(:bar)
184
+
185
+ opts = Slop.new :autocreate => true do
186
+ on :f, :foo=
187
+ end
188
+ opts.parse %w[ --foo bar --baz stuff ]
189
+ assert_equal 'bar', opts[:foo]
190
+ assert_equal 'stuff', opts[:baz]
178
191
  end
179
192
 
180
193
  test "option terminator" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slop
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.2
4
+ version: 3.3.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-26 00:00:00.000000000 Z
12
+ date: 2012-08-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -78,7 +78,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
78
  version: '0'
79
79
  segments:
80
80
  - 0
81
- hash: -3236998902673647399
81
+ hash: 3713237527878732625
82
82
  required_rubygems_version: !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
@@ -87,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
87
  version: '0'
88
88
  segments:
89
89
  - 0
90
- hash: -3236998902673647399
90
+ hash: 3713237527878732625
91
91
  requirements: []
92
92
  rubyforge_project:
93
93
  rubygems_version: 1.8.23