slop 3.6.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95327fe15a3e8091259a7a045f9ede744256ae52
4
- data.tar.gz: 6804548aa33795082ef1068a7c3021e3305258c5
3
+ metadata.gz: 52a2290f6608ad9ab25b6b4b07dd6ec213421edf
4
+ data.tar.gz: f752d959151a6f15f978922b389c2952553dfbc9
5
5
  SHA512:
6
- metadata.gz: c625631bde9b8289e1f50337de14139a762f12b873b2f94b7a1d406a3605202269dbef1ee19c8a3978379415afb902528ccc438f76fe3d563deb0a0f180f3413
7
- data.tar.gz: b8de8a12ee0aca903b74950009d10c4f66b437430c9983eb2ba61ef2ac988ce686cff56428f1caf0f3c2098d076ad9a3d4da5d901ce9768a2df5aae7b8870af7
6
+ metadata.gz: c6d233481d6ce8340a7ee563fb615187f3152f27211bf00f07ab006ec140d7f61ab427c92c3cb6a228351494ad5a7a5c8ee65d297af9f42c888d61bf240c4b6c
7
+ data.tar.gz: 97fcb2ff7f34071efa6b0b5091c6cc6058cc0c1d06e7f9669a27df4afb924fe083b3b1813cebfc3b761911c1d7230a95720ef8a298dfd645dfd1f99d9403a5c4
@@ -1,8 +1,5 @@
1
1
  rvm:
2
- - 1.9.3
3
- - 2.1
4
- - ruby-head
5
- - jruby-19mode
2
+ - 2.0.0
6
3
  notifications:
7
4
  email:
8
5
  on_success: change
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Lee Jarvis
1
+ Copyright (c) Lee Jarvis
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -2,201 +2,249 @@ Slop
2
2
  ====
3
3
 
4
4
  Slop is a simple option parser with an easy to remember syntax and friendly API.
5
- API Documentation is available [here](http://leejarvis.github.com/rdoc/slop/).
5
+
6
+ Version 4 of Slop is aimed at Ruby 2.0 or later. Please use
7
+ [Version 3](https://github.com/leejarvis/slop/tree/v3) for Ruby 1.9 support.
6
8
 
7
9
  [![Build Status](https://travis-ci.org/leejarvis/slop.png?branch=master)](http://travis-ci.org/leejarvis/slop)
8
10
 
11
+ Installation
12
+ ------------
13
+
14
+ gem install slop
15
+
9
16
  Usage
10
17
  -----
11
18
 
12
19
  ```ruby
13
- opts = Slop.parse do
14
- banner 'Usage: foo.rb [options]'
15
-
16
- on 'name=', 'Your name'
17
- on 'p', 'password', 'An optional password', argument: :optional
18
- on 'v', 'verbose', 'Enable verbose mode'
20
+ opts = Slop.parse do |o|
21
+ o.string '-h', '--host', 'a hostname'
22
+ o.integer '--port', 'custom port', default: 80
23
+ o.bool '-v', '--verbose', 'enable verbose mode'
24
+ o.bool '-q', '--quiet', 'suppress output (quiet mode)'
25
+ o.on '--version', 'print the version' do
26
+ puts Slop::VERSION
27
+ exit
28
+ end
19
29
  end
20
30
 
21
- # if ARGV is `--name Lee -v`
22
- opts.verbose? #=> true
23
- opts.password? #=> false
24
- opts[:name] #=> 'lee'
25
- opts.to_hash #=> {:name=>"Lee", :password=>nil, :verbose=>true}
26
- ```
31
+ ARGV #=> -v --host 192.168.0.1
27
32
 
28
- Installation
29
- ------------
33
+ opts[:host] #=> 192.168.0.1
34
+ opts.verbose? #=> true
35
+ opts.quiet? #=> false
30
36
 
31
- gem install slop
37
+ opts.to_hash #=> { host: "192.168.0.1", port: 80, verbose: true, quiet: false }
38
+ ```
32
39
 
33
- Printing Help
34
- -------------
40
+ Option types
41
+ ------------
35
42
 
36
- Slop attempts to build a good looking help string to print to your users. You
37
- can see this by calling `opts.help` or simply `puts opts`.
43
+ Built in Option types are as follows:
38
44
 
39
- Configuration Options
40
- ---------------------
45
+ ```ruby
46
+ o.string #=> Slop::StringOption, expects an argument
47
+ o.bool #=> Slop::BoolOption, no argument, aliased to BooleanOption
48
+ o.integer #=> Slop::IntegerOption, expects an argument, aliased to IntOption
49
+ o.array #=> Slop::ArrayOption, expects an argument
50
+ o.null #=> Slop::NullOption, no argument and ignored from `to_hash`
51
+ o.on #=> alias for o.null
52
+ ```
41
53
 
42
- All of these options can be sent to `Slop.new` or `Slop.parse` in Hash form.
54
+ You can see all built in types in `slop/types.rb`. Suggestions or pull requests
55
+ for more types are welcome.
43
56
 
44
- | Option | Description | Default/Example |
45
- | ------ | ----------- | --------------- |
46
- | strict | Enable strict mode. Slop will raise an `InvalidOptionError` for unkown options. | `false` |
47
- | help | Automatically add the `--help` option. | `false` |
48
- | banner | Set the help banner text. | `nil` |
49
- | ignore_case | When enabled, `-A` will look for the `-a` option if `-A` does not exist. | `false` |
50
- | autocreate | Autocreate options on the fly. | `false` |
51
- | arguments | Force all options to expect arguments. | `false` |
52
- | optional_arguments | Force all options to accept optional arguments. | `false` |
53
- | multiple_switches | When disabled, Slop will parse `-abc` as the option `a` with the argument `bc` rather than 3 separate options. | `true` |
57
+ Advanced Usage
58
+ --------------
54
59
 
55
- Lists
56
- -----
60
+ This example is really just to describe how the underlying API works.
61
+ It's not necessarily the best way to do it.
57
62
 
58
63
  ```ruby
59
- opts = Slop.parse do
60
- on :list=, as: Array
61
- end
62
- # ruby run.rb --list one,two
63
- opts[:list] #=> ["one", "two"]
64
- # ruby run.rb --list one,two --list three
65
- opts[:list] #=> ["one", "two", "three"]
64
+ opts = Slop::Options.new
65
+ opts.banner = "usage: connect [options] ..."
66
+ opts.separator ""
67
+ opts.separator "Connection options:"
68
+ opts.string "-H", "--hostname", "a hostname"
69
+ opts.int "-p", "--port", "a port", default: 80
70
+ opts.separator ""
71
+ opts.separator "Extra options:"
72
+ opts.array "--files", "a list of files to import"
73
+ opts.bool "-v", "--verbose", "enable verbose mode"
74
+
75
+ parser = Slop::Parser.new(opts)
76
+ result = parser.parse(["--hostname", "192.168.0.1"])
77
+
78
+ result.to_hash #=> { hostname: "192.168.0.1", port: 80,
79
+ # files: [], verbose: false }
80
+
81
+ puts opts # prints out help
66
82
  ```
67
83
 
68
- You can also specify a delimiter and limit.
84
+ Arguments
85
+ ---------
86
+
87
+ It's common to want to retrieve an array of arguments that were not processed
88
+ by the parser (i.e options or consumed arguments). You can do that with the
89
+ `Result#arguments` method:
69
90
 
70
91
  ```ruby
71
- opts = Slop.parse do
72
- on :list=, as: Array, delimiter: ':', limit: 2
92
+ args = %w(connect --host google.com GET)
93
+ opts = Slop.parse args do |o|
94
+ o.string '--host'
73
95
  end
74
- # ruby run.rb --list one:two:three
75
- opts[:list] #=> ["one", "two:three"]
96
+
97
+ p opts.arguments #=> ["connect", "GET"] # also aliased to `args`
76
98
  ```
77
99
 
78
- Ranges
100
+ Arrays
79
101
  ------
80
102
 
103
+ Slop has a built in `ArrayOption` for handling array values:
104
+
81
105
  ```ruby
82
- opts = Slop.parse do
83
- on :range=, as: Range
106
+ opts = Slop.parse do |o|
107
+ # the delimiter defaults to ','
108
+ o.array '--files', 'a list of files', delimiter: ','
84
109
  end
85
- # ruby run.rb --range 1..10
86
- opts[:range] #=> 1..10
87
- # ruby run.rb --range 1...10
88
- opts[:range] #=> 1...10
89
- # ruby run.rb --range 1-10
90
- opts[:range] #=> 1..10
91
- # ruby run.rb --range 1,10
92
- opts[:range] #=> 1..10
93
- ```
94
-
95
- Autocreate
96
- ----------
97
110
 
98
- Slop has an 'autocreate' feature. This feature is intended to create
99
- options on the fly, without having to specify them yourself. In some case,
100
- using this code could be all you need in your application:
101
-
102
- ```ruby
103
- # ruby run.rb --foo bar --baz --name lee
104
- opts = Slop.parse(autocreate: true)
105
- opts.to_hash #=> {:foo=>"bar", :baz=>true, :name=>"lee"}
106
- opts.fetch_option(:name).expects_argument? #=> true
111
+ # both of these will return o[:files] as ["foo.txt", "bar.rb"]:
112
+ # --files foo.txt,bar.rb
113
+ # --files foo.txt --files bar.rb
107
114
  ```
108
115
 
109
- Commands
110
- --------
116
+ Custom option types
117
+ -------------------
111
118
 
112
- Slop supports git style sub-commands, like so:
119
+ Slop uses option type classes for every new option added. They default to the
120
+ `NullOption`. When you type `o.array` Slop looks for an option called
121
+ `Slop::ArrayOption`. This class must contain at least 1 method, `call`. This
122
+ method is executed at parse time, and the return value of this method is
123
+ used for the option value. We can use this to build custom option types:
113
124
 
114
125
  ```ruby
115
- opts = Slop.parse do
116
- on '-v', 'Print the version' do
117
- puts "Version 1.0"
126
+ module Slop
127
+ class PathOption < Option
128
+ def call(value)
129
+ Pathname.new(value)
130
+ end
118
131
  end
132
+ end
119
133
 
120
- command 'add' do
121
- on :v, :verbose, 'Enable verbose mode'
122
- on :name=, 'Your name'
134
+ opts = Slop.parse %w(--path ~/) do |o|
135
+ o.path '--path', 'a custom path name'
136
+ end
123
137
 
124
- run do |opts, args|
125
- puts "You ran 'add' with options #{opts.to_hash} and args: #{args.inspect}"
138
+ p opts[:path] #=> #<Pathname:~/>
139
+ ```
140
+
141
+ Custom options can also implement a `finish` method. This method by default
142
+ does nothing, but it's executed once *all* options have been parsed. This
143
+ allows us to go back and mutate state without having to rely on options
144
+ being parsed in a particular order. Here's an example:
145
+
146
+ ```ruby
147
+ module Slop
148
+ class FilesOption < ArrayOption
149
+ def finish(opts)
150
+ if opts.expand?
151
+ self.value = value.map { |f| File.expand_path(f) }
152
+ end
126
153
  end
127
154
  end
128
155
  end
129
156
 
130
- # ruby run.rb -v
131
- #=> Version 1.0
132
- # ruby run.rb add -v foo --name Lee
133
- #=> You ran 'add' with options {:verbose=>true,:name=>"Lee"} and args ["foo"]
134
- opts.to_hash(true) # Pass true to tell Slop to merge sub-command option values.
135
- # => { :v => nil, :add => { :v => true, :name => "Lee" } }
157
+ opts = Slop.parse %w(--files foo.txt,bar.rb -e) do |o|
158
+ o.files '--files', 'an array of files'
159
+ o.bool '-e', '--expand', 'if used, list of files will be expanded'
160
+ end
161
+
162
+ p opts[:files] #=> ["/full/path/foo.txt", "/full/path/bar.rb"]
136
163
  ```
137
164
 
138
- Remaining arguments
139
- -------------------
165
+ Errors
166
+ ------
140
167
 
141
- The *parse!* method will remove any options and option arguments from the original Array:
168
+ Slop will raise errors for the following:
142
169
 
143
- ```ruby
144
- # restarguments.rb
145
- opts = Slop.parse! do
146
- on :foo
147
- end
148
- ```
170
+ * An option used without an argument when it expects one: `Slop::MissingArgument`
171
+ * An option used that Slop doesn't know about: `Slop::UnknownOption`
149
172
 
150
- Example:
173
+ These errors inherit from `Slop::Error`, so you can rescue them all.
174
+ Alternatively you can suppress these errors with the `suppress_errors` config
175
+ option:
151
176
 
152
- ```
153
- ruby restarguments.rb --foo bar
154
- ```
177
+ ```ruby
178
+ opts = Slop.parse suppress_errors: true do
179
+ o.string '-name'
180
+ end
155
181
 
156
- ```
157
- opts.to_hash = { :foo => true }
182
+ # or per option:
158
183
 
159
- ARGV #=> ["bar"]
184
+ opts = Slop.parse do
185
+ o.string '-host', suppress_errors: true
186
+ o.int '-port'
187
+ end
160
188
  ```
161
189
 
162
- Woah woah, why you hating on OptionParser?
163
- ------------------------------------------
190
+ Printing help
191
+ -------------
164
192
 
165
- I'm not, honestly! I love OptionParser. I really do, it's a fantastic library.
166
- So why did I build Slop? Well, I find myself using OptionParser to simply
167
- gather a bunch of key/value options, usually you would do something like this:
193
+ The return value of `Slop.parse` is a `Slop::Result` which provides a nice
194
+ help string to display your options. Just `puts opts` or call `opts.to_s`:
168
195
 
169
196
  ```ruby
170
- require 'optparse'
197
+ opts = Slop.parse do |o|
198
+ o.string '-h', '--host', 'hostname'
199
+ o.int '-p', '--port', 'port (default: 80)', default: 80
200
+ o.string '--username'
201
+ o.separator ''
202
+ o.separator 'other options:'
203
+ o.bool '--quiet', 'suppress output'
204
+ o.on '-v', '--version' do
205
+ puts "1.1.1"
206
+ end
207
+ end
171
208
 
172
- things = {}
209
+ puts opts
210
+ ```
173
211
 
174
- opt = OptionParser.new do |opt|
175
- opt.on('-n', '--name NAME', 'Your name') do |name|
176
- things[:name] = name
177
- end
212
+ Output:
178
213
 
179
- opt.on('-a', '--age AGE', 'Your age') do |age|
180
- things[:age] = age.to_i
181
- end
214
+ ```
215
+ % ruby run.rb
216
+ usage: run.rb [options]
217
+ -h, --host hostname
218
+ -p, --port port (default: 80)
219
+ --username
220
+
221
+ other options:
222
+ --quiet suppress output
223
+ -v, --version
224
+ ```
182
225
 
183
- # you get the point
184
- end
226
+ This method takes an optional `prefix` value, which defaults to `" " * 4`:
185
227
 
186
- opt.parse
187
- things #=> { :name => 'lee', :age => 105 }
228
+ ```
229
+ puts opts.to_s(prefix: " ")
188
230
  ```
189
231
 
190
- Which is all great and stuff, but it can lead to some repetition. The same
191
- thing in Slop:
232
+ It'll deal with aligning your descriptions according to the longest option
233
+ flag.
192
234
 
193
- ```ruby
194
- require 'slop'
235
+ Here's an example of adding your own help option:
195
236
 
196
- opts = Slop.parse do
197
- on :n, :name=, 'Your name'
198
- on :a, :age=, 'Your age', as: Integer
237
+ ```ruby
238
+ o.on '--help' do
239
+ puts o
240
+ exit
199
241
  end
200
-
201
- opts.to_hash #=> { :name => 'lee', :age => 105 }
202
242
  ```
243
+
244
+ Commands
245
+ --------
246
+
247
+ As of version 4, Slop does not have built in support for git-style subcommands.
248
+ You can use version 3 of Slop (see `v3` branch). I also expect there to be some
249
+ external libraries released soon that wrap around Slop to provide support for
250
+ this feature. I'll update this document when that happens.
@@ -1,687 +1,56 @@
1
1
  require 'slop/option'
2
- require 'slop/commands'
2
+ require 'slop/options'
3
+ require 'slop/parser'
4
+ require 'slop/result'
5
+ require 'slop/types'
6
+ require 'slop/error'
3
7
 
4
- class Slop
5
- include Enumerable
8
+ module Slop
9
+ # The current version of Slop, of course.
10
+ VERSION = '4.0.0'
6
11
 
7
- VERSION = '3.6.0'
8
-
9
- # The main Error class, all Exception classes inherit from this class.
10
- class Error < StandardError; end
11
-
12
- # Raised when an option argument is expected but none are given.
13
- class MissingArgumentError < Error; end
14
-
15
- # Raised when an option is expected/required but not present.
16
- class MissingOptionError < Error; end
17
-
18
- # Raised when an argument does not match its intended match constraint.
19
- class InvalidArgumentError < Error; end
20
-
21
- # Raised when an invalid option is found and the strict flag is enabled.
22
- class InvalidOptionError < Error; end
23
-
24
- # Raised when an invalid command is found and the strict flag is enabled.
25
- class InvalidCommandError < Error; end
26
-
27
- # Returns a default Hash of configuration options this Slop instance uses.
28
- DEFAULT_OPTIONS = {
29
- :strict => false,
30
- :help => false,
31
- :banner => nil,
32
- :ignore_case => false,
33
- :autocreate => false,
34
- :arguments => false,
35
- :optional_arguments => false,
36
- :multiple_switches => true,
37
- :longest_flag => 0
38
- }
39
-
40
- class << self
41
-
42
- # items - The Array of items to extract options from (default: ARGV).
43
- # config - The Hash of configuration options to send to Slop.new().
44
- # block - An optional block used to add options.
45
- #
46
- # Examples:
47
- #
48
- # Slop.parse(ARGV, :help => true) do
49
- # on '-n', '--name', 'Your username', :argument => true
50
- # end
51
- #
52
- # Returns a new instance of Slop.
53
- def parse(items = ARGV, config = {}, &block)
54
- parse! items.dup, config, &block
55
- end
56
-
57
- # items - The Array of items to extract options from (default: ARGV).
58
- # config - The Hash of configuration options to send to Slop.new().
59
- # block - An optional block used to add options.
60
- #
61
- # Returns a new instance of Slop.
62
- def parse!(items = ARGV, config = {}, &block)
63
- config, items = items, ARGV if items.is_a?(Hash) && config.empty?
64
- slop = new config, &block
65
- slop.parse! items
66
- slop
67
- end
68
-
69
- # Build a Slop object from a option specification.
70
- #
71
- # This allows you to design your options via a simple String rather
72
- # than programatically. Do note though that with this method, you're
73
- # unable to pass any advanced options to the on() method when creating
74
- # options.
75
- #
76
- # string - The optspec String
77
- # config - A Hash of configuration options to pass to Slop.new
78
- #
79
- # Examples:
80
- #
81
- # opts = Slop.optspec(<<-SPEC)
82
- # ruby foo.rb [options]
83
- # ---
84
- # n,name= Your name
85
- # a,age= Your age
86
- # A,auth Sign in with auth
87
- # p,passcode= Your secret pass code
88
- # SPEC
89
- #
90
- # opts.fetch_option(:name).description #=> "Your name"
91
- #
92
- # Returns a new instance of Slop.
93
- def optspec(string, config = {})
94
- warn "[DEPRECATED] `Slop.optspec` is deprecated and will be removed in version 4"
95
- config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
96
- lines = optspec.split("\n").reject(&:empty?)
97
- opts = Slop.new(config)
98
-
99
- lines.each do |line|
100
- opt, description = line.split(' ', 2)
101
- short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
102
- opt = opts.on(short, long, description)
103
-
104
- if long && long.end_with?('=')
105
- long.sub!(/\=$/, '')
106
- opt.config[:argument] = true
107
- end
108
- end
109
-
110
- opts
111
- end
112
-
113
- end
114
-
115
- # The Hash of configuration options for this Slop instance.
116
- attr_reader :config
117
-
118
- # The Array of Slop::Option objects tied to this Slop instance.
119
- attr_reader :options
120
-
121
- # The Hash of sub-commands for this Slop instance.
122
- attr_reader :commands
123
-
124
- # Create a new instance of Slop and optionally build options via a block.
125
- #
126
- # config - A Hash of configuration options.
127
- # block - An optional block used to specify options.
128
- def initialize(config = {}, &block)
129
- @config = DEFAULT_OPTIONS.merge(config)
130
- @options = []
131
- @commands = {}
132
- @trash = []
133
- @triggered_options = []
134
- @unknown_options = []
135
- @callbacks = {}
136
- @separators = {}
137
- @runner = nil
138
- @command = config.delete(:command)
139
-
140
- if block_given?
141
- block.arity == 1 ? yield(self) : instance_eval(&block)
142
- end
143
-
144
- if config[:help]
145
- on('-h', '--help', 'Display this help message.', :tail => true) do
146
- puts help
147
- exit
148
- end
149
- end
150
- end
151
-
152
- # Is strict mode enabled?
153
- #
154
- # Returns true if strict mode is enabled, false otherwise.
155
- def strict?
156
- config[:strict]
157
- end
158
-
159
- # Set the banner.
160
- #
161
- # banner - The String to set the banner.
162
- def banner=(banner)
163
- config[:banner] = banner
164
- end
165
-
166
- # Get or set the banner.
167
- #
168
- # banner - The String to set the banner.
169
- #
170
- # Returns the banner String.
171
- def banner(banner = nil)
172
- config[:banner] = banner if banner
173
- config[:banner]
174
- end
175
-
176
- # Set the description (used for commands).
177
- #
178
- # desc - The String to set the description.
179
- def description=(desc)
180
- config[:description] = desc
181
- end
182
-
183
- # Get or set the description (used for commands).
184
- #
185
- # desc - The String to set the description.
186
- #
187
- # Returns the description String.
188
- def description(desc = nil)
189
- config[:description] = desc if desc
190
- config[:description]
191
- end
192
-
193
- # Add a new command.
194
- #
195
- # command - The Symbol or String used to identify this command.
196
- # options - A Hash of configuration options (see Slop::new)
197
- #
198
- # Returns a new instance of Slop mapped to this command.
199
- def command(command, options = {}, &block)
200
- options = @config.merge(options)
201
- @commands[command.to_s] = Slop.new(options.merge(:command => command.to_s), &block)
202
- end
203
-
204
- # Parse a list of items, executing and gathering options along the way.
205
- #
206
- # items - The Array of items to extract options from (default: ARGV).
207
- # block - An optional block which when used will yield non options.
208
- #
209
- # Returns an Array of original items.
210
- def parse(items = ARGV, &block)
211
- parse! items.dup, &block
212
- items
213
- end
214
-
215
- # Parse a list of items, executing and gathering options along the way.
216
- # unlike parse() this method will remove any options and option arguments
217
- # from the original Array.
218
- #
219
- # items - The Array of items to extract options from (default: ARGV).
220
- # block - An optional block which when used will yield non options.
221
- #
222
- # Returns an Array of original items with options removed.
223
- def parse!(items = ARGV, &block)
224
- if items.empty? && @callbacks[:empty]
225
- @callbacks[:empty].each { |cb| cb.call(self) }
226
- return items
227
- end
228
-
229
- # reset the trash so it doesn't carry over if you parse multiple
230
- # times with the same instance
231
- @trash.clear
232
-
233
- if cmd = @commands[items[0]]
234
- items.shift
235
- return cmd.parse! items
236
- end
237
-
238
- items.each_with_index do |item, index|
239
- @trash << index && break if item == '--'
240
- autocreate(items, index) if config[:autocreate]
241
- process_item(items, index, &block) unless @trash.include?(index)
242
- end
243
- items.reject!.with_index { |item, index| @trash.include?(index) }
244
-
245
- missing_options = options.select { |opt| opt.required? && opt.count < 1 }
246
- if missing_options.any?
247
- raise MissingOptionError,
248
- "Missing required option(s): #{missing_options.map(&:key).join(', ')}"
249
- end
250
-
251
- if @unknown_options.any?
252
- raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
253
- end
254
-
255
- if @triggered_options.empty? && @callbacks[:no_options]
256
- @callbacks[:no_options].each { |cb| cb.call(self) }
257
- end
258
-
259
- if @runner.respond_to?(:call)
260
- @runner.call(self, items) unless config[:help] and present?(:help)
261
- end
262
-
263
- items
264
- end
265
-
266
- # Add an Option.
267
- #
268
- # objects - An Array with an optional Hash as the last element.
269
- #
270
- # Examples:
271
- #
272
- # on '-u', '--username=', 'Your username'
273
- # on :v, :verbose, 'Enable verbose mode'
274
- #
275
- # Returns the created instance of Slop::Option.
276
- def on(*objects, &block)
277
- option = build_option(objects, &block)
278
- original = options.find do |o|
279
- o.long and o.long == option.long or o.short and o.short == option.short
280
- end
281
- options.delete(original) if original
282
- options << option
283
- option
284
- end
285
- alias option on
286
- alias opt on
287
-
288
- # Fetch an options argument value.
289
- #
290
- # key - The Symbol or String option short or long flag.
291
- #
292
- # Returns the Object value for this option, or nil.
293
- def [](key)
294
- option = fetch_option(key)
295
- option.value if option
296
- end
297
- alias get []
298
-
299
- # Returns a new Hash with option flags as keys and option values as values.
300
- #
301
- # include_commands - If true, merge options from all sub-commands.
302
- def to_hash(include_commands = false)
303
- hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
304
- if include_commands
305
- @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
306
- end
307
- hash
308
- end
309
- alias to_h to_hash
310
-
311
- # Enumerable interface. Yields each Slop::Option.
312
- def each(&block)
313
- options.each(&block)
314
- end
315
-
316
- # Specify code to be executed when these options are parsed.
317
- #
318
- # callable - An object responding to a call method.
319
- #
320
- # yields - The instance of Slop parsing these options
321
- # An Array of unparsed arguments
12
+ # Parse an array of options (defaults to ARGV). Accepts an
13
+ # optional hash of configuration options and block.
322
14
  #
323
15
  # Example:
324
16
  #
325
- # Slop.parse do
326
- # on :v, :verbose
327
- #
328
- # run do |opts, args|
329
- # puts "Arguments: #{args.inspect}" if opts.verbose?
330
- # end
17
+ # opts = Slop.parse(["-host", "localhost"]) do |o|
18
+ # o.string '-host', 'a hostname', default: '0.0.0.0'
331
19
  # end
332
- def run(callable = nil, &block)
333
- @runner = callable || block
334
- unless @runner.respond_to?(:call)
335
- raise ArgumentError, "You must specify a callable object or a block to #run"
336
- end
337
- end
338
-
339
- # Check for an options presence.
340
- #
341
- # Examples:
20
+ # opts.to_hash #=> { host: 'localhost' }
342
21
  #
343
- # opts.parse %w( --foo )
344
- # opts.present?(:foo) #=> true
345
- # opts.present?(:bar) #=> false
346
- #
347
- # Returns true if all of the keys are present in the parsed arguments.
348
- def present?(*keys)
349
- keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
350
- end
351
-
352
- # Override this method so we can check if an option? method exists.
353
- #
354
- # Returns true if this option key exists in our list of options.
355
- def respond_to_missing?(method_name, include_private = false)
356
- options.any? { |o| o.key == method_name.to_s.chop } || super
357
- end
358
-
359
- # Fetch a list of options which were missing from the parsed list.
360
- #
361
- # Examples:
362
- #
363
- # opts = Slop.new do
364
- # on :n, :name=
365
- # on :p, :password=
366
- # end
367
- #
368
- # opts.parse %w[ --name Lee ]
369
- # opts.missing #=> ['password']
370
- #
371
- # Returns an Array of Strings representing missing options.
372
- def missing
373
- (options - @triggered_options).map(&:key)
374
- end
375
-
376
- # Fetch a Slop::Option object.
377
- #
378
- # key - The Symbol or String option key.
379
- #
380
- # Examples:
381
- #
382
- # opts.on(:foo, 'Something fooey', :argument => :optional)
383
- # opt = opts.fetch_option(:foo)
384
- # opt.class #=> Slop::Option
385
- # opt.accepts_optional_argument? #=> true
386
- #
387
- # Returns an Option or nil if none were found.
388
- def fetch_option(key)
389
- options.find { |option| [option.long, option.short].include?(clean(key)) }
390
- end
391
-
392
- # Fetch a Slop object associated with this command.
393
- #
394
- # command - The String or Symbol name of the command.
395
- #
396
- # Examples:
397
- #
398
- # opts.command :foo do
399
- # on :v, :verbose, 'Enable verbose mode'
400
- # end
401
- #
402
- # # ruby run.rb foo -v
403
- # opts.fetch_command(:foo).verbose? #=> true
404
- def fetch_command(command)
405
- @commands[command.to_s]
406
- end
407
-
408
- # Add a callback.
409
- #
410
- # label - The Symbol identifier to attach this callback.
411
- #
412
- # Returns nothing.
413
- def add_callback(label, &block)
414
- (@callbacks[label] ||= []) << block
415
- end
416
-
417
- # Add string separators between options.
418
- #
419
- # text - The String text to print.
420
- def separator(text)
421
- if @separators[options.size]
422
- @separators[options.size] << "\n#{text}"
423
- else
424
- @separators[options.size] = text
425
- end
426
- end
427
-
428
- # Print a handy Slop help string.
429
- #
430
- # Returns the banner followed by available option help strings.
431
- def to_s
432
- heads = options.reject(&:tail?)
433
- tails = (options - heads)
434
- opts = (heads + tails).select(&:help).map(&:to_s)
435
- optstr = opts.each_with_index.map { |o, i|
436
- (str = @separators[i + 1]) ? [o, str].join("\n") : o
437
- }.join("\n")
438
-
439
- if @commands.any?
440
- optstr << "\n" if !optstr.empty?
441
- optstr << "\nAvailable commands:\n\n"
442
- optstr << commands_to_help
443
- optstr << "\n\nSee `<command> --help` for more information on a specific command."
444
- end
445
-
446
- banner = config[:banner]
447
- if banner.nil?
448
- banner = "Usage: #{File.basename($0, '.*')}"
449
- banner << " #{@command}" if @command
450
- banner << " [command]" if @commands.any?
451
- banner << " [options]"
452
- end
453
- if banner
454
- "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
455
- else
456
- optstr
457
- end
458
- end
459
- alias help to_s
460
-
461
- private
462
-
463
- # Convenience method for present?(:option).
464
- #
465
- # Examples:
466
- #
467
- # opts.parse %( --verbose )
468
- # opts.verbose? #=> true
469
- # opts.other? #=> false
470
- #
471
- # Returns true if this option is present. If this method does not end
472
- # with a ? character it will instead call super().
473
- def method_missing(method, *args, &block)
474
- meth = method.to_s
475
- if meth.end_with?('?')
476
- meth.chop!
477
- present?(meth) || present?(meth.gsub('_', '-'))
478
- else
479
- super
480
- end
481
- end
482
-
483
- # Process a list item, figure out if it's an option, execute any
484
- # callbacks, assign any option arguments, and do some sanity checks.
485
- #
486
- # items - The Array of items to process.
487
- # index - The current Integer index of the item we want to process.
488
- # block - An optional block which when passed will yield non options.
489
- #
490
- # Returns nothing.
491
- def process_item(items, index, &block)
492
- return unless item = items[index]
493
- option, argument = extract_option(item) if item.start_with?('-')
494
-
495
- if option
496
- option.count += 1 unless item.start_with?('--no-')
497
- option.count += 1 if option.key[0, 3] == "no-"
498
- @trash << index
499
- @triggered_options << option
500
-
501
- if option.expects_argument?
502
- argument ||= items.at(index + 1)
503
-
504
- if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
505
- raise MissingArgumentError, "#{option.key} expects an argument"
506
- end
507
-
508
- execute_option(option, argument, index, item)
509
- elsif option.accepts_optional_argument?
510
- argument ||= items.at(index + 1)
511
-
512
- if argument && argument =~ /\A([^\-?]|-\d)+/
513
- execute_option(option, argument, index, item)
514
- else
515
- option.call(nil)
516
- end
517
- elsif config[:multiple_switches] && argument
518
- execute_multiple_switches(option, argument, items, index)
519
- else
520
- option.value = option.count > 0
521
- option.call(nil)
522
- end
523
- else
524
- @unknown_options << item if strict? && item =~ /\A--?/
525
- block.call(item) if block && !@trash.include?(index)
526
- end
527
- end
528
-
529
- # Execute an option, firing off callbacks and assigning arguments.
530
- #
531
- # option - The Slop::Option object found by #process_item.
532
- # argument - The argument Object to assign to this option.
533
- # index - The current Integer index of the object we're processing.
534
- # item - The optional String item we're processing.
535
- #
536
- # Returns nothing.
537
- def execute_option(option, argument, index, item = nil)
538
- if !option
539
- if config[:multiple_switches] && strict?
540
- raise InvalidOptionError, "Unknown option -#{item}"
541
- end
542
- return
543
- end
544
-
545
- if argument
546
- unless item && item.end_with?("=#{argument}")
547
- @trash << index + 1 unless option.argument_in_value
548
- end
549
- option.value = argument
550
- else
551
- option.value = option.count > 0
552
- end
553
-
554
- if option.match? && !argument.match(option.config[:match])
555
- raise InvalidArgumentError, "#{argument} is an invalid argument"
556
- end
557
-
558
- option.call(option.value)
559
- end
560
-
561
- # Execute a `-abc` type option where a, b and c are all options. This
562
- # method is only executed if the multiple_switches argument is true.
563
- #
564
- # option - The first Option object.
565
- # argument - The argument to this option. (Split into multiple Options).
566
- # items - The Array of items currently being parsed.
567
- # index - The index of the current item being processed.
568
- #
569
- # Returns nothing.
570
- def execute_multiple_switches(option, argument, items, index)
571
- execute_option(option, nil, index)
572
- flags = argument.split('')
573
- flags.each do |key|
574
- if opt = fetch_option(key)
575
- opt.count += 1
576
- if (opt.expects_argument? || opt.accepts_optional_argument?) &&
577
- (flags[-1] == opt.key) && (val = items[index+1])
578
- execute_option(opt, val, index, key)
579
- else
580
- execute_option(opt, nil, index, key)
581
- end
582
- else
583
- raise InvalidOptionError, "Unknown option -#{key}" if strict?
584
- end
585
- end
22
+ # Returns a Slop::Result.
23
+ def self.parse(items = ARGV, **config, &block)
24
+ Options.new(config, &block).parse(items)
586
25
  end
587
26
 
588
- # Extract an option from a flag.
589
- #
590
- # flag - The flag key used to extract an option.
591
- #
592
- # Returns an Array of [option, argument].
593
- def extract_option(flag)
594
- option = fetch_option(flag)
595
- option ||= fetch_option(flag.downcase) if config[:ignore_case]
596
- option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
597
-
598
- unless option
599
- case flag
600
- when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
601
- option, argument = fetch_option($1), ($2 || false)
602
- option.argument_in_value = true if option
603
- end
604
- end
605
-
606
- [option, argument]
607
- end
608
-
609
- # Autocreate an option on the fly. See the :autocreate Slop config option.
27
+ # Example:
610
28
  #
611
- # items - The Array of items we're parsing.
612
- # index - The current Integer index for the item we're processing.
29
+ # Slop.option_defined?(:string) #=> true
30
+ # Slop.option_defined?(:omg) #=> false
613
31
  #
614
- # Returns nothing.
615
- def autocreate(items, index)
616
- flag = items[index]
617
- if !fetch_option(flag) && !@trash.include?(index)
618
- option = build_option(Array(flag))
619
- argument = items[index + 1]
620
- option.config[:argument] = (argument && argument !~ /\A--?/)
621
- option.config[:autocreated] = true
622
- options << option
623
- end
32
+ # Returns true if an option is defined.
33
+ def self.option_defined?(name)
34
+ const_defined?(string_to_option(name.to_s))
624
35
  end
625
36
 
626
- # Build an option from a list of objects.
627
- #
628
- # objects - An Array of objects used to build this option.
37
+ # Example:
629
38
  #
630
- # Returns a new instance of Slop::Option.
631
- def build_option(objects, &block)
632
- config = {}
633
- config[:argument] = true if @config[:arguments]
634
- config[:optional_argument] = true if @config[:optional_arguments]
635
-
636
- if objects.last.is_a?(Hash)
637
- config.merge!(objects.pop)
638
- end
639
-
640
- short = extract_short_flag(objects, config)
641
- long = extract_long_flag(objects, config)
642
- desc = objects.shift if objects[0].respond_to?(:to_str)
643
-
644
- Option.new(self, short, long, desc, config, &block)
645
- end
646
-
647
- def extract_short_flag(objects, config)
648
- flag = objects[0].to_s
649
- if flag =~ /\A-?\w=?\z/
650
- config[:argument] ||= flag.end_with?('=')
651
- objects.shift
652
- flag.delete('-=')
653
- end
654
- end
655
-
656
- # Extract the long flag from an item.
39
+ # Slop.string_to_option("string") #=> "StringOption"
40
+ # Slop.string_to_option("some_thing") #=> "SomeThingOption"
657
41
  #
658
- # objects - The Array of objects passed from #build_option.
659
- # config - The Hash of configuration options built in #build_option.
660
- def extract_long_flag(objects, config)
661
- flag = objects.first.to_s
662
- if flag =~ /\A(?:--?)?[a-zA-Z0-9][a-zA-Z0-9_.-]+\=?\??\z/
663
- config[:argument] ||= true if flag.end_with?('=')
664
- config[:optional_argument] = true if flag.end_with?('=?')
665
- objects.shift
666
- clean(flag).sub(/\=\??\z/, '')
667
- end
42
+ # Returns a camel-cased class looking string with Option suffix.
43
+ def self.string_to_option(s)
44
+ s.gsub(/(?:^|_)([a-z])/) { $1.capitalize } + "Option"
668
45
  end
669
46
 
670
- # Remove any leading -- characters from a string.
47
+ # Example:
671
48
  #
672
- # object - The Object we want to cast to a String and clean.
49
+ # Slop.string_to_option_class("string") #=> Slop::StringOption
50
+ # Slop.string_to_option_class("foo") #=> uninitialized constant FooOption
673
51
  #
674
- # Returns the newly cleaned String with leading -- characters removed.
675
- def clean(object)
676
- object.to_s.sub(/\A--?/, '')
52
+ # Returns the full qualified option class. Uses `#string_to_option`.
53
+ def self.string_to_option_class(s)
54
+ const_get(string_to_option(s))
677
55
  end
678
-
679
- def commands_to_help
680
- padding = 0
681
- @commands.each { |c, _| padding = c.size if c.size > padding }
682
- @commands.map do |cmd, opts|
683
- " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
684
- end.join("\n")
685
- end
686
-
687
56
  end