slop 0.2.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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