options_by_example 4.1.0 → 4.2.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
  SHA256:
3
- metadata.gz: 3f146461611879c6934270f692e97391830d93f634b9300f7fa2883195bc0a22
4
- data.tar.gz: 19ad57569defbb334404085af3248675cfa84000511bbf49b2803b5c6cb38dee
3
+ metadata.gz: 59f737886cd6b035ebefb01b6d513d6a4cd66957858d9c3f9d4b489058fe5725
4
+ data.tar.gz: d3f6f2d559a4f5c077f878dec4e8fb8c8f9cf9e4daaeebaba8b5611f343ac86b
5
5
  SHA512:
6
- metadata.gz: 01146df8d0c06df84d2c093d0493219d6bd9d78ab5174f089b6b13b79b5fbb355ebd16f8bd5e3b65358670b44f6479e032e8967e57df52935328e5f151bfb4a1
7
- data.tar.gz: fbfe47bf367329b63e3661c28b964118c9dd47e013dabcfabdf6ceae9bdad3a2af9cd0998a852d5e3ede7a22b07146d64ae85fb61727aac54e1ced139075f918
6
+ metadata.gz: ad0b05e43b8ec21c9bbc434085896082fe0a956190d255c5ef24382f3f0ec8faf5f05b32f3acda272ca8d0fbbb63e736d9a5db4860e6bd750be26f7601a263c6
7
+ data.tar.gz: e2008e435bbfc129170599f7e28626ede57501b078e65684c436fea0644d69b8a564c388da1bd197a60332c4c34404d60424541dd9912307c0b2316ad069a811
data/README.md CHANGED
@@ -16,8 +16,8 @@ require %(options_by_example)
16
16
 
17
17
  flags = OptionsByExample.read(DATA).parse(ARGV)
18
18
 
19
- puts 'Feeling verbose today' if flags.include?(:verbose)
20
- puts flags.get(:words).sample(flags.get(:num))
19
+ puts 'Feeling verbose today' if flags.include_verbose?
20
+ puts flags.get_words.sample(flags.get_num)
21
21
 
22
22
  __END__
23
23
  Choose at random from a list of provided words.
@@ -46,7 +46,7 @@ class OptionsByExample
46
46
 
47
47
  def treat_everything_after_double_dash_as_positionals
48
48
  index = @slices.index { |head,| head == '--' }
49
- @slices[index..] = [@slices.drop(index).flatten] if index
49
+ @slices[index..-1] = [@slices.drop(index).flatten] if index
50
50
  end
51
51
 
52
52
  def exit_if_help_option
@@ -120,15 +120,32 @@ class OptionsByExample
120
120
  end
121
121
 
122
122
  option = current.shift # consume the option/flag
123
- option_name, has_argument, _ = @option_names[option]
123
+ option_name, argument_arity, _, _ = @option_names[option]
124
124
  @option_values[option_name] = true
125
125
 
126
- if has_argument
126
+ if argument_arity == :required
127
127
  raise "Expected argument for option '#{option}', got none" unless current.first
128
- @argument_values[option_name] = current.shift # consume the argument
129
- @option_took_argument = option
130
- else
131
- @option_took_argument = nil
128
+
129
+ if pending.length == 1
130
+ minimum_count = count_arguments(:required) + count_arguments(:vararg)
131
+ if current.length <= minimum_count
132
+ raise "Ambiguous argument for option '#{option}', not enough remaining positional arguments"
133
+ end
134
+ end
135
+
136
+ @argument_values[option_name] = current.shift
137
+ elsif argument_arity == :optional && current.first
138
+ if pending.length == 1
139
+ required_count = count_arguments(:required)
140
+ if current.length <= required_count and required_count == @argument_names.length
141
+ return current
142
+ end
143
+ if current.length <= required_count or required_count != @argument_names.length
144
+ raise "Ambiguous argument for option '#{option}', please use -- before positional arguments"
145
+ end
146
+ end
147
+
148
+ @argument_values[option_name] = current.shift
132
149
  end
133
150
 
134
151
  pending.shift if current.empty?
@@ -138,12 +155,12 @@ class OptionsByExample
138
155
  end
139
156
 
140
157
  def coerce_num_date_time_etc
141
- @option_names.each do |option, (each, argument_name, default_value)|
158
+ @option_names.each do |option, (each, _, argument_type, default_value)|
142
159
  value = @argument_values.fetch(each, default_value)
143
160
  next unless value
144
161
 
145
162
  begin
146
- case argument_name
163
+ case argument_type
147
164
  when 'NUM'
148
165
  expected_type = 'an integer value'
149
166
  value = Integer value
@@ -197,10 +214,6 @@ class OptionsByExample
197
214
  # :nocov:
198
215
  end
199
216
 
200
- if @option_took_argument
201
- msg += " (considering #{@option_took_argument} takes an argument)"
202
- end
203
-
204
217
  raise msg
205
218
  end
206
219
  end
@@ -23,7 +23,7 @@ class OptionsByExample
23
23
 
24
24
  usage_line = text.lines.grep(/Usage:/).first
25
25
  raise "Expected usage string, got none" unless usage_line
26
- tokens = usage_line.scan(/\[.*?\]|\w+ \.\.\.|\S+/)
26
+ tokens = usage_line.split(/(\[.*?\]?\]|\w+ \.\.\.)|\s+/).reject(&:empty?)
27
27
  raise "Expected usage line to start with 'Usage:'" unless tokens.shift == 'Usage:'
28
28
  raise "Expected command name on same line as 'Usage:'" unless tokens.shift
29
29
  tokens.shift if tokens.first == '[options]'
@@ -74,30 +74,42 @@ class OptionsByExample
74
74
 
75
75
  options = inline_options + text.lines.grep(/^\s*--?\w/)
76
76
  options.each do |string|
77
- tokens = string.scan(/--?\w[\w-]*(?: \w+)?|,|\(default \S+\)|\S+/)
77
+ tokens = string.strip.split(/(\s+)|(,)\s*/)
78
78
 
79
79
  short_form = nil
80
80
  long_form = nil
81
81
  option_name = nil
82
- argument_name = nil
82
+ argument_arity = nil
83
+ argument_type = nil
83
84
  default_value = nil
84
85
 
85
- if /^-(\w)( \w+)?$/ === tokens.first
86
- short_form, argument_name = tokens.shift.split
86
+ if /^-(\w)$/ === tokens.first
87
+ short_form = tokens.shift
87
88
  option_name = sanitize $1
88
- tokens.shift if ',' === tokens.first
89
+ tokens.shift if tokens.first == ','
89
90
  end
90
91
 
91
- if /^--([\w-]+)( \w+)?$/ === tokens.first
92
- long_form, argument_name = tokens.shift.split
92
+ if /^--([\w-]+)$/ === tokens.first
93
+ long_form = tokens.shift
93
94
  option_name = sanitize $1
94
95
  end
95
96
 
96
- if /^\(default (\S+)\)$/ === tokens.last
97
+ if tokens.shift == ' '
98
+ if /^(\[(.+)\]|(.+)\?)$/ === tokens.first
99
+ argument_type = $2 || $3
100
+ argument_arity = :optional
101
+ tokens.shift
102
+ else
103
+ argument_type = tokens.shift
104
+ argument_arity = :required
105
+ end
106
+ end
107
+
108
+ if /\(default\s+(\S+)\)$/ === tokens.join
97
109
  default_value = $1
98
110
  end
99
111
 
100
- ary = [option_name, argument_name, default_value]
112
+ ary = [option_name, argument_arity, argument_type, default_value]
101
113
  [short_form, long_form].each do |each|
102
114
  @option_names[each] = ary if each
103
115
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class OptionsByExample
4
- VERSION = '4.1.0'
4
+ VERSION = '4.2.0'
5
5
  end
6
6
 
7
7
 
@@ -11,6 +11,14 @@ __END__
11
11
  # Minor version bump when backward-compatible changes or enhancements
12
12
  # Patch version bump when backward-compatible bug fixes, security updates etc
13
13
 
14
+ 4.2.0
15
+ - Generate #get_NAME methods for arguments
16
+ - Keep #argument_NAME as backwards-compatible aliases
17
+ - New method #get_at_most_one returns the provided option or nil
18
+ - New method #get_mutually_exclusive aliases #get_at_most_one
19
+ - Deprecate #expect_at_most_one_of in favor of #expect_at_most_one
20
+ - Support optional option arguments, eg '--compress [NUM]'
21
+
14
22
  4.1.0
15
23
  - Treat everything after double-dash as positional arguments
16
24
  - Improve error message when usage is split across lines
@@ -32,16 +32,24 @@ class OptionsByExample
32
32
  end
33
33
 
34
34
  def expect_at_most_one_except(*extra_options)
35
- expect_at_most_one_of *(@options.keys - extra_options)
35
+ expect_at_most_one *(@options.keys - extra_options)
36
36
  end
37
37
 
38
38
  def expect_at_most_one_of(*mutually_exclusive_options)
39
+ expect_at_most_one *mutually_exclusive_options
40
+ end
41
+
42
+ def get_at_most_one(*mutually_exclusive_options)
39
43
  provided_options = @options.keys & mutually_exclusive_options
40
44
  if provided_options.length > 1
41
45
  abort "ERR: Found more than one mutually-exclusive option {#{provided_options.join ', '}}"
42
46
  end
47
+ provided_options.first
43
48
  end
44
49
 
50
+ alias expect_at_most_one get_at_most_one
51
+ alias get_mutually_exclusive get_at_most_one
52
+
45
53
  def fetch(*args, &block)
46
54
  @arguments.fetch(*args, &block)
47
55
  end
@@ -77,14 +85,16 @@ class OptionsByExample
77
85
  [
78
86
  *@usage_spec.argument_names.keys,
79
87
  *@usage_spec.option_names.values
80
- .map { |option_name, has_argument| option_name if has_argument }
88
+ .map { |option_name, argument_arity| option_name if argument_arity }
81
89
  .compact,
82
90
  ].each do |argument_name|
83
91
  instance_eval %{
84
- def argument_#{argument_name}
92
+ def get_#{argument_name}
85
93
  val = @arguments[:#{argument_name}]
86
94
  val && block_given? ? (yield val) : val
87
95
  end
96
+
97
+ alias argument_#{argument_name} get_#{argument_name}
88
98
  }
89
99
  end
90
100
  end
@@ -99,4 +109,3 @@ class OptionsByExample
99
109
  end
100
110
  end
101
111
  end
102
-
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: options_by_example
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Kuhn
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-04-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
13
  - akuhn@iam.unibe.ch
16
14
  executables: []
@@ -29,7 +27,6 @@ metadata:
29
27
  homepage_uri: https://github.com/akuhn/options_by_example
30
28
  source_code_uri: https://github.com/akuhn/options_by_example
31
29
  changelog_uri: https://github.com/akuhn/options_by_example/blob/master/lib/options_by_example/version.rb
32
- post_install_message:
33
30
  rdoc_options: []
34
31
  require_paths:
35
32
  - lib
@@ -44,8 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
41
  - !ruby/object:Gem::Version
45
42
  version: '0'
46
43
  requirements: []
47
- rubygems_version: 3.0.3.1
48
- signing_key:
44
+ rubygems_version: 3.6.9
49
45
  specification_version: 4
50
46
  summary: No-code options parser that extracts arguments directly from usage text.
51
47
  test_files: []