slop 0.2.0 → 1.0.0.rc1

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.
@@ -1,190 +1,108 @@
1
1
  class Slop
2
- class Option
3
-
4
- # @return [Symbol,#to_s]
5
- attr_reader :flag
6
-
7
- # @return [Symbol,#to_s]
8
- attr_reader :option
9
-
10
- # @return [String]
11
- attr_reader :description
12
-
13
- # @return [Object]
14
- attr_reader :default
15
-
16
- # @return [Proc]
17
- attr_reader :callback
18
-
19
- # The maximum size of any --option, recorded for
20
- # padding out any output string with smaller option lengths
21
- # with the correct amount of whitespace
22
- #
23
- # @return [Fixnum]
24
- @@max_option_size = 0
25
-
26
- # @param [Hash] options Option attributes
27
- # @option options [Symbol,#to_s] :flag
28
- # @option options [Symbol,#to_s] :option
29
- # @option options [Symbol,#to_s] :description
30
- # @option options [Boolean] :argument
31
- # @option options [Boolean] :optional
32
- # @option options [Object] :default
33
- # @option options [Object] :as
34
- # @option options [Proc] :callback
35
- # @option options [String,#to_s] :delimiter
36
- # @option options [Integer] :limit
37
- def initialize(options={}, &blk)
38
- @options = options
39
- @flag = options[:flag]
40
- @option = options[:option] || options[:opt]
41
- @description = options[:description] || options[:desc]
42
- @argument = options[:argument] || false
43
- @optional = options[:optional] || options[:optional_argument]
44
- @argument ||= @optional
45
- @default = options[:default]
46
- @as = options[:as]
47
- @callback = options[:callback]
48
-
49
- # Array properties
50
- @delimiter = options[:delimiter] || ','
51
- @limit = options[:limit] || 0
52
-
53
- @argument_value = nil
54
-
55
- if @option
56
- if requires_argument?
57
- size = (@option.size * 2) + 4
58
- else
59
- size = @option.size + 2
60
- end
61
-
62
- @@max_option_size = size if @@max_option_size < size
2
+ class Options < Array
3
+ def to_hash
4
+ each_with_object({}) do |option, out|
5
+ out[option.key] = option.argument_value
63
6
  end
64
7
  end
65
8
 
66
- # Set the argument value
67
- # @param [Object] value
68
- def argument_value=(value)
69
- @argument_value = value
70
- end
71
-
72
- # @return [Object] the argument value after it's been cast
73
- # according to the `:as` option
74
- def argument_value
75
- @argument_value ||= @default
76
- return unless @argument_value
77
-
78
- case @as.to_s.downcase[0, 3]
79
- when 'arr'; @argument_value.split(@delimiter, @limit)
80
- when 'int'; @argument_value.to_i
81
- when 'sym'; @argument_value.to_sym
9
+ def [](item)
10
+ item = item.to_s
11
+ if item =~ /^\d+/
12
+ super
82
13
  else
83
- @argument_value
14
+ find do |option|
15
+ option.short_flag == item || option.long_flag == item
16
+ end
84
17
  end
85
18
  end
19
+ end
86
20
 
87
- # @param [to_s] flag
88
- # @return [Boolean] true if this option contains a flag
89
- def has_flag?(flag)
90
- @flag.to_s == flag.to_s
91
- end
21
+ class Option
92
22
 
93
- # @param [to_s] option
94
- # @return [Boolean] true if this option contains an option label
95
- def has_option?(option)
96
- @option.to_s == option.to_s
97
- end
23
+ attr_reader :short_flag
24
+ attr_reader :long_flag
25
+ attr_reader :description
26
+ attr_reader :callback
27
+ attr_writer :argument_value
98
28
 
99
- # @return [Boolean] true if this option has a default value
100
- def has_default?
101
- !@default.nil?
102
- end
29
+ @@longest_flag = 0
103
30
 
104
- # @return [Boolean] true if this option has a switch value
105
- def has_switch?
106
- !!@options[:switch]
107
- end
31
+ def initialize(short, long, description, argument, options={}, &block)
32
+ @short_flag, @long_flag = short, long
33
+ @description, @expects_argument = description, argument
34
+ @options = options
108
35
 
109
- # @return [Boolean] true if the option has a callback
110
- def has_callback?
111
- !!@callback
112
- end
36
+ if @long_flag && @long_flag.size > @@longest_flag
37
+ @@longest_flag = @long_flag.size
38
+ end
113
39
 
114
- # execute this options callback
115
- def execute_callback
116
- @callback.call if has_callback?
40
+ @callback = block if block_given?
41
+ @callback ||= options[:callback]
42
+ @argument_value = nil
117
43
  end
118
44
 
119
- # does the option require an argument?
120
- # @return [Boolean]
121
- def requires_argument?
122
- !!@argument
45
+ def expects_argument?
46
+ @expects_argument || @options[:argument]
123
47
  end
124
48
 
125
- # Is the argument optional?
126
- # @return [Boolean]
127
- def optional_argument?
49
+ def accepts_optional_argument?
128
50
  @options[:optional]
129
51
  end
130
52
 
131
- # @return
132
- def [](key)
133
- @options[key]
134
- end
135
-
136
- # Replace options argument value with the switch value supplied, used
137
- # when supplying the `switch` option making switch flags easy to alter
138
- #
139
- # @example
140
- # option :v, :verbose, :default => false, :switch => true
141
- #
142
- # Now when the `-v` or `--verbose` option is supplied, verbose will
143
- # be set to `true`, rather than the default `false` option
144
- def switch_argument_value
145
- @argument_value = @options[:switch]
53
+ def has_callback?
54
+ !!@callback && @callback.respond_to?(:call)
146
55
  end
147
56
 
148
- # return a key for an option, prioritize
149
- # option before flag as it's more descriptive
150
57
  def key
151
- @option || @flag
58
+ @long_flag || @short_flag
152
59
  end
153
60
 
154
- def to_s
155
- str = "\t"
61
+ def default
62
+ @options[:default]
63
+ end
156
64
 
157
- if @flag
158
- str << "-#{@flag}"
65
+ def argument_value
66
+ value = @argument_value || default
67
+ return unless value
68
+
69
+ case @options[:as].to_s
70
+ when 'Array'
71
+ value.split(@options[:delimiter] || ',', @options[:limit] || 0)
72
+ when 'String'; value.to_s
73
+ when 'Symbol'; value.to_s.to_sym
74
+ when 'Integer'; value.to_i
75
+ when 'Float'; value.to_f
159
76
  else
160
- str << " " * 4
77
+ value
161
78
  end
79
+ end
162
80
 
163
- if @option
164
- str << ", " if @flag
165
- optionstr = "--#{@option}"
81
+ def to_s
82
+ out = " "
83
+ out += @short_flag ? "-#{@short_flag}, " : ' ' * 4
166
84
 
167
- if requires_argument?
168
- if optional_argument?
169
- optionstr << " [#{@option}]"
170
- else
171
- optionstr << " <#{@option}>"
172
- end
173
- end
174
- size_diff = @@max_option_size - ((@option.size * 2) + 4)
175
- optionstr << " " * size_diff if size_diff > 0
176
- str << optionstr
85
+ out += "--#{@long_flag}" if @long_flag
86
+
87
+ if @long_flag
88
+ diff = @@longest_flag - @long_flag.size
89
+ spaces = " " * (diff + 6)
90
+ out += spaces
177
91
  else
178
- str << " " * (@@max_option_size + 2)
92
+ spaces = " " * (@@longest_flag + 8)
93
+ out += spaces
179
94
  end
180
95
 
181
- str << "\t\t#{@description}" if @description
182
- str
96
+ out += @description if @description
97
+
98
+ out
183
99
  end
184
100
 
185
101
  def inspect
186
- "#<#{self.class}: #{@options}>"
102
+ "#<Slop::Option short_flag=#{@short_flag.inspect} " +
103
+ "long_flag=#{@long_flag.inspect} " +
104
+ "description=#{@description.inspect} "
187
105
  end
188
-
189
106
  end
190
- end
107
+
108
+ end
@@ -0,0 +1,3 @@
1
+ class Slop
2
+ VERSION = '1.0.0.rc1'
3
+ end
@@ -0,0 +1,17 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'slop/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'slop'
6
+ s.version = Slop::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = 'Option gathering made easy'
9
+ s.description = 'A simple DSL for gathering options and parsing the command line'
10
+ s.author = 'Lee Jarvis'
11
+ s.email = 'lee@jarvis.co'
12
+ s.homepage = 'http://github.com/injekt/slop'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- test/*`.split("\n")
16
+ s.require_paths = ["lib"]
17
+ end
@@ -0,0 +1,14 @@
1
+ class TestCase < MiniTest::Unit::TestCase
2
+ def self.test(name, &block)
3
+ test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
4
+ defined = instance_method(test_name) rescue false
5
+ raise "#{test_name} is already defined in #{self}" if defined
6
+ if block_given?
7
+ define_method(test_name, &block)
8
+ else
9
+ define_method(test_name) do
10
+ flunk "No implementation provided for #{name}"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class OptionTest < TestCase
4
+ def option(*args, &block)
5
+ Slop.new.option(*args, &block)
6
+ end
7
+
8
+ def option_with_argument(*args, &block)
9
+ options = args.shift
10
+ slop = Slop.new
11
+ option = slop.opt(*args)
12
+ slop.parse(options)
13
+ slop.find {|opt| opt.key == option.key }
14
+ end
15
+
16
+ def option_value(*args, &block)
17
+ option_with_argument(*args, &block).argument_value
18
+ end
19
+
20
+ test 'expects an argument if argument is true' do
21
+ assert option(:f, :foo, 'foo', true).expects_argument?
22
+ assert option(:f, :argument => true).expects_argument?
23
+
24
+ refute option(:f, :foo).expects_argument?
25
+ end
26
+
27
+ test 'accepts an optional argument if optional is true' do
28
+ assert option(:f, :optional => true).accepts_optional_argument?
29
+ assert option(:f, false, :optional => true).accepts_optional_argument?
30
+
31
+ refute option(:f, true).accepts_optional_argument?
32
+ end
33
+
34
+ test 'has a callback when passed a block or callback option' do
35
+ assert option(:f){}.has_callback?
36
+ assert option(:callback => proc {}).has_callback?
37
+
38
+ refute option(:f).has_callback?
39
+ end
40
+
41
+ test 'splits argument_value with :as => array' do
42
+ assert_equal %w/lee john bill/, option_value(
43
+ %w/--people lee,john,bill/, :people, true, :as => Array
44
+ )
45
+
46
+ assert_equal %w/lee john bill/, option_value(
47
+ %w/--people lee:john:bill/,
48
+ :people, true, :as => Array, :delimiter => ':'
49
+ )
50
+
51
+ assert_equal ['lee', 'john,bill'], option_value(
52
+ %w/--people lee,john,bill/,
53
+ :people, true, :as => Array, :limit => 2
54
+ )
55
+
56
+ assert_equal ['lee', 'john:bill'], option_value(
57
+ %w/--people lee:john:bill/,
58
+ :people, true, :as => Array, :limit => 2, :delimiter => ':'
59
+ )
60
+ end
61
+
62
+ test 'returns a symbol with :as => Symbol' do
63
+ assert_equal :foo, option_value(%w/--name foo/, :name, true, :as => Symbol)
64
+ end
65
+
66
+ test 'returns an integer with :as => Integer' do
67
+ assert_equal 30, option_value(%w/--age 30/, :age, true, :as => Integer)
68
+ end
69
+
70
+ test 'printing options' do
71
+ slop = Slop.new
72
+ slop.opt :n, :name, 'Your name', true
73
+ slop.opt :age, 'Your age', true
74
+ slop.opt :V, 'Display the version'
75
+
76
+ assert_equal " -n, --name Your name", slop.options[:name].to_s
77
+ assert_equal " --age Your age", slop.options[:age].to_s
78
+ assert_equal " -V, Display the version", slop.options[:V].to_s
79
+ end
80
+ end
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class SlopTest < TestCase
4
+ def clean_options(*args)
5
+ Slop.new.send(:clean_options, args)
6
+ end
7
+
8
+ def parse(items, &block)
9
+ Slop.parse(items, &block)
10
+ end
11
+
12
+ test 'includes Enumerable' do
13
+ assert Slop.included_modules.include?(Enumerable)
14
+ end
15
+
16
+ test 'parse returns a Slop object' do
17
+ slop = Slop.parse([])
18
+ assert_kind_of Slop, slop
19
+ end
20
+
21
+ test 'enumerating options' do
22
+ slop = Slop.new
23
+ slop.opt(:f, :foo, 'foo')
24
+ slop.opt(:b, :bar, 'bar')
25
+
26
+ slop.each { |option| assert option }
27
+ end
28
+
29
+ test 'parsing options' do
30
+
31
+ end
32
+
33
+ test '#parse does not remove parsed items' do
34
+ items = %w/--foo/
35
+ Slop.new { |opt| opt.on :foo }.parse(items)
36
+ assert_equal %w/--foo/, items
37
+ end
38
+
39
+ test '#parse! removes parsed items' do
40
+ items = %w/--foo/
41
+ Slop.new { |opt| opt.on :foo }.parse!(items)
42
+ assert_empty items
43
+ end
44
+
45
+ test 'the shit out of clean_options' do
46
+ assert_equal(
47
+ ['s', 'short', 'short option', false],
48
+ clean_options('-s', '--short', 'short option')
49
+ )
50
+
51
+ assert_equal(
52
+ [nil, 'long', 'long option only', true],
53
+ clean_options('--long', 'long option only', true)
54
+ )
55
+
56
+ assert_equal(
57
+ ['S', 'symbol', 'symbolize', false],
58
+ clean_options(:S, :symbol, 'symbolize')
59
+ )
60
+
61
+ assert_equal(
62
+ ['a', nil, 'alphabetical only', true],
63
+ clean_options('a', 'alphabetical only', true)
64
+ )
65
+
66
+ assert_equal( # for description-less options
67
+ [nil, 'optiononly', nil, false],
68
+ clean_options('--optiononly')
69
+ )
70
+
71
+ assert_equal(['c', nil, nil, true], clean_options(:c, true))
72
+ assert_equal(['c', nil, nil, false], clean_options(:c, false))
73
+ end
74
+
75
+ test '[] returns an options argument value or nil' do
76
+ slop = Slop.new
77
+ slop.opt :n, :name, true
78
+ slop.parse %w/--name lee/
79
+
80
+ assert_equal 'lee', slop[:name]
81
+ assert_equal 'lee', slop[:n]
82
+ end
83
+
84
+ test 'arguments ending ? test for option existance' do
85
+ slop = Slop.new
86
+ slop.opt :v, :verbose
87
+ slop.opt :d, :debug
88
+ slop.parse %w/--verbose/
89
+
90
+ assert slop[:verbose]
91
+ assert slop.verbose?
92
+
93
+ refute slop[:debug]
94
+ refute slop.debug?
95
+ end
96
+
97
+ test 'raises if an option expects an argument and none is given' do
98
+ slop = Slop.new
99
+ slop.opt :name, true
100
+ slop.opt :age, :optional => true
101
+
102
+ assert_raises(Slop::MissingArgumentError, /name/) { slop.parse %w/--name/ }
103
+ assert slop.parse %w/--name 'foo'/
104
+ end
105
+
106
+ test 'printing help' do
107
+ slop = Slop.new
108
+ slop.banner = 'Usage: foo [options]'
109
+ slop.parse
110
+
111
+ assert slop.to_s =~ /^Usage: foo/
112
+ end
113
+ end