easyoptions 2015.2.28

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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/easyoptions.rb +277 -0
  3. metadata +44 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1d08b70cbfa1961622109897ef00d7fef0defc23
4
+ data.tar.gz: 1300aefd50ddb122a1e081bc90938b2acbc30eb2
5
+ SHA512:
6
+ metadata.gz: 3d5e7f0926e2e6291c8a24f1f65ff143378e15c62c2f6ebbce0042446a7b9ea4dcb7b73b6f771feef58f5506d91e1d5683ad6806777bc4446dc35a7c83885eb6
7
+ data.tar.gz: 245258d0faa8c764056f850f6a380d54db2261c57d3ee0e0257b8cae62dd9f6ed4d752e781bfc5470295332ffac67515389e5c80e47bf363d3ed81dbe295b890
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ ##
5
+ ## EasyOptions 2015.2.28
6
+ ## Copyright (c) 2013, 2014 Renato Silva
7
+ ## BSD licensed
8
+ ##
9
+ ## This script is supposed to parse command line arguments in a way that,
10
+ ## even though its implementation is not trivial, it should be easy and
11
+ ## smooth to use. For using this script, simply document your target script
12
+ ## using double-hash comments, like this:
13
+ ##
14
+ ## ## Program Name v1.0
15
+ ## ## Copyright (C) Someone
16
+ ## ##
17
+ ## ## This program does something. Usage:
18
+ ## ## @#script.name [option]
19
+ ## ##
20
+ ## ## Options:
21
+ ## ## -h, --help All client scripts have this by default,
22
+ ## ## it shows this double-hash documentation.
23
+ ## ##
24
+ ## ## -o, --option This option will get stored as true value
25
+ ## ## under EasyOptions.options[:option]. Long
26
+ ## ## version is mandatory, and can be specified
27
+ ## ### before or after short version.
28
+ ## ##
29
+ ## ## --some-boolean This will get stored as true value under
30
+ ## ## EasyOptions.options[:some_boolean].
31
+ ## ##
32
+ ## ## --some-value=VALUE This is going to store the VALUE specified
33
+ ## ## under EasyOptions.options[:some_value].
34
+ ## ## The equal sign is optional and can be
35
+ ## ## replaced with blank space when running the
36
+ ## ## target script. If VALUE is composed of
37
+ ## ## digits, it will be converted into an
38
+ ## ## integer, otherwise it will get stored as a
39
+ ## ## string. Short version is not available in
40
+ ## ## this format.
41
+ ##
42
+ ## The above comments work both as source code documentation and as help
43
+ ## text, as well as define the options supported by your script. There is no
44
+ ## duplication of the options specification. The string @#script.name will be
45
+ ## replaced with the actual script name.
46
+ ##
47
+ ## After writing your documentation, you simply require this script. Then all
48
+ ## command line options will get parsed into the EasyOptions.options hash, as
49
+ ## described above. You can then check their values for reacting to them. All
50
+ ## regular arguments will get stored into the EasyOptions.arguments array.
51
+ ##
52
+ ## In fact, this script is an example of itself. You are seeing this help
53
+ ## message either because you are reading the source code, or you have called
54
+ ## the script in command line with the --help option.
55
+ ##
56
+ ## Note: the options and arguments are also available as global variables in
57
+ ## current version, but their use is discouraged and is supposed to be
58
+ ## eventually removed.
59
+ ##
60
+ ## This script can be used from Bash scripts as well. If the $from environment
61
+ ## variable is set, that will be assumed as the source Bash script from which to
62
+ ## parse the documentation and the provided options. Then, instead of parsing
63
+ ## the options into Ruby variables, evaluable assignment statements will be
64
+ ## generated for the corresponding Bash environment variables. For example:
65
+ ##
66
+ ## eval "$(from="$0" @script.name "$@" || echo exit 1)"
67
+ ##
68
+ ## If the script containing this command is documented as in the example above,
69
+ ## and it is executed from command line with the -o and --some-value=10 options,
70
+ ## and one regular argument abc, then the evaluable output would look like this:
71
+ ##
72
+ ## option="yes"
73
+ ## some_value="10"
74
+ ## unset arguments
75
+ ## arguments+=("abc")
76
+ ## arguments
77
+ ##
78
+
79
+ module EasyOptions
80
+ class Option
81
+ def initialize(long_version, short_version, boolean = true)
82
+ fail ArgumentError.new('Long version is mandatory') if !long_version || long_version.length < 2
83
+ @short = short_version.to_sym if short_version
84
+ @long = long_version.to_s.gsub('-', '_').to_sym
85
+ @boolean = boolean
86
+ end
87
+
88
+ def to_s
89
+ "--#{long_dashed}"
90
+ end
91
+
92
+ def in?(string)
93
+ string =~ /^--#{long_dashed}$/ || (@short && string =~ /^-#{@short}$/)
94
+ end
95
+
96
+ def in_with_value?(string)
97
+ string =~ /^--#{long_dashed}=.*$/
98
+ end
99
+
100
+ def long_dashed
101
+ @long.to_s.gsub('_', '-')
102
+ end
103
+ attr_accessor :short
104
+ attr_accessor :long
105
+ attr_accessor :boolean
106
+ end
107
+
108
+ class Parser
109
+ def initialize
110
+ @known_options = [Option.new(:help, :h)]
111
+ @documentation = parse_doc
112
+ @arguments = []
113
+ @options = {}
114
+ end
115
+
116
+ def parse_doc
117
+ begin
118
+ doc = File.readlines($PROGRAM_NAME)
119
+ rescue Errno::ENOENT
120
+ exit false
121
+ end
122
+ doc = doc.find_all do |line|
123
+ line =~ /^##[^#]*/
124
+ end
125
+ doc.map do |line|
126
+ line.strip!
127
+ line.sub!(/^## ?/, '')
128
+ line.gsub!(/@script.name/, File.basename($PROGRAM_NAME))
129
+ line.gsub(/@#/, '@')
130
+ end
131
+ end
132
+
133
+ def parse
134
+ # Parse known options from documentation
135
+ @documentation.map do |line|
136
+ line = line.strip
137
+ case line
138
+ when /^-h, --help.*/ then next
139
+ when /^--help, -h.*/ then next
140
+ when /^-.*, --.*/ then line = line.split(/(^-|,\s--|\s)/); @known_options << Option.new(line[4], line[2])
141
+ when /^--.*, -.*/ then line = line.split(/(--|,\s-|\s)/); @known_options << Option.new(line[2], line[4])
142
+ when /^--.*=.*/ then line = line.split(/(--|=|\s)/); @known_options << Option.new(line[2], nil, false)
143
+ when /^--.* .*/ then line = line.split(/(--|\s)/); @known_options << Option.new(line[2], nil)
144
+ end
145
+ end
146
+
147
+ # Format arguments input
148
+ raw_arguments = ARGV.map do |argument|
149
+ if argument =~ /^-[^-].*$/i
150
+ argument.split('')[1..-1].map { |char| "-#{char}" }
151
+ else
152
+ argument
153
+ end
154
+ end.flatten
155
+
156
+ # Parse the provided options
157
+ raw_arguments.each_with_index do |argument, index|
158
+ unknown_option = true
159
+ @known_options.each do |known_option|
160
+
161
+ # Boolean option
162
+ if known_option.in?(argument) && known_option.boolean
163
+ @options[known_option.long] = true
164
+ unknown_option = false
165
+ break
166
+
167
+ # Option with value in next parameter
168
+ elsif known_option.in?(argument) && !known_option.boolean
169
+ value = raw_arguments[index + 1]
170
+ Parser.finish("you must specify a value for #{known_option}") if !value || value.start_with?('-')
171
+ value = value.to_i if value =~ /^[0-9]+$/
172
+ @options[known_option.long] = value
173
+ unknown_option = false
174
+ break
175
+
176
+ # Option with value after equal sign
177
+ elsif known_option.in_with_value?(argument) && !known_option.boolean
178
+ value = argument.split('=')[1]
179
+ value = value.to_i if value =~ /^[0-9]+$/
180
+ @options[known_option.long] = value
181
+ unknown_option = false
182
+ break
183
+
184
+ # Long option with unnecessary value
185
+ elsif known_option.in_with_value?(argument) && known_option.boolean
186
+ value = argument.split('=')[1]
187
+ Parser.finish("#{known_option} does not accept a value (you specified \"#{value}\")")
188
+ end
189
+ end
190
+
191
+ # Unrecognized option
192
+ Parser.finish("unrecognized option \"#{argument}\"") if unknown_option && argument.start_with?('-')
193
+ end
194
+
195
+ # Help option
196
+ if @options[:help]
197
+ if BashOutput
198
+ print "printf '"
199
+ puts @documentation
200
+ puts "'"
201
+ puts 'exit'
202
+ else
203
+ puts @documentation
204
+ end
205
+ exit(-1)
206
+ end
207
+
208
+ # Regular arguments
209
+ next_is_value = false
210
+ raw_arguments.each do |argument|
211
+ if argument.start_with?('-')
212
+ known_option = @known_options.find { |option| option.in?(argument) }
213
+ next_is_value = (known_option && !known_option.boolean)
214
+ else
215
+ arguments << argument unless next_is_value
216
+ next_is_value = false
217
+ end
218
+ end
219
+
220
+ # Bash support
221
+ return unless BashOutput
222
+ @options.keys.each do |name|
223
+ puts "#{name}=\"#{@options[name].to_s.sub('true', 'yes')}\""
224
+ end
225
+ puts 'unset arguments'
226
+ arguments.each do |argument|
227
+ puts "arguments+=(\"#{argument}\")"
228
+ end
229
+ end
230
+
231
+ def self.finish(error)
232
+ warn "Error: #{error}."
233
+ warn 'See --help for usage and options.'
234
+ puts 'exit 1' if BashOutput
235
+ exit false
236
+ end
237
+
238
+ def self.check_bash_output
239
+ $0 = ENV['from'] || $PROGRAM_NAME
240
+ $PROGRAM_NAME == ENV['from']
241
+ end
242
+
243
+ BashOutput = check_bash_output
244
+ attr_accessor :documentation
245
+ attr_accessor :arguments
246
+ attr_accessor :options
247
+ end
248
+
249
+ class << self
250
+ @@parser = Parser.new
251
+ @@parser.parse
252
+ def options
253
+ @@parser.options
254
+ end
255
+
256
+ def arguments
257
+ @@parser.arguments
258
+ end
259
+
260
+ def documentation
261
+ @@parser.documentation
262
+ end
263
+
264
+ def all
265
+ [options, arguments, documentation]
266
+ end
267
+
268
+ def finish(error)
269
+ Parser.finish(error)
270
+ end
271
+ end
272
+
273
+ # This is supposed to be eventually removed
274
+ $documentation = @@parser.documentation
275
+ $arguments = @@parser.arguments
276
+ $options = @@parser.options
277
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easyoptions
3
+ version: !ruby/object:Gem::Version
4
+ version: 2015.2.28
5
+ platform: ruby
6
+ authors:
7
+ - Renato Silva
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: br.renatosilva@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - easyoptions.rb
20
+ homepage: https://github.com/renatosilva/easyoptions
21
+ licenses:
22
+ - BSD
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - "."
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.2.2
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Easy option parsing
44
+ test_files: []