pezra-options 2.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.
- data/CHANGELOG +7 -0
- data/README +218 -0
- data/README.erb +41 -0
- data/Rakefile +216 -0
- data/lib/options.rb +224 -0
- data/options.gemspec +26 -0
- data/samples/a.rb +15 -0
- data/samples/b.rb +50 -0
- data/samples/c.rb +20 -0
- data/samples/d.rb +15 -0
- data/samples/e.rb +18 -0
- data/spec/options_spec.rb +61 -0
- data/spec/spec_helper.rb +7 -0
- metadata +65 -0
data/CHANGELOG
ADDED
data/README
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
NAME
|
|
2
|
+
options.rb
|
|
3
|
+
|
|
4
|
+
DESCRIPTION
|
|
5
|
+
options.rb simplifies the common idiom of dealing with keyword options in
|
|
6
|
+
ruby functions. it also deals correctly with symbol vs string keywords and
|
|
7
|
+
prevents many subtle programming errors that can arise from doing so
|
|
8
|
+
incorrectly. options.rb doesn't hack ruby's core with one exception: the
|
|
9
|
+
method Array#options.
|
|
10
|
+
|
|
11
|
+
SYNOPSIS
|
|
12
|
+
require 'options'
|
|
13
|
+
|
|
14
|
+
def method(*args, &block)
|
|
15
|
+
args, options = Options.parse(args)
|
|
16
|
+
|
|
17
|
+
a = args.shift
|
|
18
|
+
b = args.shift
|
|
19
|
+
|
|
20
|
+
force = options.getopt(:force, default = false)
|
|
21
|
+
verbose = options.getopt([:verbose, :VERBOSE])
|
|
22
|
+
foo, bar = options.getopt(:foo, :bar)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
INSTALL
|
|
26
|
+
gem install options
|
|
27
|
+
|
|
28
|
+
HISTORY
|
|
29
|
+
2.2.0:
|
|
30
|
+
- Calculated default values
|
|
31
|
+
- #getopt second arg can be an options hash
|
|
32
|
+
2.1.1:
|
|
33
|
+
- Improved samples
|
|
34
|
+
- Improved 1.9 compatibility
|
|
35
|
+
2.1.0:
|
|
36
|
+
- 1.9 compatibility
|
|
37
|
+
- Validation of passed options
|
|
38
|
+
|
|
39
|
+
SAMPLES
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<========< samples/a.rb >========>
|
|
43
|
+
|
|
44
|
+
~ > cat samples/a.rb
|
|
45
|
+
|
|
46
|
+
require 'options'
|
|
47
|
+
|
|
48
|
+
# options.rb makes it super easy to deal with keyword options in a safe and
|
|
49
|
+
# easy way.
|
|
50
|
+
#
|
|
51
|
+
|
|
52
|
+
def method(*args)
|
|
53
|
+
args, options = Options.parse(args)
|
|
54
|
+
|
|
55
|
+
force = options.getopt(:force, :default => false)
|
|
56
|
+
p force
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
method(:foo, :bar, :force => true)
|
|
60
|
+
method('force' => true)
|
|
61
|
+
|
|
62
|
+
~ > ruby samples/a.rb
|
|
63
|
+
|
|
64
|
+
true
|
|
65
|
+
true
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
<========< samples/b.rb >========>
|
|
69
|
+
|
|
70
|
+
~ > cat samples/b.rb
|
|
71
|
+
|
|
72
|
+
require 'options'
|
|
73
|
+
|
|
74
|
+
# options.rb avoids common mistakes made handling keyword arguments
|
|
75
|
+
#
|
|
76
|
+
|
|
77
|
+
def broken(*args)
|
|
78
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
79
|
+
if options[:force]
|
|
80
|
+
puts 'forcing'
|
|
81
|
+
else
|
|
82
|
+
puts 'broken'
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def nonbroken(*args)
|
|
87
|
+
args, options = Options.parse(args)
|
|
88
|
+
if options.getopt(:force)
|
|
89
|
+
puts 'nonbroken'
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
broken('force' => true)
|
|
94
|
+
nonbroken('force' => true)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def fubar(*args)
|
|
99
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
100
|
+
verbose = options[:verbose] || true
|
|
101
|
+
if verbose
|
|
102
|
+
if options[:verbose]
|
|
103
|
+
puts 'verbosely'
|
|
104
|
+
else
|
|
105
|
+
puts 'fubar'
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def nonfubar(*args)
|
|
111
|
+
args, options = Options.parse(args)
|
|
112
|
+
verbose = options.getopt(:verbose)
|
|
113
|
+
if verbose
|
|
114
|
+
puts 'verbosely'
|
|
115
|
+
else
|
|
116
|
+
puts 'nonfubar'
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
fubar(:verbose => false)
|
|
121
|
+
nonfubar(:verbose => false)
|
|
122
|
+
|
|
123
|
+
~ > ruby samples/b.rb
|
|
124
|
+
|
|
125
|
+
broken
|
|
126
|
+
nonbroken
|
|
127
|
+
fubar
|
|
128
|
+
nonfubar
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
<========< samples/c.rb >========>
|
|
132
|
+
|
|
133
|
+
~ > cat samples/c.rb
|
|
134
|
+
|
|
135
|
+
require 'options'
|
|
136
|
+
|
|
137
|
+
# options.rb hacks ruby core in exactly one way - the method Array#options
|
|
138
|
+
#
|
|
139
|
+
|
|
140
|
+
def method(*args)
|
|
141
|
+
options = args.options
|
|
142
|
+
p :args => args
|
|
143
|
+
p :options => options
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
method(:a, :b, :k => :v)
|
|
147
|
+
|
|
148
|
+
def method2(*args)
|
|
149
|
+
options = args.options.pop
|
|
150
|
+
p :args => args
|
|
151
|
+
p :options => options
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
method2(:a, :b, :k => :v)
|
|
155
|
+
|
|
156
|
+
~ > ruby samples/c.rb
|
|
157
|
+
|
|
158
|
+
{:args=>[:a, :b, {:k=>:v}]}
|
|
159
|
+
{:options=>{:k=>:v}}
|
|
160
|
+
{:args=>[:a, :b]}
|
|
161
|
+
{:options=>{:k=>:v}}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
<========< samples/d.rb >========>
|
|
165
|
+
|
|
166
|
+
~ > cat samples/d.rb
|
|
167
|
+
|
|
168
|
+
require 'options'
|
|
169
|
+
|
|
170
|
+
# options.rb makes it easy to provide good error messages when people
|
|
171
|
+
# misuse a method.
|
|
172
|
+
#
|
|
173
|
+
|
|
174
|
+
def method(*args)
|
|
175
|
+
args, options = Options.parse(args)
|
|
176
|
+
options.validate(:force)
|
|
177
|
+
|
|
178
|
+
force = options.getopt(:force, default=false)
|
|
179
|
+
p force
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
method(:foo, :bar, :misspelled_option => true)
|
|
183
|
+
|
|
184
|
+
~ > ruby samples/d.rb
|
|
185
|
+
|
|
186
|
+
/Users/pezra/Development/options/lib/options.rb:188:in `validate': Unrecognized options: misspelled_option (ArgumentError)
|
|
187
|
+
from samples/d.rb:9:in `method'
|
|
188
|
+
from samples/d.rb:15:in `<main>'
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
<========< samples/e.rb >========>
|
|
192
|
+
|
|
193
|
+
~ > cat samples/e.rb
|
|
194
|
+
|
|
195
|
+
require 'options'
|
|
196
|
+
require 'date'
|
|
197
|
+
|
|
198
|
+
# You can also provide a lambda (or anything that responds to `#call`)
|
|
199
|
+
# as the default value. If default responds to `#call` the return
|
|
200
|
+
# value of `#call` be used as the default value. Default value
|
|
201
|
+
# calculation procs will not be called when the option is
|
|
202
|
+
# present. This allows for runtime calculation of default values, or
|
|
203
|
+
# for defaults that are expensive to create.
|
|
204
|
+
|
|
205
|
+
def method(*args)
|
|
206
|
+
args, options = Options.parse(args)
|
|
207
|
+
|
|
208
|
+
force = options.getopt(:force, :default => lambda{Date.today.day.even?})
|
|
209
|
+
p force
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
method(:foo) # force will be true on even days and false on odd days
|
|
213
|
+
|
|
214
|
+
~ > ruby samples/e.rb
|
|
215
|
+
|
|
216
|
+
true
|
|
217
|
+
|
|
218
|
+
|
data/README.erb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
NAME
|
|
2
|
+
options.rb
|
|
3
|
+
|
|
4
|
+
DESCRIPTION
|
|
5
|
+
options.rb simplifies the common idiom of dealing with keyword options in
|
|
6
|
+
ruby functions. it also deals correctly with symbol vs string keywords and
|
|
7
|
+
prevents many subtle programming errors that can arise from doing so
|
|
8
|
+
incorrectly. options.rb doesn't hack ruby's core with one exception: the
|
|
9
|
+
method Array#options.
|
|
10
|
+
|
|
11
|
+
SYNOPSIS
|
|
12
|
+
require 'options'
|
|
13
|
+
|
|
14
|
+
def method(*args, &block)
|
|
15
|
+
args, options = Options.parse(args)
|
|
16
|
+
|
|
17
|
+
a = args.shift
|
|
18
|
+
b = args.shift
|
|
19
|
+
|
|
20
|
+
force = options.getopt(:force, default = false)
|
|
21
|
+
verbose = options.getopt([:verbose, :VERBOSE])
|
|
22
|
+
foo, bar = options.getopt(:foo, :bar)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
INSTALL
|
|
26
|
+
gem install options
|
|
27
|
+
|
|
28
|
+
HISTORY
|
|
29
|
+
2.2.0:
|
|
30
|
+
- Runtime calculation of default values
|
|
31
|
+
- #getopt second arg may be an options hash
|
|
32
|
+
2.1.1:
|
|
33
|
+
- Improved samples
|
|
34
|
+
- Improved 1.9 compatibility
|
|
35
|
+
2.1.0:
|
|
36
|
+
- 1.9 compatibility
|
|
37
|
+
- Validation of passed options
|
|
38
|
+
|
|
39
|
+
SAMPLES
|
|
40
|
+
|
|
41
|
+
<%= samples %>
|
data/Rakefile
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
This.rubyforge_project = 'codeforpeople'
|
|
2
|
+
This.author = "Ara T. Howard"
|
|
3
|
+
This.email = "ara.t.howard@gmail.com"
|
|
4
|
+
This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
task :default do
|
|
8
|
+
puts(Rake::Task.tasks.map{|task| task.name} - ['default'])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
task :spec do
|
|
12
|
+
require 'spec/rake/spectask'
|
|
13
|
+
Spec::Rake::SpecTask.new do |t|
|
|
14
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
require 'pp'
|
|
19
|
+
task :gemspec do
|
|
20
|
+
shiteless = lambda do |list|
|
|
21
|
+
ignore_patterns = '*.git', '*.tmp', '*.sw?', '*.gem', 'pkg/**/*', 'test/log'
|
|
22
|
+
Rake::FileList.new(*list).exclude(*ignore_patterns) {|f| File.directory?(f)}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
lib = This.lib
|
|
26
|
+
version = This.version
|
|
27
|
+
test_files = shiteless[FileList.new('test/**/*', 'spec/**/*')]
|
|
28
|
+
files = shiteless[Dir::glob("**/**")] - test_files
|
|
29
|
+
executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
|
|
30
|
+
has_rdoc = true #File.exist?('doc')
|
|
31
|
+
|
|
32
|
+
extensions = This.extensions
|
|
33
|
+
if extensions.nil?
|
|
34
|
+
%w( Makefile configure extconf.rb ).each do |ext|
|
|
35
|
+
extensions << ext if File.exists?(ext)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
extensions = [extensions].flatten.compact
|
|
39
|
+
|
|
40
|
+
template =
|
|
41
|
+
if test(?e, 'gemspec.erb')
|
|
42
|
+
Template{ IO.read('gemspec.erb') }
|
|
43
|
+
else
|
|
44
|
+
Template {
|
|
45
|
+
<<-__
|
|
46
|
+
## #{ lib }.gemspec
|
|
47
|
+
#
|
|
48
|
+
|
|
49
|
+
Gem::Specification::new do |spec|
|
|
50
|
+
spec.name = #{ lib.inspect }
|
|
51
|
+
spec.version = #{ version.inspect }
|
|
52
|
+
spec.platform = Gem::Platform::RUBY
|
|
53
|
+
spec.summary = #{ lib.inspect }
|
|
54
|
+
|
|
55
|
+
spec.files = #{ files.inspect }
|
|
56
|
+
spec.executables = #{ executables.inspect }
|
|
57
|
+
|
|
58
|
+
spec.require_path = "lib"
|
|
59
|
+
|
|
60
|
+
spec.has_rdoc = #{ has_rdoc.inspect }
|
|
61
|
+
spec.test_files = #{ test_files.inspect }
|
|
62
|
+
#spec.add_dependency 'lib', '>= version'
|
|
63
|
+
#spec.add_dependency 'fattr'
|
|
64
|
+
|
|
65
|
+
spec.extensions.push(*#{ extensions.inspect })
|
|
66
|
+
|
|
67
|
+
spec.rubyforge_project = #{ This.rubyforge_project.inspect }
|
|
68
|
+
spec.author = #{ This.author.inspect }
|
|
69
|
+
spec.email = #{ This.email.inspect }
|
|
70
|
+
spec.homepage = #{ This.homepage.inspect }
|
|
71
|
+
end
|
|
72
|
+
__
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
open("#{ lib }.gemspec", "w"){|fd| fd.puts template}
|
|
77
|
+
This.gemspec = "#{ lib }.gemspec"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
task :gem => [:clean, :gemspec] do
|
|
81
|
+
Fu.mkdir_p This.pkgdir
|
|
82
|
+
before = Dir['*.gem']
|
|
83
|
+
cmd = "gem build #{ This.gemspec }"
|
|
84
|
+
`#{ cmd }`
|
|
85
|
+
after = Dir['*.gem']
|
|
86
|
+
gem = ((after - before).first || after.first) or abort('no gem!')
|
|
87
|
+
Fu.mv gem, This.pkgdir
|
|
88
|
+
This.gem = File.basename(gem)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
task :readme do
|
|
92
|
+
samples = ''
|
|
93
|
+
prompt = '~ > '
|
|
94
|
+
lib = This.lib
|
|
95
|
+
version = This.version
|
|
96
|
+
|
|
97
|
+
Dir['sample*/*.rb'].sort.each do |sample|
|
|
98
|
+
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
|
99
|
+
|
|
100
|
+
cmd = "cat #{ sample }"
|
|
101
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
|
102
|
+
samples << Util.indent(`#{ cmd }`, 4) << "\n"
|
|
103
|
+
|
|
104
|
+
cmd = "ruby #{ sample }"
|
|
105
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
|
106
|
+
|
|
107
|
+
cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -Ilib #{ sample })'"
|
|
108
|
+
samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
template =
|
|
112
|
+
if test(?e, 'readme.erb')
|
|
113
|
+
Template{ IO.read('readme.erb') }
|
|
114
|
+
else
|
|
115
|
+
Template {
|
|
116
|
+
<<-__
|
|
117
|
+
NAME
|
|
118
|
+
#{ lib }
|
|
119
|
+
|
|
120
|
+
DESCRIPTION
|
|
121
|
+
|
|
122
|
+
INSTALL
|
|
123
|
+
gem install #{ lib }
|
|
124
|
+
|
|
125
|
+
SAMPLES
|
|
126
|
+
#{ samples }
|
|
127
|
+
__
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
open("README", "w"){|fd| fd.puts template}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
task :clean do
|
|
136
|
+
Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
task :release => [:clean, :gemspec, :gem] do
|
|
141
|
+
gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
|
|
142
|
+
raise "which one? : #{ gems.inspect }" if gems.size > 1
|
|
143
|
+
raise "no gems?" if gems.size < 1
|
|
144
|
+
cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.pkgdir }/#{ This.gem }"
|
|
145
|
+
puts cmd
|
|
146
|
+
system cmd
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
BEGIN {
|
|
154
|
+
$VERBOSE = nil
|
|
155
|
+
|
|
156
|
+
require 'ostruct'
|
|
157
|
+
require 'erb'
|
|
158
|
+
require 'fileutils'
|
|
159
|
+
|
|
160
|
+
Fu = FileUtils
|
|
161
|
+
|
|
162
|
+
This = OpenStruct.new
|
|
163
|
+
|
|
164
|
+
This.file = File.expand_path(__FILE__)
|
|
165
|
+
This.dir = File.dirname(This.file)
|
|
166
|
+
This.pkgdir = File.join(This.dir, 'pkg')
|
|
167
|
+
|
|
168
|
+
lib = ENV['LIB']
|
|
169
|
+
unless lib
|
|
170
|
+
lib = File.basename(Dir.pwd)
|
|
171
|
+
end
|
|
172
|
+
This.lib = lib
|
|
173
|
+
|
|
174
|
+
version = ENV['VERSION']
|
|
175
|
+
unless version
|
|
176
|
+
name = lib.capitalize
|
|
177
|
+
require "./lib/#{ lib }"
|
|
178
|
+
version = eval(name).send(:version)
|
|
179
|
+
end
|
|
180
|
+
This.version = version
|
|
181
|
+
|
|
182
|
+
abort('no lib') unless This.lib
|
|
183
|
+
abort('no version') unless This.version
|
|
184
|
+
|
|
185
|
+
module Util
|
|
186
|
+
def indent(s, n = 2)
|
|
187
|
+
s = unindent(s)
|
|
188
|
+
ws = ' ' * n
|
|
189
|
+
s.gsub(%r/^/, ws)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def unindent(s)
|
|
193
|
+
indent = nil
|
|
194
|
+
s.lines do |line|
|
|
195
|
+
next if line =~ %r/^\s*$/
|
|
196
|
+
indent = line[%r/^\s*/] and break
|
|
197
|
+
end
|
|
198
|
+
indent ? s.gsub(%r/^#{ indent }/, "") : s
|
|
199
|
+
end
|
|
200
|
+
extend self
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
class Template
|
|
204
|
+
def initialize(&block)
|
|
205
|
+
@block = block
|
|
206
|
+
@template = block.call.to_s
|
|
207
|
+
end
|
|
208
|
+
def expand(b=nil)
|
|
209
|
+
ERB.new(Util.unindent(@template)).result(b||@block.binding)
|
|
210
|
+
end
|
|
211
|
+
alias_method 'to_s', 'expand'
|
|
212
|
+
end
|
|
213
|
+
def Template(*args, &block) Template.new(*args, &block) end
|
|
214
|
+
|
|
215
|
+
Dir.chdir(This.dir)
|
|
216
|
+
}
|
data/lib/options.rb
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
module Options
|
|
2
|
+
VERSION = '2.2.0'
|
|
3
|
+
|
|
4
|
+
class << Options
|
|
5
|
+
def version
|
|
6
|
+
Options::VERSION
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def normalize!(hash)
|
|
10
|
+
hash.keys.each{|key| hash[key.to_s.to_sym] = hash.delete(key) unless Symbol===key}
|
|
11
|
+
hash
|
|
12
|
+
end
|
|
13
|
+
alias_method 'to_options!', 'normalize!'
|
|
14
|
+
|
|
15
|
+
def normalize(hash)
|
|
16
|
+
normalize!(hash.dup)
|
|
17
|
+
end
|
|
18
|
+
alias_method 'to_options', 'normalize'
|
|
19
|
+
|
|
20
|
+
def stringify!(hash)
|
|
21
|
+
hash.keys.each{|key| hash[key.to_s] = hash.delete(key) unless String===key}
|
|
22
|
+
hash
|
|
23
|
+
end
|
|
24
|
+
alias_method 'stringified!', 'stringify!'
|
|
25
|
+
|
|
26
|
+
def stringify(hash)
|
|
27
|
+
stringify!(hash)
|
|
28
|
+
end
|
|
29
|
+
alias_method 'stringified', 'stringify'
|
|
30
|
+
|
|
31
|
+
def for(hash)
|
|
32
|
+
hash =
|
|
33
|
+
case hash
|
|
34
|
+
when Hash
|
|
35
|
+
hash
|
|
36
|
+
when Array
|
|
37
|
+
Hash[*hash.flatten]
|
|
38
|
+
when String, Symbol
|
|
39
|
+
{hash => true}
|
|
40
|
+
else
|
|
41
|
+
hash.to_hash
|
|
42
|
+
end
|
|
43
|
+
normalize!(hash)
|
|
44
|
+
ensure
|
|
45
|
+
hash.extend(Options) unless hash.is_a?(Options)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def parse(args)
|
|
49
|
+
case args
|
|
50
|
+
when Array
|
|
51
|
+
args.extend(Arguments) unless args.is_a?(Arguments)
|
|
52
|
+
[args, args.options.pop]
|
|
53
|
+
when Hash
|
|
54
|
+
Options.for(args)
|
|
55
|
+
else
|
|
56
|
+
raise ArgumentError, "`args` should be and Array or Hash"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_options!
|
|
62
|
+
replace to_options
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def to_options
|
|
66
|
+
keys.inject(Hash.new){|h,k| h.update k.to_s.to_sym => fetch(k)}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def getopt key, default = nil
|
|
70
|
+
[ key ].flatten.each do |key|
|
|
71
|
+
return fetch(key) if has_key?(key)
|
|
72
|
+
key = key.to_s
|
|
73
|
+
return fetch(key) if has_key?(key)
|
|
74
|
+
key = key.to_sym
|
|
75
|
+
return fetch(key) if has_key?(key)
|
|
76
|
+
end
|
|
77
|
+
# did not find the option; fall back on default
|
|
78
|
+
|
|
79
|
+
default = Options.for(default).getopt(:default) if Hash === default
|
|
80
|
+
|
|
81
|
+
if default.respond_to?(:call)
|
|
82
|
+
default.call
|
|
83
|
+
else
|
|
84
|
+
default
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def getopts *args
|
|
89
|
+
args.flatten.map{|arg| getopt arg}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def hasopt key, default = nil
|
|
93
|
+
[ key ].flatten.each do |key|
|
|
94
|
+
return true if has_key?(key)
|
|
95
|
+
key = key.to_s
|
|
96
|
+
return true if has_key?(key)
|
|
97
|
+
key = key.to_sym
|
|
98
|
+
return true if has_key?(key)
|
|
99
|
+
end
|
|
100
|
+
default
|
|
101
|
+
end
|
|
102
|
+
alias_method 'hasopt?', 'hasopt'
|
|
103
|
+
|
|
104
|
+
def hasopts *args
|
|
105
|
+
args.flatten.map{|arg| hasopt arg}
|
|
106
|
+
end
|
|
107
|
+
alias_method 'hasopts?', 'hasopts'
|
|
108
|
+
|
|
109
|
+
def delopt key, default = nil
|
|
110
|
+
[ key ].flatten.each do |key|
|
|
111
|
+
return delete(key) if has_key?(key)
|
|
112
|
+
key = key.to_s
|
|
113
|
+
return delete(key) if has_key?(key)
|
|
114
|
+
key = key.to_sym
|
|
115
|
+
return delete(key) if has_key?(key)
|
|
116
|
+
end
|
|
117
|
+
default
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def delopts *args
|
|
121
|
+
args.flatten.map{|arg| delopt arg}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def setopt key, value = nil
|
|
125
|
+
[ key ].flatten.each do |key|
|
|
126
|
+
return self[key]=value if has_key?(key)
|
|
127
|
+
key = key.to_s
|
|
128
|
+
return self[key]=value if has_key?(key)
|
|
129
|
+
key = key.to_sym
|
|
130
|
+
return self[key]=value if has_key?(key)
|
|
131
|
+
end
|
|
132
|
+
return self[key]=value
|
|
133
|
+
end
|
|
134
|
+
alias_method 'setopt!', 'setopt'
|
|
135
|
+
|
|
136
|
+
def setopts opts
|
|
137
|
+
opts.each{|key, value| setopt key, value}
|
|
138
|
+
opts
|
|
139
|
+
end
|
|
140
|
+
alias_method 'setopts!', 'setopts'
|
|
141
|
+
|
|
142
|
+
def select! *a, &b
|
|
143
|
+
replace select(*a, &b).to_hash
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def normalize!
|
|
147
|
+
Options.normalize!(self)
|
|
148
|
+
end
|
|
149
|
+
alias_method 'normalized!', 'normalize!'
|
|
150
|
+
alias_method 'to_options!', 'normalize!'
|
|
151
|
+
|
|
152
|
+
def normalize
|
|
153
|
+
Options.normalize(self)
|
|
154
|
+
end
|
|
155
|
+
alias_method 'normalized', 'normalize'
|
|
156
|
+
alias_method 'to_options', 'normalize'
|
|
157
|
+
|
|
158
|
+
def stringify!
|
|
159
|
+
Options.stringify!(self)
|
|
160
|
+
end
|
|
161
|
+
alias_method 'stringified!', 'stringify!'
|
|
162
|
+
|
|
163
|
+
def stringify
|
|
164
|
+
Options.stringify(self)
|
|
165
|
+
end
|
|
166
|
+
alias_method 'stringified', 'stringify'
|
|
167
|
+
|
|
168
|
+
attr_accessor :arguments
|
|
169
|
+
def pop
|
|
170
|
+
pop! unless popped?
|
|
171
|
+
self
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def popped?
|
|
175
|
+
defined?(@popped) and @popped
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def pop!
|
|
179
|
+
@popped = arguments.pop
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Validates that the options provided are acceptable.
|
|
183
|
+
#
|
|
184
|
+
# @param [Symbol] *acceptable_options List of options that are
|
|
185
|
+
# allowed
|
|
186
|
+
def validate(*acceptable_options)
|
|
187
|
+
remaining = (provided_options - acceptable_options).map{|opt| opt.to_s}.sort
|
|
188
|
+
raise ArgumentError, "Unrecognized options: #{remaining.join(', ')}" unless remaining.empty?
|
|
189
|
+
|
|
190
|
+
self
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
protected
|
|
194
|
+
|
|
195
|
+
def provided_options
|
|
196
|
+
normalize!.keys
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
module Arguments
|
|
201
|
+
def options
|
|
202
|
+
@options ||= Options.for(last.is_a?(Hash) ? last : {})
|
|
203
|
+
ensure
|
|
204
|
+
@options.arguments = self
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
class << Arguments
|
|
208
|
+
def for(args)
|
|
209
|
+
args.extend(Arguments) unless args.is_a?(Arguments)
|
|
210
|
+
args
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def parse(args)
|
|
214
|
+
[args, Options.parse(args)]
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
class Array
|
|
220
|
+
def options
|
|
221
|
+
extend(Arguments) unless is_a?(Arguments)
|
|
222
|
+
options
|
|
223
|
+
end
|
|
224
|
+
end
|
data/options.gemspec
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## options.gemspec
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
Gem::Specification::new do |spec|
|
|
5
|
+
spec.name = "options"
|
|
6
|
+
spec.version = "2.2.0"
|
|
7
|
+
spec.platform = Gem::Platform::RUBY
|
|
8
|
+
spec.summary = "options"
|
|
9
|
+
|
|
10
|
+
spec.files = ["CHANGELOG", "lib/options.rb", "options.gemspec", "Rakefile", "README", "README.erb", "samples/a.rb", "samples/b.rb", "samples/c.rb", "samples/d.rb", "samples/e.rb"]
|
|
11
|
+
spec.executables = []
|
|
12
|
+
|
|
13
|
+
spec.require_path = "lib"
|
|
14
|
+
|
|
15
|
+
spec.has_rdoc = true
|
|
16
|
+
spec.test_files = ["spec/options_spec.rb", "spec/spec_helper.rb"]
|
|
17
|
+
#spec.add_dependency 'lib', '>= version'
|
|
18
|
+
#spec.add_dependency 'fattr'
|
|
19
|
+
|
|
20
|
+
spec.extensions.push(*[])
|
|
21
|
+
|
|
22
|
+
spec.rubyforge_project = "codeforpeople"
|
|
23
|
+
spec.author = "Ara T. Howard"
|
|
24
|
+
spec.email = "ara.t.howard@gmail.com"
|
|
25
|
+
spec.homepage = "http://github.com/ahoward/options/tree/master"
|
|
26
|
+
end
|
data/samples/a.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'options'
|
|
2
|
+
|
|
3
|
+
# options.rb makes it super easy to deal with keyword options in a safe and
|
|
4
|
+
# easy way.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
def method(*args)
|
|
8
|
+
args, options = Options.parse(args)
|
|
9
|
+
|
|
10
|
+
force = options.getopt(:force, :default => false)
|
|
11
|
+
p force
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
method(:foo, :bar, :force => true)
|
|
15
|
+
method('force' => true)
|
data/samples/b.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'options'
|
|
2
|
+
|
|
3
|
+
# options.rb avoids common mistakes made handling keyword arguments
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
def broken(*args)
|
|
7
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
8
|
+
if options[:force]
|
|
9
|
+
puts 'forcing'
|
|
10
|
+
else
|
|
11
|
+
puts 'broken'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def nonbroken(*args)
|
|
16
|
+
args, options = Options.parse(args)
|
|
17
|
+
if options.getopt(:force)
|
|
18
|
+
puts 'nonbroken'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
broken('force' => true)
|
|
23
|
+
nonbroken('force' => true)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def fubar(*args)
|
|
28
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
29
|
+
verbose = options[:verbose] || true
|
|
30
|
+
if verbose
|
|
31
|
+
if options[:verbose]
|
|
32
|
+
puts 'verbosely'
|
|
33
|
+
else
|
|
34
|
+
puts 'fubar'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def nonfubar(*args)
|
|
40
|
+
args, options = Options.parse(args)
|
|
41
|
+
verbose = options.getopt(:verbose)
|
|
42
|
+
if verbose
|
|
43
|
+
puts 'verbosely'
|
|
44
|
+
else
|
|
45
|
+
puts 'nonfubar'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
fubar(:verbose => false)
|
|
50
|
+
nonfubar(:verbose => false)
|
data/samples/c.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'options'
|
|
2
|
+
|
|
3
|
+
# options.rb hacks ruby core in exactly one way - the method Array#options
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
def method(*args)
|
|
7
|
+
options = args.options
|
|
8
|
+
p :args => args
|
|
9
|
+
p :options => options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
method(:a, :b, :k => :v)
|
|
13
|
+
|
|
14
|
+
def method2(*args)
|
|
15
|
+
options = args.options.pop
|
|
16
|
+
p :args => args
|
|
17
|
+
p :options => options
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
method2(:a, :b, :k => :v)
|
data/samples/d.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'options'
|
|
2
|
+
|
|
3
|
+
# options.rb makes it easy to provide good error messages when people
|
|
4
|
+
# misuse a method.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
def method(*args)
|
|
8
|
+
args, options = Options.parse(args)
|
|
9
|
+
options.validate(:force)
|
|
10
|
+
|
|
11
|
+
force = options.getopt(:force, default=false)
|
|
12
|
+
p force
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
method(:foo, :bar, :misspelled_option => true)
|
data/samples/e.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'options'
|
|
2
|
+
require 'date'
|
|
3
|
+
|
|
4
|
+
# You can also provide a lambda (or anything that responds to `#call`)
|
|
5
|
+
# as the default value. If default responds to `#call` the return
|
|
6
|
+
# value of `#call` be used as the default value. Default value
|
|
7
|
+
# calculation procs will not be called when the option is
|
|
8
|
+
# present. This allows for runtime calculation of default values, or
|
|
9
|
+
# for defaults that are expensive to create.
|
|
10
|
+
|
|
11
|
+
def method(*args)
|
|
12
|
+
args, options = Options.parse(args)
|
|
13
|
+
|
|
14
|
+
force = options.getopt(:force, :default => lambda{Date.today.day.even?})
|
|
15
|
+
p force
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
method(:foo) # force will be true on even days and false on odd days
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
|
2
|
+
require 'options'
|
|
3
|
+
|
|
4
|
+
describe Options do
|
|
5
|
+
describe "parsing" do
|
|
6
|
+
it "should be able to handle an options hash" do
|
|
7
|
+
opts = Options.parse({:this => 'that'})
|
|
8
|
+
opts.getopt(:this).should eql('that')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should be able to handle args list w/ options hash" do
|
|
12
|
+
args, opts = Options.parse([:foo, {:this => 'that'}])
|
|
13
|
+
opts.getopt(:this).should eql('that')
|
|
14
|
+
args.should eql([:foo])
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "validation" do
|
|
19
|
+
it "should be able to detect extraneous options" do
|
|
20
|
+
lambda{
|
|
21
|
+
Options.parse({:this => 'test'}).validate(:foo, :bar)
|
|
22
|
+
}.should raise_error(ArgumentError, "Unrecognized options: this")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should list all extraneous options in message" do
|
|
26
|
+
lambda{
|
|
27
|
+
Options.parse({:this => 'test', :that => 'test'}).validate(:foo)
|
|
28
|
+
}.should raise_error(ArgumentError, "Unrecognized options: that, this")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should accept options from simple list" do
|
|
32
|
+
lambda{
|
|
33
|
+
Options.parse({:foo => 'this', :bar => 'that'}).validate(:foo, :bar)
|
|
34
|
+
}.should_not raise_error
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "#getopt" do
|
|
39
|
+
it "should handle simple lookup" do
|
|
40
|
+
Options.for({}).getopt(:this).should be_nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should handle lookup w/ default" do
|
|
44
|
+
Options.for({}).getopt(:this, "some default").should eql('some default')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should handle lookup w/ default in options hash" do
|
|
48
|
+
Options.for({}).getopt(:this, :default => "some default").should eql('some default')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should handle lookup w/ lambda default" do
|
|
52
|
+
ext = 2
|
|
53
|
+
Options.for({}).getopt(:this, lambda{ext + 1}).should eql(3)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should handle lookup w/ lambda default in options hash" do
|
|
57
|
+
ext = 2
|
|
58
|
+
Options.for({}).getopt(:this, :default => lambda{ext + 1}).should eql(3)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pezra-options
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 2.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ara T. Howard
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-05-16 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description:
|
|
17
|
+
email: ara.t.howard@gmail.com
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
|
|
24
|
+
files:
|
|
25
|
+
- CHANGELOG
|
|
26
|
+
- lib/options.rb
|
|
27
|
+
- options.gemspec
|
|
28
|
+
- Rakefile
|
|
29
|
+
- README
|
|
30
|
+
- README.erb
|
|
31
|
+
- samples/a.rb
|
|
32
|
+
- samples/b.rb
|
|
33
|
+
- samples/c.rb
|
|
34
|
+
- samples/d.rb
|
|
35
|
+
- samples/e.rb
|
|
36
|
+
has_rdoc: true
|
|
37
|
+
homepage: http://github.com/ahoward/options/tree/master
|
|
38
|
+
licenses:
|
|
39
|
+
post_install_message:
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
|
|
42
|
+
require_paths:
|
|
43
|
+
- lib
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: "0"
|
|
49
|
+
version:
|
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: "0"
|
|
55
|
+
version:
|
|
56
|
+
requirements: []
|
|
57
|
+
|
|
58
|
+
rubyforge_project: codeforpeople
|
|
59
|
+
rubygems_version: 1.3.5
|
|
60
|
+
signing_key:
|
|
61
|
+
specification_version: 2
|
|
62
|
+
summary: options
|
|
63
|
+
test_files:
|
|
64
|
+
- spec/options_spec.rb
|
|
65
|
+
- spec/spec_helper.rb
|