switches.rb 0.11.0 → 0.12.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 +4 -4
- data/CHANGELOG +7 -0
- data/README.md +1 -0
- data/lib/Switches/VERSION.rb +1 -1
- data/lib/Switches.rb +11 -0
- data/spec/all.rb +45 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60984de78f4a7074bd1f41f4293bcf5c4fb4d837c138bb59f2b7d3519cf17372
|
|
4
|
+
data.tar.gz: ed1fd8d58a77fd2335d047dea552326759291b7f985cc027004b0bde774e603b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '080a361c32b9e9db115abe54e677cbd4ceff39d8f895c8a2d4e4819bf27b81393e5f3636550f12b52e547d9aa58398302d29506fe67c0ff9e2215a27f78f6aa9'
|
|
7
|
+
data.tar.gz: 4dc836fb4dc0195323b31013af2367657eb1d8ac21cb87a1ec09d0dc7b2d0e6aebe164ae49475a866053765ec5b9047734d64e580076fcf78f75c802107dc569
|
data/CHANGELOG
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## 20260816
|
|
4
4
|
|
|
5
|
+
0.12.0: Refuse a switch whose accessor is a method the instance already has, rather than losing it.
|
|
6
|
+
|
|
7
|
+
1. + lib/Switches.rb: Switches#check_switch_names, called first in #do_set and #do_action, which raises where respond_to?(accessor) is true, #method_missing firing only where no method exists.
|
|
8
|
+
2. + lib/Switches.rb: SwitchNameCollision < ArgumentError.
|
|
9
|
+
3. ~ README.md: + a Notes entry for the refusal, and for the booleans and OptionParser names it does not cover.
|
|
10
|
+
4. + spec/all.rb: the refusal through #set and #perform, the message, that nothing reaches the parser first, and the two legal cases.
|
|
11
|
+
|
|
5
12
|
0.11.0: Forward to OptionParser, which #method_missing has never actually done.
|
|
6
13
|
|
|
7
14
|
1. ~ lib/Switches.rb: #method_missing tested (@op.methods - Switches.instance_methods).include?(method_name.to_s), which compares a String against an array of Symbols and so was never true. Every call fell through to the settings OpenStruct, which accepts anything, so s.banner = '...' — documented in the README's "With a banner" example — silently became a setting named banner while the parser kept its default of "Usage: <program_name> [options]". The comparison is now made in symbols, and the forwarding works: #banner=, #separator, #program_name= and the other 58 OptionParser methods reach the parser.
|
data/README.md
CHANGED
|
@@ -192,6 +192,7 @@ switches.class # => Hash
|
|
|
192
192
|
## Notes
|
|
193
193
|
|
|
194
194
|
- The switch `-?` can't be used, since there is no Ruby method `#?` for it to map to.
|
|
195
|
+
- A switch whose accessor would be a method the instance already has is refused at declaration, with a `SwitchNameCollision`. `s.set(:tap)` raises, `#tap` being `Kernel#tap` and so never reaching the settings; so do `Switches`' own methods, `s.set(:settings)` and `s.set(:set)` among them. The same name is fine as a boolean, since `s.boolean(:tap)` reads back through `#tap?`, and fine where the collision is with `OptionParser` rather than the instance, a declared switch winning over the parser.
|
|
195
196
|
- A call which is neither a switch nor a `Switches` method is forwarded to the underlying `OptionParser`, which is what makes `s.banner = '...'` and `s.separator '...'` work. A declared switch wins over an `OptionParser` method of the same name, so `s.set(:version)` still reads back through `#version`; without that rule a switch called `--version`, `--help`, `--load` or `--order` would be shadowed by the parser, all four being names `OptionParser` has. The names Ruby may call implicitly — `#to_a`, `#to_s` and the rest of `Switches::NEVER_FORWARDED` — are never forwarded, since `OptionParser#to_a` answers with the help and would otherwise be what `Array(switches)` and `[*switches]` gave you.
|
|
196
197
|
- There is a clear demarcation between whether a switch's argument is required and whether the switch itself is required.
|
|
197
198
|
- The bang interface methods (for example `set!` and `required!`) modify the switch in place with a required argument, rather than relying on a default or a value set elsewhere and later.
|
data/lib/Switches/VERSION.rb
CHANGED
data/lib/Switches.rb
CHANGED
|
@@ -150,6 +150,7 @@ module CastingInterfaceMethods
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
class RequiredSwitchMissing < RuntimeError; end
|
|
153
|
+
class SwitchNameCollision < ArgumentError; end
|
|
153
154
|
|
|
154
155
|
class Switches
|
|
155
156
|
|
|
@@ -257,6 +258,7 @@ class Switches
|
|
|
257
258
|
private
|
|
258
259
|
|
|
259
260
|
def do_set(requires_argument, *attrs, &block)
|
|
261
|
+
check_switch_names(*attrs)
|
|
260
262
|
set_if_required_switch(*attrs)
|
|
261
263
|
options = attrs.extract_options!
|
|
262
264
|
@all_switches = @all_switches + attrs.collect{|a| a.to_s}
|
|
@@ -268,6 +270,7 @@ class Switches
|
|
|
268
270
|
end
|
|
269
271
|
|
|
270
272
|
def do_action(requires_argument, *attrs, &block)
|
|
273
|
+
check_switch_names(*attrs)
|
|
271
274
|
attrs.each do |attr|
|
|
272
275
|
@settings.send(attr.to_s + '=', nil) # Needs to be set prior to checking for required switches, since that check relies upon the key having been set in @settings.
|
|
273
276
|
end
|
|
@@ -299,6 +302,14 @@ class Switches
|
|
|
299
302
|
@all_switches.include?(method_name.to_s.delete_suffix('='))
|
|
300
303
|
end
|
|
301
304
|
|
|
305
|
+
def check_switch_names(*attrs)
|
|
306
|
+
attrs.each do |attr|
|
|
307
|
+
next unless attr.is_a?(Symbol) || attr.is_a?(String)
|
|
308
|
+
next unless respond_to?(attr.to_s)
|
|
309
|
+
raise SwitchNameCollision, "switch, #{switch_string(attr)}, is already #{method(attr.to_s).owner}##{attr}, so it would never reach the settings"
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
302
313
|
def switch_string(attr)
|
|
303
314
|
attr = attr.to_s
|
|
304
315
|
"-#{attr.long_switch? ? '-' : ''}#{attr.delete('?')}"
|
data/spec/all.rb
CHANGED
|
@@ -164,6 +164,51 @@ describe Switches do
|
|
|
164
164
|
end
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
+
describe "switch names which collide with a method" do
|
|
168
|
+
def build(&block)
|
|
169
|
+
ARGV.clear
|
|
170
|
+
Switches.new(&block)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "refuses a switch named after a core method" do
|
|
174
|
+
expect{build{|s| s.set(:tap)}}.to raise_error(SwitchNameCollision, /Kernel#tap/)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "refuses a switch named after a Switches method" do
|
|
178
|
+
expect{build{|s| s.set(:settings)}}.to raise_error(SwitchNameCollision, /Switches#settings/)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "refuses a colliding name declared through #perform" do
|
|
182
|
+
expect{build{|s| s.perform(:display){}}}.to raise_error(SwitchNameCollision)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "names the switch in the form it would be supplied in" do
|
|
186
|
+
expect{build{|s| s.set(:instance_variable_get)}}.to raise_error(SwitchNameCollision, /--instance_variable_get/)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "raises before the switch is declared to the parser" do
|
|
190
|
+
switches = Switches.new
|
|
191
|
+
begin; switches.set(:tap); rescue SwitchNameCollision; end
|
|
192
|
+
expect(switches.to_h).to eq({})
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "accepts the name as a boolean, the accessor then carrying a question mark" do
|
|
196
|
+
ARGV.clear
|
|
197
|
+
ARGV << '--tap'
|
|
198
|
+
expect(Switches.new{|s| s.boolean(:tap)}.tap?).to eq(true)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "accepts a name OptionParser has, a declared switch winning over the parser" do
|
|
202
|
+
ARGV.clear
|
|
203
|
+
%w{--banner text}.each{|a| ARGV << a}
|
|
204
|
+
expect(Switches.new{|s| s.set(:banner)}.banner).to eq('text')
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "raises a SwitchNameCollision, which is an ArgumentError" do
|
|
208
|
+
expect(SwitchNameCollision.ancestors).to include(ArgumentError)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
167
212
|
describe "OptionParser methods" do
|
|
168
213
|
def build(&block)
|
|
169
214
|
ARGV.clear
|