switches.rb 0.9.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +211 -0
- data/lib/Switches.rb +471 -0
- data/spec/all.rb +222 -0
- data/switches.rb.gemspec +23 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f576247bc19561658a5bef4cda1a46f90e62b569fa15a6800507e5aeb05e7601
|
4
|
+
data.tar.gz: c5c805d86f2c5fa6d3cb757118d6024c9dea9261b6ec0172e4c8c0c73018d7c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e355d5bbb5bf7ee88fd4dabd45761d7eeb4ea2d20bc443228cf9881929411d605ab92c594d0d66c4d081bd16f1d4c896caf597f938f1a1d92cf4877531b6658b
|
7
|
+
data.tar.gz: 1f8f0162b5eb238df5e8e6143fe69348754fcfab16d31cb9480e378e31459fc974472916dab1cf00910c1adfa3a1422eb776ce9f93adf6c637a94d0faf5ade16
|
data/README.md
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# Switches
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
Switches provides for a nice wrapper to OptionParser to also act as a store for switches supplied.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'switches'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install switches
|
20
|
+
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
```Ruby
|
25
|
+
|
26
|
+
# With optional switch
|
27
|
+
|
28
|
+
switches = Switches.new do |s|
|
29
|
+
s.set(:a)
|
30
|
+
# OR
|
31
|
+
s.optional(:a)
|
32
|
+
end
|
33
|
+
switches.a
|
34
|
+
|
35
|
+
# With argument required
|
36
|
+
|
37
|
+
switches = Switches.new do |s|
|
38
|
+
s.set!(:a)
|
39
|
+
# OR
|
40
|
+
s.optional!(:a)
|
41
|
+
end
|
42
|
+
switches.a
|
43
|
+
|
44
|
+
# With switch required
|
45
|
+
|
46
|
+
switches = Switches.new do |s|
|
47
|
+
s.set(:a, required: true)
|
48
|
+
# OR
|
49
|
+
s.required(:a)
|
50
|
+
end
|
51
|
+
switches.a
|
52
|
+
|
53
|
+
# With switch and argument required
|
54
|
+
|
55
|
+
switches = Switches.new do |s|
|
56
|
+
s.set!(:a, required: true)
|
57
|
+
# OR
|
58
|
+
s.required!(:a)
|
59
|
+
end
|
60
|
+
switches.a
|
61
|
+
|
62
|
+
# With boolean switch
|
63
|
+
|
64
|
+
switches = Switches.new do |s|
|
65
|
+
s.set(:a?)
|
66
|
+
# OR
|
67
|
+
s.boolean(:a)
|
68
|
+
end
|
69
|
+
switches.a?
|
70
|
+
|
71
|
+
# With a default value
|
72
|
+
|
73
|
+
switches = Switches.new do |s|
|
74
|
+
s.set(:a, default: 31)
|
75
|
+
end
|
76
|
+
switches.a # => 31
|
77
|
+
|
78
|
+
# With switch cast to an integer
|
79
|
+
|
80
|
+
switches = Switches.new(include_casting_interface_methods: true) do |s|
|
81
|
+
s.set(:a, cast: Integer)
|
82
|
+
# OR
|
83
|
+
s.set(:a, type: Integer)
|
84
|
+
# OR
|
85
|
+
s.set(:a, class: Integer)
|
86
|
+
# OR
|
87
|
+
s.integer(:a)
|
88
|
+
end
|
89
|
+
switches.a.class # => Integer
|
90
|
+
|
91
|
+
# With switch cast to a float
|
92
|
+
|
93
|
+
switches = Switches.new(include_casting_interface_methods: true) do |s|
|
94
|
+
s.set(:a, cast: Float)
|
95
|
+
# OR
|
96
|
+
s.set(:a, type: Float)
|
97
|
+
# OR
|
98
|
+
s.set(:a, class: Float)
|
99
|
+
# OR
|
100
|
+
s.float(:a)
|
101
|
+
end
|
102
|
+
switches.a.class # => Float
|
103
|
+
|
104
|
+
# With switch cast to an array
|
105
|
+
|
106
|
+
switches = Switches.new(include_casting_interface_methods: true) do |s|
|
107
|
+
s.set(:a, cast: Array)
|
108
|
+
# OR
|
109
|
+
s.set(:a, type: Array)
|
110
|
+
# OR
|
111
|
+
s.set(:a, class: Array)
|
112
|
+
# OR
|
113
|
+
s.float(:a)
|
114
|
+
end
|
115
|
+
switches.a.class # => Array
|
116
|
+
|
117
|
+
# With switch cast to a regex
|
118
|
+
|
119
|
+
switches = Switches.new(include_casting_interface_methods: true) do |s|
|
120
|
+
s.set(:a, cast: Regexp)
|
121
|
+
# OR
|
122
|
+
s.set(:a, type: Regexp)
|
123
|
+
# OR
|
124
|
+
s.set(:a, class: Regexp)
|
125
|
+
# OR
|
126
|
+
s.regexp(:a)
|
127
|
+
end
|
128
|
+
switches.a.class # => Regexp
|
129
|
+
|
130
|
+
# With a default value and cast to an integer
|
131
|
+
|
132
|
+
switches = Switches.new(include_casting_interface_methods: true) do |s|
|
133
|
+
s.set(:a, default: 31, cast: Integer)
|
134
|
+
# OR
|
135
|
+
s.set(:a, default: 31, type: Integer)
|
136
|
+
# OR
|
137
|
+
s.set(:a, default: 31, class: Integer)
|
138
|
+
# OR
|
139
|
+
s.integer(:a, default: 31)
|
140
|
+
end
|
141
|
+
switches.a # => 31 if no argument supplied
|
142
|
+
switches.a.class # => Integer
|
143
|
+
|
144
|
+
# With a description of the switch
|
145
|
+
|
146
|
+
switches = Switches.new do |s|
|
147
|
+
s.set(:a){'This is the -a switch.'}
|
148
|
+
end
|
149
|
+
|
150
|
+
# With a banner
|
151
|
+
|
152
|
+
switches = Switches.new do |s|
|
153
|
+
s.banner = 'Here is a banner for the switches.'
|
154
|
+
end
|
155
|
+
|
156
|
+
# Return a hash instead of an object of class Switches
|
157
|
+
|
158
|
+
switches = Switches.new(to_h: true) do |s|
|
159
|
+
s.set(:a)
|
160
|
+
end
|
161
|
+
# OR
|
162
|
+
switches = Switches.as_h do |s|
|
163
|
+
s.set(:a)
|
164
|
+
end
|
165
|
+
|
166
|
+
switches[:a]
|
167
|
+
switches.class # => Hash
|
168
|
+
|
169
|
+
# Without a block
|
170
|
+
|
171
|
+
switches = Switches.new
|
172
|
+
switches.set(:a)
|
173
|
+
switches.parse!
|
174
|
+
switches.a
|
175
|
+
|
176
|
+
# With an action
|
177
|
+
|
178
|
+
switches = Switches.new do |s|
|
179
|
+
switches.perform(:a){puts 'a'}
|
180
|
+
switches.parse! # => a
|
181
|
+
|
182
|
+
# With an action uses the argument
|
183
|
+
|
184
|
+
switches = Switches.new do |s|
|
185
|
+
switches.perform(:a){|block_arg| puts block_arg.upcase}
|
186
|
+
switches.parse! # => A
|
187
|
+
|
188
|
+
# With multiple features combined
|
189
|
+
|
190
|
+
switches = Switches.new(include_casting_interface_methods: true, to_h: true) do |s|
|
191
|
+
s.set(:a, default: 31, cast: Integer){'This is the switch -a. It has a default of 31.'}
|
192
|
+
# OR
|
193
|
+
s.set(:a, default: 31, type: Integer){'This is the switch -a. It has a default of 31.'}
|
194
|
+
# OR
|
195
|
+
s.set(:a, default: 31, class: Integer){'This is the switch -a. It has a default of 31.'}
|
196
|
+
# OR
|
197
|
+
s.integer(:a, default: 31){'This is the switch -a. It has a default of 31.'}
|
198
|
+
end
|
199
|
+
switches[:a] # => 31 if no argument supplied
|
200
|
+
switches.a.class # => Integer
|
201
|
+
switches.class # => Hash
|
202
|
+
|
203
|
+
```
|
204
|
+
|
205
|
+
## Contributing
|
206
|
+
|
207
|
+
1. Fork it ( https://github.com/thoran/HTTP/fork )
|
208
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
209
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
210
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
211
|
+
5. Create a new pull request
|
data/lib/Switches.rb
ADDED
@@ -0,0 +1,471 @@
|
|
1
|
+
# Switches.rb
|
2
|
+
# Switches
|
3
|
+
|
4
|
+
# 20180403
|
5
|
+
# 0.9.15
|
6
|
+
|
7
|
+
# Description: Switches provides for a nice wrapper to OptionParser to also act as a store for switches supplied.
|
8
|
+
|
9
|
+
# Todo:
|
10
|
+
# 1. Clean up #set. Done as of 0.4.0.
|
11
|
+
# 2. Reinstitute some specs. Done as of 0.9.8.
|
12
|
+
|
13
|
+
# Ideas:
|
14
|
+
# 1. Use ! for options with required switches? Done as of 0.6.0. (Changed to being for required arguments in 0.9.0 however.)
|
15
|
+
# 2. Do away with optional arguments entirely. Since when does anyone want to specify a non-boolean switch and then supply no arguments anyway?... OK, maybe sometimes, but this is pretty obscure IMO. OK, bad idea. These can be used as action oriented sub-commands.
|
16
|
+
# 3. Allow for any one of the switches OpenStruct methods to assign values for any of the other associated methods, so as it is more than a read once switch and can be used for storage through out the application; although this might be stepping on Attributes.rb's toes?...
|
17
|
+
|
18
|
+
# Notes:
|
19
|
+
# 1. A limitation is the inability to use the switch, "-?", since there is no Ruby method, #?.
|
20
|
+
# 2. An additional limitation is that the Switches class cannot have methods which are the same as any OptionParser methods since these will be forwarded to the OptionParser instance, @op.
|
21
|
+
# 3. In 0.9.0 there is finally now a clear demarcation between switch arguments and switches themselves being mandatory or optional.
|
22
|
+
# 4. It mostly makes sense that the Switches interface methods are bang methods, since it is modifying that switch in place with a required argument, rather than relying upon a default or otherwise set value elsewhere and presumably later than when the switch was set.
|
23
|
+
|
24
|
+
# Dependencies:
|
25
|
+
# 1. Standard Ruby Library.
|
26
|
+
|
27
|
+
# Changes since 0.8:
|
28
|
+
# (Renamed this project/class from Options to Switches since there are required switches now and required options don't make sense.)
|
29
|
+
# 1. /Options/Switches/.
|
30
|
+
# 2. /RequiredOptionMissing/RequiredSwitchMissing/.
|
31
|
+
# 3. /String#boolean_arg?/String#boolean_switch?/.
|
32
|
+
# 4. Other references to options changed to switches.
|
33
|
+
# 5. + Switches#required/mandatory/necessary.
|
34
|
+
# 6. + Swtiches#optional.
|
35
|
+
# 7. + Switches#supplied_switches.
|
36
|
+
# 8. + Switches#set_unused_switches_to_nil.
|
37
|
+
# 9. ~ self-run section to reflect new interface.
|
38
|
+
# 0/1 (Removed ! from switch setting methods and moved those to the Switches interface methods.)
|
39
|
+
# 10. + Switches#do_set.
|
40
|
+
# 11. ~ Switches#set.
|
41
|
+
# 12. + Switches#set!.
|
42
|
+
# 13. + Switches#required!.
|
43
|
+
# 14. - String#required_arg?.
|
44
|
+
# 15. ~ Switches#on_args + requires_argument.
|
45
|
+
# 1/2 (Added casting to nominated Ruby classes.)
|
46
|
+
# 16. ~ Switches#on_args + options.
|
47
|
+
# 17. + Array#extract_options!.
|
48
|
+
# 18. + Switches#allowed.
|
49
|
+
# 19. + Switches#allowed!.
|
50
|
+
# 20. + Swithces#mandatory.
|
51
|
+
# 21. + Switches#necessary.
|
52
|
+
# 22. ~ Switches#do_set + ...options.
|
53
|
+
# 23. ~ Switches#on_args + ...options[:cast].
|
54
|
+
# 24. ~ self-run section to do a simple test of casting.
|
55
|
+
# 2/3 (Renaming the library file.)
|
56
|
+
# 25. /Options.rb/Switches.rb/.
|
57
|
+
# 3/4 (+ casting interface methods)
|
58
|
+
# 26. + CastingInterfaceMethods#integer(!).
|
59
|
+
# 27. + CastingInterfaceMethods#float(!).
|
60
|
+
# 28. + CastingInterfaceMethods#array(!).
|
61
|
+
# 29. + CastingInterfaceMethods#regex(p)(!).
|
62
|
+
# 30. + CastingInterfaceMethods#boolean(!).
|
63
|
+
# 31. ~ Switches#initialize + include_casting_interface_methods.
|
64
|
+
# 32. /String#short_arg?/String#short_switch?/.
|
65
|
+
# 33. /String#long_arg?/String#long_switch?/.
|
66
|
+
# 34. ~ Switches#on_args - boolean_switch.
|
67
|
+
# 4/5 (Added default values for switches.)
|
68
|
+
# 35. ~ CastingInterfaceMethods to extract any options before pushing a cast option, since it was overwriting for those casting interface methods.
|
69
|
+
# 36. ~ Switches#initialize, + @defaults.
|
70
|
+
# 37. /Switches#set_unused_switches_to_nil/Switches#set_unused_switches/.
|
71
|
+
# 38. ~ Switches#set_unused_switches, so as it handles setting unused switches with a default.
|
72
|
+
# 5/6 (Setting of defaults was overriding supplied switch values.)
|
73
|
+
# 39. ~ Switches#set_unused_switches, to make use of #switch_defaults and #unused_switches.
|
74
|
+
# 40. + Switches#switch_defaults, as an interface method since it might be useful for querying at some point?
|
75
|
+
# 41. + Switches#unused_switches, also as an interface method since it might also be useful for querying at some point?
|
76
|
+
# 42. ~ Switches#on_args, so as to enable alternate hash keys (:type and :class) for type casting.
|
77
|
+
# 43. ~ self-run section to reflect the optionalness/optionality(?) of summaries.
|
78
|
+
# 6/7 (Some small tidyups and removal of self-run section.)
|
79
|
+
# 44. ~ Switches#on_args, shorter when handling casting arguments.
|
80
|
+
# 45. /unused_switches/unset_switches/.
|
81
|
+
# 46. ~ Switches#check_required_switches, so that the message is not being assigned until a switch is missing.
|
82
|
+
# 47. Removed the self-run section and moved it to ./test/run.rb.
|
83
|
+
# 7/8 (Addition of #perform and #perform! methods, as well the #do_action method and changes to #on_args to facilitate #perform* methods.)
|
84
|
+
# 48. ~ Switches#on_args, so as if a block is supplied which is not returning a String, then it will not be placed into the on_args argument list.
|
85
|
+
# 49. + Switches#perform, interface for actions which don't require an argument.
|
86
|
+
# 50. + Switches#perform!, interface for actions which do require an argument.
|
87
|
+
# 51. + Switches#do_action, executes a block when called by either of #perform or #perform!.
|
88
|
+
# 8/9
|
89
|
+
# 52. ~ Switches#check_required_switches, so as it now assembles the complete list of missing switches rather than failing on and reporting only the first.
|
90
|
+
# 53. + Switches#required_perform
|
91
|
+
# 54. + Switches#required_perform!
|
92
|
+
# 55. - OptionParse#soft_parse, since this was never used here, but only in Attributes.
|
93
|
+
# 56. + Object#default_is(et.al.)
|
94
|
+
# 57. + NilClass#default_is(et.al.) overrides Object#default_is for the specific case of nil.
|
95
|
+
# 58. /switches_defaults/switches_with_defaults/.
|
96
|
+
# 59. + Array#peek_options.
|
97
|
+
# 60. + Array#poke_options!.
|
98
|
+
# 61. + Array#all_but_last.
|
99
|
+
# 62. + Array#last!.
|
100
|
+
# 63. + Module#alias_methods.
|
101
|
+
# 64. ~ CastingInterfaceMethods, to make use of Array#poke_options.
|
102
|
+
# 65. + CastingInterfaceMethods#flag.
|
103
|
+
# 66. + Switches.as_h (and associated aliased interfaces).
|
104
|
+
# 67. ~ Switches#initialize, arguments are now a splat with the intention of allowing both casting_interface_methods and as_h being specified as a hash supplied to the initialiser.
|
105
|
+
# 68. ~ Switches, to make use of Module#alias_methods.
|
106
|
+
# 69. ~ Switches, to make use of Array#peek_options and Array#poke_options!.
|
107
|
+
# 70. + Switches#required_perform.
|
108
|
+
# 71. + Switches#required_perform!.
|
109
|
+
# 72. + Switches#switches_with_defaults.
|
110
|
+
# 73. + Switches#switches_with_castings.
|
111
|
+
# 74. + Swtiches#to_h.
|
112
|
+
# 75. + Switches#set_if_required_switch.
|
113
|
+
# 76. + Switches#set_switches_with_defaults.
|
114
|
+
# 77. ~ Switches#parse!, + set_switches_with_defaults() and + to_h().
|
115
|
+
# 78. ~ Switches#do_set, + set_if_required_switch().
|
116
|
+
# 79. ~ Switches#do_action, + set_if_required_switch().
|
117
|
+
# 80. ~ Switches#on_args, so as it can handle castings.
|
118
|
+
# 9/10
|
119
|
+
# 81. + OpenStruct#to_h.
|
120
|
+
# 82. ~ Switches#to_h.
|
121
|
+
# 10/11 (Whoops! The defaults were overwriting the supplied settings.)
|
122
|
+
# 83. ~ Switches#set_switches_with_defaults to check if a setting had been set before applying a default value.
|
123
|
+
# 10/11
|
124
|
+
# 84.
|
125
|
+
# 11/12 (Removed default aliases for default_to, since it clashes with the mail gem's Mail::Message#default method.)
|
126
|
+
# 85. - Object#default.
|
127
|
+
# 86. - NilClass#default.
|
128
|
+
# 12/13
|
129
|
+
# 87. Swapped out a couple of @settings.instance_variable_get(:@table)'s for OpenStruct#to_h's.
|
130
|
+
# 13/14
|
131
|
+
# 88. README is now markdown.
|
132
|
+
# 89. - examples directory
|
133
|
+
# 90. Version number bump to 0.9.14.
|
134
|
+
# 91. Turned into a gem (+ switches.gemspec).
|
135
|
+
# 14/15
|
136
|
+
# 92. switches gem name taken, so renamed the gem to switches.rb.
|
137
|
+
# 93. Version number bump to 0.9.15.
|
138
|
+
# 94. Tidied some comments in spec/all.rb.
|
139
|
+
|
140
|
+
require 'optparse'
|
141
|
+
require 'ostruct'
|
142
|
+
|
143
|
+
class OpenStruct
|
144
|
+
|
145
|
+
def to_h
|
146
|
+
@table
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
class String
|
152
|
+
|
153
|
+
def short_switch?
|
154
|
+
self =~ /^.$/ || self =~ /^.\?$/ || self =~ /^.\!$/
|
155
|
+
end
|
156
|
+
|
157
|
+
def long_switch?
|
158
|
+
(self =~ /^..+$/ && !short_switch?) || self =~ /^..+\?$/ || self =~ /^..+!$/
|
159
|
+
end
|
160
|
+
|
161
|
+
def boolean_switch?
|
162
|
+
self =~ /\?$/
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
class Array
|
168
|
+
|
169
|
+
def extract_options!
|
170
|
+
last.is_a?(::Hash) ? pop : {}
|
171
|
+
end
|
172
|
+
|
173
|
+
def peek_options
|
174
|
+
last.is_a?(::Hash) ? last : {}
|
175
|
+
end
|
176
|
+
|
177
|
+
def poke_options!(h)
|
178
|
+
self << self.extract_options!.merge(h)
|
179
|
+
end
|
180
|
+
|
181
|
+
alias_method :last!, :pop
|
182
|
+
|
183
|
+
def all_but_last
|
184
|
+
d = self.dup
|
185
|
+
d.last!
|
186
|
+
d
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
class Object
|
192
|
+
|
193
|
+
def default_is(o)
|
194
|
+
self
|
195
|
+
end
|
196
|
+
alias_method :defaults_to, :default_is
|
197
|
+
alias_method :default_to, :default_is
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
class NilClass
|
202
|
+
|
203
|
+
def default_is(o)
|
204
|
+
o
|
205
|
+
end
|
206
|
+
alias_method :defaults_to, :default_is
|
207
|
+
alias_method :default_to, :default_is
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
class Module
|
212
|
+
|
213
|
+
def alias_methods(*args)
|
214
|
+
args.all_but_last.each{|e| alias_method e.to_sym, args.last.to_sym}
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
module CastingInterfaceMethods
|
220
|
+
|
221
|
+
def integer(*attrs, &block)
|
222
|
+
attrs.poke_options!({:cast => Integer})
|
223
|
+
set(*attrs, &block)
|
224
|
+
end
|
225
|
+
|
226
|
+
def integer!(*attrs, &block)
|
227
|
+
attrs.poke_options!({:cast => Integer})
|
228
|
+
set!(*attrs, &block)
|
229
|
+
end
|
230
|
+
|
231
|
+
def float(*attrs, &block)
|
232
|
+
attrs.poke_options!({:cast => Float})
|
233
|
+
set(*attrs, &block)
|
234
|
+
end
|
235
|
+
|
236
|
+
def float!(*attrs, &block)
|
237
|
+
attrs.poke_options!({:cast => Float})
|
238
|
+
set!(*attrs, &block)
|
239
|
+
end
|
240
|
+
|
241
|
+
def array(*attrs, &block)
|
242
|
+
attrs.poke_options!({:cast => Array})
|
243
|
+
set(*attrs, &block)
|
244
|
+
end
|
245
|
+
|
246
|
+
def array!(*attrs, &block)
|
247
|
+
attrs.poke_options!({:cast => Array})
|
248
|
+
set!(*attrs, &block)
|
249
|
+
end
|
250
|
+
|
251
|
+
def regexp(*attrs, &block)
|
252
|
+
attrs.poke_options!({:cast => Regexp})
|
253
|
+
set(*attrs, &block)
|
254
|
+
end
|
255
|
+
alias_method :regex, :regexp
|
256
|
+
|
257
|
+
def regexp!(*attrs, &block)
|
258
|
+
attrs.poke_options!({:cast => Regexp})
|
259
|
+
set!(*attrs, &block)
|
260
|
+
end
|
261
|
+
alias_method :regex!, :regexp!
|
262
|
+
|
263
|
+
def boolean(*attrs, &block)
|
264
|
+
attrs.collect!{|a| a.to_s =~ /\?$/ ? a : (a.to_s + '?').to_sym}
|
265
|
+
set(*attrs, &block)
|
266
|
+
end
|
267
|
+
alias_method :flag, :boolean
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
class RequiredSwitchMissing < RuntimeError; end
|
272
|
+
|
273
|
+
class Switches
|
274
|
+
|
275
|
+
class << self
|
276
|
+
|
277
|
+
def as_h(*args)
|
278
|
+
switches = new(*args)
|
279
|
+
switches.to_h
|
280
|
+
end
|
281
|
+
alias_methods :to_h, :as_hash, :as_a_hash, :as_h
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
attr_accessor :settings
|
286
|
+
|
287
|
+
def initialize(*args)
|
288
|
+
options = args.extract_options!
|
289
|
+
self.class.send(:include, CastingInterfaceMethods) if options[:include_casting_interface_methods].default_is(true)
|
290
|
+
@as_h = (options[:as_h] || options[:to_h] || options[:as_hash] || options[:as_a_hash]).default_is(false)
|
291
|
+
@settings = OpenStruct.new
|
292
|
+
@op = OptionParser.new
|
293
|
+
@required_switches = []
|
294
|
+
@all_switches = []
|
295
|
+
@defaults = {}
|
296
|
+
@castings = {}
|
297
|
+
if block_given?
|
298
|
+
yield self
|
299
|
+
parse!
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def set(*attrs, &block)
|
304
|
+
do_set(false, *attrs, &block)
|
305
|
+
end
|
306
|
+
alias_methods :optional_switch, :optional, :set
|
307
|
+
|
308
|
+
def set!(*attrs, &block)
|
309
|
+
do_set(true, *attrs, &block)
|
310
|
+
end
|
311
|
+
alias_methods :optional_switch!, :optional!, :set!
|
312
|
+
|
313
|
+
def required(*attrs, &block)
|
314
|
+
attrs.poke_options!({:required => true})
|
315
|
+
set(*attrs, &block)
|
316
|
+
end
|
317
|
+
alias_method :required_switch, :required
|
318
|
+
|
319
|
+
def required!(*attrs, &block)
|
320
|
+
attrs.poke_options!({:required => true})
|
321
|
+
set!(*attrs, &block)
|
322
|
+
end
|
323
|
+
alias_method :required_switch!, :required!
|
324
|
+
|
325
|
+
def perform(*attrs, &block)
|
326
|
+
do_action(false, *attrs, &block)
|
327
|
+
end
|
328
|
+
alias_methods :optionally_perform, :optional_perform, :perform
|
329
|
+
|
330
|
+
def perform!(*attrs, &block)
|
331
|
+
do_action(true, *attrs, &block)
|
332
|
+
end
|
333
|
+
alias_methods :optionally_perform!, :optional_perform!, :perform!
|
334
|
+
|
335
|
+
def required_perform(*attrs, &block)
|
336
|
+
attrs.poke_options!({:required => true})
|
337
|
+
perform(*attrs, &block)
|
338
|
+
end
|
339
|
+
|
340
|
+
def required_perform!(*attrs, &block)
|
341
|
+
attrs.poke_options!({:required => true})
|
342
|
+
perform!(*attrs, &block)
|
343
|
+
end
|
344
|
+
|
345
|
+
def parse!
|
346
|
+
@op.parse!
|
347
|
+
check_required_switches
|
348
|
+
set_switches_with_defaults
|
349
|
+
set_unset_switches
|
350
|
+
to_h if @as_h
|
351
|
+
end
|
352
|
+
|
353
|
+
def supplied_switches
|
354
|
+
@settings.to_h.keys.collect{|s| s.to_s}
|
355
|
+
end
|
356
|
+
|
357
|
+
def switches_with_defaults
|
358
|
+
@defaults.keys.collect{|default| default.to_s}
|
359
|
+
end
|
360
|
+
|
361
|
+
def switches_with_castings
|
362
|
+
@castings.keys.collect{|default| default.to_s}
|
363
|
+
end
|
364
|
+
|
365
|
+
def unset_switches
|
366
|
+
@all_switches - supplied_switches - switches_with_defaults
|
367
|
+
end
|
368
|
+
|
369
|
+
def to_h
|
370
|
+
@settings.to_h
|
371
|
+
end
|
372
|
+
|
373
|
+
private
|
374
|
+
|
375
|
+
def do_set(requires_argument, *attrs, &block)
|
376
|
+
set_if_required_switch(*attrs)
|
377
|
+
options = attrs.extract_options!
|
378
|
+
@all_switches = @all_switches + attrs.collect{|a| a.to_s}
|
379
|
+
@op.on(*on_args(requires_argument, options, *attrs, &block)) do |o|
|
380
|
+
attrs.each do |attr|
|
381
|
+
@settings.send(attr.to_s + '=', o)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def do_action(requires_argument, *attrs, &block)
|
387
|
+
attrs.each do |attr|
|
388
|
+
@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.
|
389
|
+
end
|
390
|
+
set_if_required_switch(*attrs)
|
391
|
+
options = attrs.extract_options!
|
392
|
+
@all_switches = @all_switches + attrs.collect{|a| a.to_s}
|
393
|
+
@op.on(*on_args(requires_argument, options, *attrs)) do |o|
|
394
|
+
yield o if block
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def method_missing(method_name, *args, &block)
|
399
|
+
if (@op.methods - Switches.instance_methods).include?(method_name.to_s)
|
400
|
+
@op.send(method_name.to_s, *args, &block)
|
401
|
+
else
|
402
|
+
@settings.send(method_name.to_s, *args, &block)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def on_args(requires_argument, options, *attrs, &block)
|
407
|
+
on_args = []
|
408
|
+
attrs.collect{|e| e.to_s}.each do |attr|
|
409
|
+
@defaults[attr] = options[:default] if options[:default]
|
410
|
+
@castings[attr] = (options[:cast] || options[:type] || options[:class]) if (options[:cast] || options[:type] || options[:class])
|
411
|
+
on_args << "-#{attr.long_switch? ? '-' : ''}#{attr.to_s.delete('?')}"
|
412
|
+
end
|
413
|
+
if requires_argument
|
414
|
+
on_args << (on_args.pop + ' REQUIRED_ARGUMENT')
|
415
|
+
elsif !!attrs.last.to_s.boolean_switch?
|
416
|
+
on_args << (on_args.pop)
|
417
|
+
else
|
418
|
+
on_args << (on_args.pop + ' [OPTIONAL_ARGUMENT]')
|
419
|
+
end
|
420
|
+
on_args << (options[:cast] || options[:type] || options[:class]) if (options[:cast] || options[:type] || options[:class])
|
421
|
+
if block
|
422
|
+
yield_result = yield
|
423
|
+
on_args << yield_result if yield_result.class == String
|
424
|
+
end
|
425
|
+
on_args
|
426
|
+
end
|
427
|
+
|
428
|
+
def check_required_switches
|
429
|
+
messages = []
|
430
|
+
@required_switches.each do |required_switch|
|
431
|
+
unless supplied_switches.include?(required_switch)
|
432
|
+
messages << "required switch, -#{required_switch.long_switch? ? '-' : ''}#{required_switch.to_s.delete('?')}, is missing"
|
433
|
+
end
|
434
|
+
end
|
435
|
+
unless messages.empty?
|
436
|
+
raise RequiredSwitchMissing, messages.join("\n")
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def set_switches_with_defaults
|
441
|
+
switches_with_defaults.each do |switch|
|
442
|
+
if @settings.to_h[switch.to_sym].nil?
|
443
|
+
if switches_with_castings.include?(switch)
|
444
|
+
cast_value = (
|
445
|
+
case @castings[switch]
|
446
|
+
when Integer; @defaults[switch].to_i
|
447
|
+
when Float; @defaults[switch].to_f
|
448
|
+
when Array; @defaults[switch].to_a
|
449
|
+
when Regexp; Regexp.new(@defaults[switch])
|
450
|
+
end
|
451
|
+
)
|
452
|
+
@settings.send(switch + '=', cast_value)
|
453
|
+
else
|
454
|
+
@settings.send(switch + '=', @defaults[switch])
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def set_unset_switches
|
461
|
+
unset_switches.each{|switch| @settings.send(switch + '=', nil)}
|
462
|
+
end
|
463
|
+
|
464
|
+
def set_if_required_switch(*attrs)
|
465
|
+
if (attrs.peek_options[:required] || attrs.peek_options[:required_switch])
|
466
|
+
attrs.extract_options!
|
467
|
+
@required_switches = @required_switches + attrs.collect{|a| a.to_s}
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
data/spec/all.rb
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../lib/Switches"
|
2
|
+
|
3
|
+
class Array
|
4
|
+
|
5
|
+
alias_method :empty!, :clear
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Switches do
|
10
|
+
|
11
|
+
describe ".new" do
|
12
|
+
|
13
|
+
it "should return an object of class Switches" do
|
14
|
+
Switches.new.class.should == Switches
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#set" do
|
20
|
+
|
21
|
+
def setup(switches_string = '')
|
22
|
+
ARGV.empty!
|
23
|
+
switches_string.split.each{|a| ARGV << a}
|
24
|
+
Switches.new do |s|
|
25
|
+
s.set :v?, :verbose?
|
26
|
+
s.set :p, :port
|
27
|
+
s.set :host, :h
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set a short boolean switch or flag to true when supplied with a short switch" do
|
32
|
+
setup('-v').v?.should == true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set a long boolean switch value to true when supplied with a short switch" do
|
36
|
+
setup('-v').verbose?.should == true
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should set a short boolean switch or flag to true when supplied with a long switch" do
|
40
|
+
setup('--verbose').v?.should == true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should set a long boolean switch or flag to true when supplied with a long switch" do
|
44
|
+
setup('--verbose').verbose?.should == true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should set a short switch value to the value supplied when supplied with a short switch" do
|
48
|
+
setup('-p 22').p.should == '22'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should set a long switch value to the value supplied when supplied with a short switch" do
|
52
|
+
setup('-p 22').port.should == '22'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should set a short switch value to the value supplied when supplied with a long switch" do
|
56
|
+
setup('--port 22').p.should == '22'
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should set a long switch value to the value supplied when supplied with a long switch" do
|
60
|
+
setup('--port 22').port.should == '22'
|
61
|
+
end
|
62
|
+
|
63
|
+
it "shouldn't matter in which order the short and long switches are given" do
|
64
|
+
setup('-h example.com').h.should == 'example.com'
|
65
|
+
setup('--host example.com').h.should == 'example.com'
|
66
|
+
end
|
67
|
+
|
68
|
+
end # describe "#set"
|
69
|
+
|
70
|
+
describe "#set!" do
|
71
|
+
|
72
|
+
def setup(switches_string = '')
|
73
|
+
ARGV.empty!
|
74
|
+
switches_string.split.each{|a| ARGV << a}
|
75
|
+
Switches.new do |s|
|
76
|
+
s.set! :p, :port
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should set a short switch value to the argument supplied with a short switch" do
|
81
|
+
setup('-p 22').p.should == '22'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should set a long switch value to the argument supplied with a short switch" do
|
85
|
+
setup('-p 22').port.should == '22'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should set a short switch value to the argument supplied with a long switch" do
|
89
|
+
setup('--port 22').p.should == '22'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should set a long switch value to the argument supplied with a long switch" do
|
93
|
+
setup('--port 22').port.should == '22'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should raise a MissingArgument error when no argument is supplied to a short switch" do
|
97
|
+
switches = lambda{setup('-p')}
|
98
|
+
switches.should{raise MissingArgument}
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should raise a MissingArgument error when no argument is supplied to a long switch" do
|
102
|
+
switches = lambda{setup('--port')}
|
103
|
+
switches.should{raise MissingArgument}
|
104
|
+
end
|
105
|
+
|
106
|
+
end # describe "#set!"
|
107
|
+
|
108
|
+
describe "#required" do
|
109
|
+
|
110
|
+
def setup(switches_string = '')
|
111
|
+
ARGV.empty!
|
112
|
+
switches_string.split.each{|a| ARGV << a}
|
113
|
+
Switches.new do |s|
|
114
|
+
s.required :p, :port
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should set a short switch value to the argument supplied with a short switch" do
|
119
|
+
setup('-p 22').p.should == '22'
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should set a long switch value to the argument supplied with a short switch" do
|
123
|
+
setup('-p 22').port.should == '22'
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should set a short switch value to the argument supplied with a long switch" do
|
127
|
+
setup('--port 22').p.should == '22'
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should set a long switch value to the argument supplied with a long switch" do
|
131
|
+
setup('--port 22').port.should == '22'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should raise a RequiredSwitchMissing error no switch is supplied" do
|
135
|
+
switches = lambda{setup}
|
136
|
+
switches.should{raise RequiredSwitchMissing}
|
137
|
+
end
|
138
|
+
|
139
|
+
end # describe "#required"
|
140
|
+
|
141
|
+
describe "#required!" do
|
142
|
+
|
143
|
+
def setup(switches_string = '')
|
144
|
+
ARGV.empty!
|
145
|
+
switches_string.split.each{|a| ARGV << a}
|
146
|
+
Switches.new do |s|
|
147
|
+
s.required! :p, :port
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should set a short switch value to the argument supplied with a short switch" do
|
152
|
+
setup('-p 22').p.should == '22'
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should set a long switch value to the argument supplied with a short switch" do
|
156
|
+
setup('-p 22').port.should == '22'
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should set a short switch value to the argument supplied with a long switch" do
|
160
|
+
setup('--port 22').p.should == '22'
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should set a long switch value to the argument supplied with a long switch" do
|
164
|
+
setup('--port 22').port.should == '22'
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should raise a MissingArgument error when no argument is supplied to a short switch" do
|
168
|
+
switches = lambda{setup('-p')}
|
169
|
+
switches.should{raise MissingArgument}
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should raise a MissingArgument error when no argument is supplied to a long switch" do
|
173
|
+
switches = lambda{setup('-p')}
|
174
|
+
switches.should{raise MissingArgument}
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should raise a RequiredSwitchMissing error no switch is supplied" do
|
178
|
+
switches = lambda{setup}
|
179
|
+
switches.should{raise RequiredSwitchMissing}
|
180
|
+
end
|
181
|
+
|
182
|
+
end # describe "#required!"
|
183
|
+
|
184
|
+
describe "#perform" do
|
185
|
+
|
186
|
+
def setup(switches_string = '')
|
187
|
+
ARGV.empty!
|
188
|
+
switches_string.split.each{|a| ARGV << a}
|
189
|
+
Switches.new do |s|
|
190
|
+
s.perform(:action){an_action}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def an_action
|
195
|
+
'value'
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should send(:an_action)"
|
199
|
+
|
200
|
+
end # describe "#perform"
|
201
|
+
|
202
|
+
describe "#perform!" do
|
203
|
+
|
204
|
+
def setup(switches_string = '')
|
205
|
+
ARGV.empty!
|
206
|
+
switches_string.split.each{|a| ARGV << a}
|
207
|
+
Switches.new do |s|
|
208
|
+
s.perform(:action){an_action}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def an_action
|
213
|
+
'value'
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should send(:an_action)"
|
217
|
+
|
218
|
+
it "should require a block parameter"
|
219
|
+
|
220
|
+
end # describe "#perform!"
|
221
|
+
|
222
|
+
end # describe Switches
|
data/switches.rb.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'switches.rb' # I would have preferred 'switches', but there's a gem with the name of switches.
|
3
|
+
|
4
|
+
s.version = '0.9.15'
|
5
|
+
s.date = '2018-04-03'
|
6
|
+
|
7
|
+
s.summary = "The easiest way to provide switches to a Ruby program."
|
8
|
+
s.description = "Switches provides for a nice wrapper to OptionParser to also act as a store for switches supplied."
|
9
|
+
s.author = 'thoran'
|
10
|
+
s.email = 'code@thoran.com'
|
11
|
+
s.homepage = "http://github.com/thoran/switches"
|
12
|
+
|
13
|
+
s.files = [
|
14
|
+
'README.md',
|
15
|
+
'switches.rb.gemspec',
|
16
|
+
Dir['lib/**/*.rb'],
|
17
|
+
Dir['spec/**/*.rb']
|
18
|
+
].flatten
|
19
|
+
|
20
|
+
s.require_paths = ['lib']
|
21
|
+
|
22
|
+
s.has_rdoc = false
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: switches.rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.15
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- thoran
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-03 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Switches provides for a nice wrapper to OptionParser to also act as a
|
14
|
+
store for switches supplied.
|
15
|
+
email: code@thoran.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- lib/Switches.rb
|
22
|
+
- spec/all.rb
|
23
|
+
- switches.rb.gemspec
|
24
|
+
homepage: http://github.com/thoran/switches
|
25
|
+
licenses: []
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.7.4
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: The easiest way to provide switches to a Ruby program.
|
47
|
+
test_files: []
|