slop 3.6.0 → 4.0.0

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.
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