numru-misc 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +41 -0
- data/LICENSE.txt +34 -0
- data/Rakefile +38 -0
- data/doc/emath.html +26 -0
- data/doc/index.html +83 -0
- data/doc/keywordopt.html +179 -0
- data/doc/md_iterators.html +86 -0
- data/doc/misc.html +83 -0
- data/install.rb +104 -0
- data/lib/numru/misc.rb +4 -0
- data/lib/numru/misc/emath.rb +75 -0
- data/lib/numru/misc/keywordopt.rb +498 -0
- data/lib/numru/misc/md_iterators.rb +180 -0
- data/lib/numru/misc/misc.rb +196 -0
- data/makedoc.csh +6 -0
- metadata +97 -0
data/doc/misc.html
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
<?xml version="1.0" ?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
5
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
6
|
+
<head>
|
7
|
+
<title>lib/numru/misc/misc.rb</title>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<h1><a name="label:0" id="label:0">module NumRu::Misc</a></h1><!-- RDLabel: "module NumRu::Misc" -->
|
11
|
+
<h2><a name="label:1" id="label:1">Overview</a></h2><!-- RDLabel: "Overview" -->
|
12
|
+
<p>Miscellaneous functions and classes to facilitate programming.</p>
|
13
|
+
<h2><a name="label:2" id="label:2">Index</a></h2><!-- RDLabel: "Index" -->
|
14
|
+
<p>CLASSES</p>
|
15
|
+
<ul>
|
16
|
+
<li><a href="keywordopt.html">class KeywordOpt</a>
|
17
|
+
to support keyward arguments with default values.</li>
|
18
|
+
<li><a href="narray_ext.html">class NArray (enhancement of NArray made by M Tanaka)</a></li>
|
19
|
+
</ul>
|
20
|
+
<p>MODULES</p>
|
21
|
+
<ul>
|
22
|
+
<li><a href="md_iterators.html">module MD_Iterators</a> A Mixin for classes with
|
23
|
+
multi-dimension indexing support (such as NArray).</li>
|
24
|
+
<li><a href="emath.html">module EMath</a>
|
25
|
+
To be included instead of the Math predefined module (or NMath in NArray).
|
26
|
+
Unlike Math and NMath, EMath handles unknown classes by calling its
|
27
|
+
native instance method (assuming the same name).</li>
|
28
|
+
</ul>
|
29
|
+
<p>MODULE FUNCTIONS</p>
|
30
|
+
<ul>
|
31
|
+
<li><a href="#label:4">check_shape_consistency</a></li>
|
32
|
+
</ul>
|
33
|
+
<h2><a name="label:3" id="label:3">Module functions</a></h2><!-- RDLabel: "Module functions" -->
|
34
|
+
<dl>
|
35
|
+
<dt><a name="label:4" id="label:4"><code>check_shape_consistency(<var>cshapes</var>, *<var>args</var>)</code></a></dt><!-- RDLabel: "check_shape_consistency" -->
|
36
|
+
<dd>
|
37
|
+
<p>Check the consistency of array shapes (multi-dim such as NArray).
|
38
|
+
Exception is raised if inconsistent.</p>
|
39
|
+
<p>ARGUMENTS</p>
|
40
|
+
<ul>
|
41
|
+
<li>cshapes (String) : description of the shapes of the args.
|
42
|
+
Delimited by one-or-more spaces between arrays,
|
43
|
+
and the shape of each array is delimited by a comma. The lengths are
|
44
|
+
expressed with string names as identifiers (in that case, length
|
45
|
+
values are unquestioned) or specified as positive integers.
|
46
|
+
Use '..' or '...' for repetition of the last shape.
|
47
|
+
See EXAMPLES below.</li>
|
48
|
+
<li>args (multi-dim arrays such as NArray): arrays to be checked</li>
|
49
|
+
</ul>
|
50
|
+
<p>RETURN VALUE</p>
|
51
|
+
<ul>
|
52
|
+
<li>nil</li>
|
53
|
+
</ul>
|
54
|
+
<p>POSSIBLE EXCEPTIONS</p>
|
55
|
+
<ul>
|
56
|
+
<li>exception is raised if cshapes and args are inconsistent:
|
57
|
+
<ul>
|
58
|
+
<li>RuntimeError, if the arrays do not have shapes specified by cshapes.</li>
|
59
|
+
<li>ArgeumentError, if the number of args are inconsistent with cshapes.
|
60
|
+
This is likely a coding error of the user.</li>
|
61
|
+
</ul></li>
|
62
|
+
</ul>
|
63
|
+
<p>EXAMPLES</p>
|
64
|
+
<ul>
|
65
|
+
<li><p>to check whether three arrays u, v, and w are shaped as
|
66
|
+
u[nx], v[ny], and w[nx,ny], where nx and ny are any integer:</p>
|
67
|
+
<pre>NumRu::Misc.check_shape_consistency('nx ny nx,ny',u,v,w)</pre>
|
68
|
+
<p>Or equivalently,</p>
|
69
|
+
<pre>NumRu::Misc.check_shape_consistency('m n m,n',u,v,w)</pre>
|
70
|
+
<p>because actual strings does not matter.</p></li>
|
71
|
+
<li><p>To specify fixed lengths, use integers instead of names:</p>
|
72
|
+
<pre>NumRu::Misc.check_shape_consistency('4 n 4,n',u,v,w)</pre>
|
73
|
+
<p>In this case, u,v,w must have shapes [4], [ny], and [4,ny],
|
74
|
+
where ny is any length.</p></li>
|
75
|
+
<li><p>Use '..' or '...' to repeat the same shape:</p>
|
76
|
+
<pre>NumRu::Misc.check_shape_consistency('nx,ny ...',u,v,w)</pre>
|
77
|
+
<p>This ensures that u, v, and w are 2D arrays with the same shape.
|
78
|
+
Note: '..' and '...' are the same, so you can use whichever you like.</p></li>
|
79
|
+
</ul></dd>
|
80
|
+
</dl>
|
81
|
+
|
82
|
+
</body>
|
83
|
+
</html>
|
data/install.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'find'
|
3
|
+
include Config
|
4
|
+
|
5
|
+
if CONFIG["MINOR"].to_i > 6 then $rb_18 = true else $rb_18 = false end
|
6
|
+
if $rb_18
|
7
|
+
require 'fileutils'
|
8
|
+
else
|
9
|
+
require 'ftools'
|
10
|
+
end
|
11
|
+
|
12
|
+
=begin
|
13
|
+
$version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
14
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", $version)
|
15
|
+
# $archdir = File.join($libdir, CONFIG["arch"])
|
16
|
+
$site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
17
|
+
if !$site_libdir
|
18
|
+
$site_libdir = File.join($libdir, "site_ruby")
|
19
|
+
elsif Regexp.compile($site_libdir) !~ Regexp.quote($version)
|
20
|
+
$site_libdir = File.join($site_libdir, $version)
|
21
|
+
end
|
22
|
+
|
23
|
+
default_destdir = $site_libdir
|
24
|
+
=end
|
25
|
+
|
26
|
+
default_destdir = CONFIG["sitelibdir"]
|
27
|
+
|
28
|
+
def install_rb(srcdir, destdir)
|
29
|
+
libdir = "lib"
|
30
|
+
libdir = File.join(srcdir, libdir) if srcdir
|
31
|
+
path = []
|
32
|
+
dir = []
|
33
|
+
Find.find(libdir) do |f|
|
34
|
+
next unless FileTest.file?(f)
|
35
|
+
next if (f = f[libdir.length+1..-1]) == nil
|
36
|
+
next if (/CVS$/ =~ File.dirname(f))
|
37
|
+
path.push f
|
38
|
+
dir |= [File.dirname(f)]
|
39
|
+
end
|
40
|
+
for f in dir
|
41
|
+
next if f == "."
|
42
|
+
next if f == "CVS"
|
43
|
+
if $rb_18
|
44
|
+
FileUtils.makedirs(File.join(destdir, f))
|
45
|
+
else
|
46
|
+
File::makedirs(File.join(destdir, f))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
for f in path
|
50
|
+
next if (/\~$/ =~ f)
|
51
|
+
next if (/^\./ =~ File.basename(f))
|
52
|
+
if $rb_18
|
53
|
+
FileUtils.install(File.join("lib", f), File.join(destdir, f), {:mode => 0644, :verbose => true})
|
54
|
+
else
|
55
|
+
File::install(File.join("lib", f), File.join(destdir, f), 0644, true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ARGV.switch
|
61
|
+
return nil if self.empty?
|
62
|
+
arg = self.shift
|
63
|
+
return nil if arg == '--'
|
64
|
+
if arg =~ /^-(.)(.*)/
|
65
|
+
return arg if $1 == '-'
|
66
|
+
raise 'unknown switch "-"' if $2.index('-')
|
67
|
+
self.unshift "-#{$2}" if $2.size > 0
|
68
|
+
"-#{$1}"
|
69
|
+
else
|
70
|
+
self.unshift arg
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def ARGV.req_arg
|
76
|
+
self.shift || raise('missing argument')
|
77
|
+
end
|
78
|
+
|
79
|
+
destdir = default_destdir
|
80
|
+
|
81
|
+
begin
|
82
|
+
while switch = ARGV.switch
|
83
|
+
case switch
|
84
|
+
when '-d', '--destdir'
|
85
|
+
destdir = ARGV.req_arg
|
86
|
+
# when '-u', '--uninstall'
|
87
|
+
# uninstall = true
|
88
|
+
else
|
89
|
+
raise "unknown switch #{switch.dump}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
rescue
|
93
|
+
STDERR.puts $!.to_s
|
94
|
+
STDERR.puts File.basename($0) +
|
95
|
+
" -d <destdir>"
|
96
|
+
exit 1
|
97
|
+
end
|
98
|
+
|
99
|
+
#if( defined?(uninstall) && uninstall )
|
100
|
+
# uninstall_rb(nil, destdir)
|
101
|
+
#else
|
102
|
+
install_rb(nil, destdir)
|
103
|
+
#end
|
104
|
+
|
data/lib/numru/misc.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
=begin
|
2
|
+
=module NumRu::Misc::EMath
|
3
|
+
|
4
|
+
To be included instead of the Math predefined module (or NMath in NArray).
|
5
|
+
Unlike Math and NMath, EMath handles unknown classes by calling its
|
6
|
+
native instance method (assuming the same name).
|
7
|
+
|
8
|
+
Therefore, if included, its function (module method) is used as:
|
9
|
+
|
10
|
+
cos( obj )
|
11
|
+
|
12
|
+
and so on. If obj is not of a supported class, EMath calls, obj.cos in
|
13
|
+
this case. (If cos is not a method of obj, then an exception is
|
14
|
+
raised.) Supported classes are Numeric (by Math) and NArray (by
|
15
|
+
NMath). EMath stands for "good Math" (for obvious reason for a
|
16
|
+
Japanese).
|
17
|
+
|
18
|
+
Note: as for atan2(a,b), a.atan2(b) will be called if a or b
|
19
|
+
is not supported. This is the case for all functions that take
|
20
|
+
two or more arguments.
|
21
|
+
|
22
|
+
=end
|
23
|
+
|
24
|
+
require "narray"
|
25
|
+
|
26
|
+
module NumRu
|
27
|
+
module Misc
|
28
|
+
module EMath
|
29
|
+
|
30
|
+
E = Math::E
|
31
|
+
PI = Math::PI
|
32
|
+
|
33
|
+
funcs = ["acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh",
|
34
|
+
"cos", "cosh", "erf", "erfc", "exp", "frexp", "hypot",
|
35
|
+
"ldexp", "log", "log10", "sin", "sinh", "sqrt", "tan", "tanh"]
|
36
|
+
# FUNCS: from ruby 1.8.0, ( Math.methods - Object.methods ).sort
|
37
|
+
|
38
|
+
module_function
|
39
|
+
funcs.each{|func|
|
40
|
+
eval <<-EOS,nil,__FILE__,__LINE__+1
|
41
|
+
def #{func}(*arg)
|
42
|
+
case arg[0]
|
43
|
+
when Numeric
|
44
|
+
Math.#{func}(*arg)
|
45
|
+
when NArray
|
46
|
+
NMath.#{func}(*arg)
|
47
|
+
else
|
48
|
+
obj = arg.shift
|
49
|
+
begin
|
50
|
+
obj.#{func}(*arg)
|
51
|
+
rescue NameError
|
52
|
+
raise TypeError,"cannot handle \#{obj.class}: \#{obj.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
EOS
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if __FILE__ == $0
|
63
|
+
include NumRu::Misc::EMath
|
64
|
+
p cos( PI )
|
65
|
+
p cos( Complex(0, 1) )
|
66
|
+
p cos( NArray[0,PI/3,PI/2] )
|
67
|
+
begin
|
68
|
+
p cos( "ggg" )
|
69
|
+
rescue
|
70
|
+
print "* error as expected >>: ",$!,"\n"
|
71
|
+
end
|
72
|
+
require 'narray_miss'
|
73
|
+
nam = NArrayMiss.to_nam( NArray[0,PI/3,PI/2] )
|
74
|
+
p cos(nam)
|
75
|
+
end
|
@@ -0,0 +1,498 @@
|
|
1
|
+
=begin
|
2
|
+
==Index
|
3
|
+
* ((<class NumRu::Misc::KeywordOpt>))
|
4
|
+
* ((<class NumRu::Misc::KeywordOptAutoHelp < NumRu::Misc::KeywordOpt>))
|
5
|
+
|
6
|
+
= class NumRu::Misc::KeywordOpt
|
7
|
+
|
8
|
+
== Overview
|
9
|
+
|
10
|
+
A class to facilitate optional keyword arguments. More specifically,
|
11
|
+
it helps the use of a Hash to mimic the keyword argument system.
|
12
|
+
With this, you can set default values and description to each
|
13
|
+
keyword argument.
|
14
|
+
|
15
|
+
== Classes defined supplementarilly
|
16
|
+
|
17
|
+
=== class NumRu::Misc::HelpMessagingException < StandardError
|
18
|
+
|
19
|
+
This is for your convenience. See the usage example below.
|
20
|
+
|
21
|
+
== Usage example
|
22
|
+
|
23
|
+
Suppose that you introduce keyword arguments "flag" and "number"
|
24
|
+
to the method "hoge" in a class/module Foo. It can be done as
|
25
|
+
follows:
|
26
|
+
|
27
|
+
require 'numru/misc' # or, specifically, require 'numru/misc/keywordopt'
|
28
|
+
include NumRu
|
29
|
+
|
30
|
+
class Foo
|
31
|
+
@@opt_hoge = Misc::KeywordOpt.new(
|
32
|
+
['flag', false, 'whether or not ...'],
|
33
|
+
['number', 1, 'number of ...'],
|
34
|
+
['help', false, 'show help message']
|
35
|
+
)
|
36
|
+
def hoge(regular_arg1, regular_arg2, options=nil)
|
37
|
+
opt = @@opt_hoge.interpret(options)
|
38
|
+
if opt['help']
|
39
|
+
puts @@opt_hoge.help
|
40
|
+
puts ' Current values='+opt.inspect
|
41
|
+
raise Misc::HelpMessagingException, '** show help message and raise **'
|
42
|
+
end
|
43
|
+
# do what you want below
|
44
|
+
# (options are set in the Hash opt: opt['flag'] and opt['number'])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Here, the options are defined in the class variable @@opt_hoge
|
49
|
+
with option names, default values, and descriptions (for help
|
50
|
+
messaging). One can use the method hoge as follows:
|
51
|
+
|
52
|
+
foo = Foo.new
|
53
|
+
...
|
54
|
+
x = ...
|
55
|
+
y = ...
|
56
|
+
...
|
57
|
+
foo.hoge( x, y, {'flag'=>true, 'number'=>10} )
|
58
|
+
|
59
|
+
Or equivalently,
|
60
|
+
|
61
|
+
foo.hoge( x, y, 'flag'=>true, 'number'=>10 )
|
62
|
+
|
63
|
+
because '{}' can be omitted here.
|
64
|
+
|
65
|
+
Tails of options names can be shortened as long as unambiguous:
|
66
|
+
|
67
|
+
foo.hoge( x, y, 'fla'=>true, 'num'=>10 )
|
68
|
+
|
69
|
+
|
70
|
+
To show the help message, call
|
71
|
+
|
72
|
+
foo.hoge( x, y, 'help'=>true )
|
73
|
+
|
74
|
+
This will cause the following help message printed with the
|
75
|
+
exception HelpMessagingException raised.
|
76
|
+
|
77
|
+
<< Description of options >>
|
78
|
+
option name => default value description:
|
79
|
+
"flag" => false whether or not ...
|
80
|
+
"number" => 1 number of ...
|
81
|
+
"help" => false show help message
|
82
|
+
Current values={"help"=>true, "number"=>1, "flag"=>false}
|
83
|
+
NumRu::Misc::HelpMessagingException: ** help messaging done **
|
84
|
+
from (irb):78:in "hoge"
|
85
|
+
from (irb):83
|
86
|
+
|
87
|
+
Do not affraid to write long descriptions. The help method
|
88
|
+
breaks lines nicely if they are long.
|
89
|
+
|
90
|
+
== Class methods
|
91
|
+
|
92
|
+
---KeywordOpt.new( *args )
|
93
|
+
|
94
|
+
Constructor.
|
95
|
+
|
96
|
+
ARGUMENTS
|
97
|
+
* args : (case 1) arrays of two or three elements: [option name,
|
98
|
+
default value, description ], or [option name, default value]
|
99
|
+
if you do not want to write descriptions. Option names and
|
100
|
+
descriptions must be String. (case 2) another KeywordOpt.
|
101
|
+
Cases 1 and 2 can be mixed.
|
102
|
+
|
103
|
+
When case 2, a link to the other KeywordOpt is kept. Thus, change
|
104
|
+
of values in it is reflected to the current one. However,
|
105
|
+
the link is deleted if values are changed by ((<set>)).
|
106
|
+
|
107
|
+
RETURN VALUE
|
108
|
+
* a KeywordOpt object
|
109
|
+
|
110
|
+
EXAMPLE
|
111
|
+
* case 1
|
112
|
+
opt = Misc::KeywordOpt.new(
|
113
|
+
['flag', false, 'whether or not ...'],
|
114
|
+
['help', false, 'show help message']
|
115
|
+
)
|
116
|
+
* case 2
|
117
|
+
opt = Misc::KeywordOpt.new( optA, optB )
|
118
|
+
* case 1 & 2
|
119
|
+
opt = Misc::KeywordOpt.new(
|
120
|
+
['flag', false, 'whether or not ...'],
|
121
|
+
optA
|
122
|
+
)
|
123
|
+
|
124
|
+
== Methods
|
125
|
+
---interpret(hash)
|
126
|
+
|
127
|
+
Interprets a hash that specifies option values.
|
128
|
+
|
129
|
+
ARGUMENTS
|
130
|
+
* hash (Hash or nil) : a hash with string keys matching option names
|
131
|
+
(initializedwhen constructed). The matching is case sensitive and done
|
132
|
+
such that the tail of a option name can be omitted as long as
|
133
|
+
unambiguous (for example, 'num' for 'number').
|
134
|
+
If the argument is nil, the current values are returned.
|
135
|
+
If there are two options like 'max' and 'maxval', to use
|
136
|
+
a key 'max' (identical to the former paramer) is allowed, although
|
137
|
+
it matches 'maxval' as well. (Again 'ma' is regarded ambiguous.)
|
138
|
+
|
139
|
+
RETURN VALUE
|
140
|
+
* a Hash containing the option values (default values overwritten
|
141
|
+
with hash).
|
142
|
+
|
143
|
+
POSSIBLE EXCEPTION
|
144
|
+
* hash has a key that does not match any of the option names.
|
145
|
+
* hash has a key that is ambiguous
|
146
|
+
|
147
|
+
---set(hash)
|
148
|
+
|
149
|
+
Similar to ((<interpret>)) but changes internal values.
|
150
|
+
|
151
|
+
ARGUMENTS
|
152
|
+
* hash (Hash) : see ((<interpret>)). (Here, nil is not permitted though)
|
153
|
+
|
154
|
+
RETURN VALUE
|
155
|
+
* a Hash containing the values replaced (the ones before calling this
|
156
|
+
method)
|
157
|
+
|
158
|
+
POSSIBLE EXCEPTION
|
159
|
+
* the argument is not a Hash
|
160
|
+
* others are same as in ((<interpret>))
|
161
|
+
|
162
|
+
---help
|
163
|
+
|
164
|
+
Returns a help message
|
165
|
+
|
166
|
+
RETURN VALUE
|
167
|
+
* a String describing the option names, default values, and descriptions
|
168
|
+
|
169
|
+
---[](key)
|
170
|
+
|
171
|
+
Returns a value associated with the key (exact matching unlike interpret)
|
172
|
+
|
173
|
+
---keys
|
174
|
+
|
175
|
+
Retunrs the keys.
|
176
|
+
|
177
|
+
---select_existent(hash_or_keys)
|
178
|
+
|
179
|
+
Copies hash_or_keys, exclude ones that are not included in the option
|
180
|
+
(by comparing keys), and returns it. I.e. select only the ones
|
181
|
+
exsitent.
|
182
|
+
|
183
|
+
NOTE: ambiguity is not checked, so the resultant value is not
|
184
|
+
necessarily accepted by ((<interpret>)).
|
185
|
+
|
186
|
+
ARGUMENTS
|
187
|
+
* hash_or_keys (Hash or Array)
|
188
|
+
|
189
|
+
RETURN VALUE
|
190
|
+
* a Hash or Array depending on the class of the argument hash_or_keys
|
191
|
+
|
192
|
+
= class NumRu::Misc::KeywordOptAutoHelp < NumRu::Misc::KeywordOpt
|
193
|
+
|
194
|
+
Same as ((<class NumRu::Misc::KeywordOpt>)), but the method ((<interpret>))
|
195
|
+
shows a help message and raise an exception if option 'help' is provided
|
196
|
+
as an argument and is not nil or false
|
197
|
+
((({NumRu::Misc::HelpMessagingException < StandardError})))
|
198
|
+
or if the arguments cannot be interpreted correctly ((({ArgumentError}))).
|
199
|
+
Option 'help' is automatically defined, so you do not have to define it
|
200
|
+
yourself.
|
201
|
+
|
202
|
+
=end
|
203
|
+
|
204
|
+
module NumRu
|
205
|
+
|
206
|
+
module Misc
|
207
|
+
class HelpMessagingException < StandardError
|
208
|
+
end
|
209
|
+
|
210
|
+
class KeywordOpt
|
211
|
+
def initialize(*args)
|
212
|
+
# USAGE:
|
213
|
+
# KeywordOpt.new([key,val,description],[key,val,description],..)
|
214
|
+
# where key is a String, and description can be omitted.
|
215
|
+
@val=Hash.new
|
216
|
+
@description=Hash.new
|
217
|
+
@keys = []
|
218
|
+
args.each{ |x|
|
219
|
+
case x
|
220
|
+
when Array
|
221
|
+
unless (x[0]=='help') && @keys.include?(x[0])
|
222
|
+
#^only 'help' can overwrap in the arguments
|
223
|
+
@keys.push(x[0])
|
224
|
+
@val[x[0]] = x[1]
|
225
|
+
@description[x[0]] = ( (x.length>=3) ? x[2] : '' )
|
226
|
+
end
|
227
|
+
when KeywordOpt
|
228
|
+
x.keys.each{|k|
|
229
|
+
unless k=='help' && @keys.include?(k)
|
230
|
+
#^only 'help' can overwrap in the arguments
|
231
|
+
@keys.push(k)
|
232
|
+
@val[k] = x #.val[k]
|
233
|
+
@description[k] = x.description[k]
|
234
|
+
end
|
235
|
+
}
|
236
|
+
def @val.[](k)
|
237
|
+
val = super(k)
|
238
|
+
val.is_a?(KeywordOpt) ? val[k] : val
|
239
|
+
end
|
240
|
+
def @val.dup
|
241
|
+
out = Hash.new
|
242
|
+
each{|k,val| out[k] = (val.is_a?(KeywordOpt) ? val[k] : val)}
|
243
|
+
out
|
244
|
+
end
|
245
|
+
else
|
246
|
+
raise ArgumentError, "invalid argument: #{x.inspect}"
|
247
|
+
end
|
248
|
+
}
|
249
|
+
@keys_sort = @keys.sort
|
250
|
+
if @keys_sort.length != @keys_sort.uniq.length
|
251
|
+
raise ArgumentError, "keys are not unique"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def interpret(hash)
|
256
|
+
return @val.dup if hash.nil?
|
257
|
+
##
|
258
|
+
len = @val.length
|
259
|
+
im = 0
|
260
|
+
out = @val.dup
|
261
|
+
hash.keys.sort.each do |key|
|
262
|
+
rkey = /^#{key}/
|
263
|
+
loop do
|
264
|
+
if rkey =~ @keys_sort[im]
|
265
|
+
if im<len-1 && rkey=~@keys_sort[im+1] &&
|
266
|
+
key != @keys_sort[im] # not identical
|
267
|
+
raise ArgumentError, "Ambiguous key specification '#{key}'."
|
268
|
+
end
|
269
|
+
out[@keys_sort[im]]=hash[key]
|
270
|
+
break
|
271
|
+
end
|
272
|
+
im += 1
|
273
|
+
if im==len
|
274
|
+
raise ArgumentError, "'#{key}' does not match any of the keys."
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
out
|
279
|
+
end
|
280
|
+
|
281
|
+
def select_existent(hash_or_keys)
|
282
|
+
hash_or_keys = hash_or_keys.dup # not to alter the original
|
283
|
+
len = @val.length
|
284
|
+
im = 0
|
285
|
+
kys = ( Array === hash_or_keys ? hash_or_keys : hash_or_keys.keys )
|
286
|
+
kys.sort.each do |key|
|
287
|
+
rkey = /^#{key}/
|
288
|
+
loop do
|
289
|
+
break if rkey =~ @keys_sort[im]
|
290
|
+
im += 1
|
291
|
+
if im==len
|
292
|
+
hash_or_keys.delete(key)
|
293
|
+
im = 0 # rewind
|
294
|
+
break
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
hash_or_keys
|
299
|
+
end
|
300
|
+
|
301
|
+
def set(hash)
|
302
|
+
raise ArgumentError, "not a hash" if !hash.is_a?(Hash)
|
303
|
+
##
|
304
|
+
replaced = Hash.new
|
305
|
+
len = @val.length
|
306
|
+
im = 0
|
307
|
+
hash.keys.sort.each do |key|
|
308
|
+
rkey = /^#{key}/
|
309
|
+
loop do
|
310
|
+
if rkey =~ @keys_sort[im]
|
311
|
+
if im<len-1 && rkey=~@keys_sort[im+1]
|
312
|
+
raise "Ambiguous key specification '#{key}'."
|
313
|
+
end
|
314
|
+
replaced[@keys_sort[im]] = @val[@keys_sort[im]]
|
315
|
+
@val[@keys_sort[im]]=hash[key]
|
316
|
+
break
|
317
|
+
end
|
318
|
+
im += 1
|
319
|
+
raise "'#{key}' does not match any of the keys." if im==len
|
320
|
+
end
|
321
|
+
end
|
322
|
+
replaced
|
323
|
+
end
|
324
|
+
|
325
|
+
# def __line_feed(str)
|
326
|
+
# if str.length >= 68
|
327
|
+
# idx = str[0..67].rindex(/\s/)
|
328
|
+
# if idx
|
329
|
+
# str[idx, 1] = "\n\t"
|
330
|
+
# end
|
331
|
+
# end
|
332
|
+
# str
|
333
|
+
# end
|
334
|
+
def __line_feed(str, len)
|
335
|
+
if str.length >= len
|
336
|
+
idx = str[0...len].rindex(/\s/)
|
337
|
+
if idx
|
338
|
+
str = str[0...idx] + "\n\t\t\t# " + __line_feed(str[(idx+1)..-1],50)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
str
|
342
|
+
end
|
343
|
+
private :__line_feed
|
344
|
+
|
345
|
+
def help
|
346
|
+
" option name\tdefault value\t# description:\n" +
|
347
|
+
@keys.collect{|k|
|
348
|
+
__line_feed(" #{k.inspect}\t#{@val[k].inspect}\t# #{@description[k]}", 66)
|
349
|
+
}.join("\n")
|
350
|
+
end
|
351
|
+
|
352
|
+
def [](k)
|
353
|
+
v = @val[k]
|
354
|
+
if v.is_a?(KeywordOpt)
|
355
|
+
v = v.val[k]
|
356
|
+
end
|
357
|
+
v
|
358
|
+
end
|
359
|
+
|
360
|
+
def keys
|
361
|
+
@keys.dup
|
362
|
+
end
|
363
|
+
|
364
|
+
##### protected methods #####
|
365
|
+
protected
|
366
|
+
attr_reader :val, :description
|
367
|
+
end
|
368
|
+
|
369
|
+
##################################################
|
370
|
+
|
371
|
+
class KeywordOptAutoHelp < KeywordOpt
|
372
|
+
def initialize(*args)
|
373
|
+
args.push(['help', false, 'show help message if true'])
|
374
|
+
super(*args)
|
375
|
+
end
|
376
|
+
|
377
|
+
def interpret(hash)
|
378
|
+
begin
|
379
|
+
out = super(hash)
|
380
|
+
rescue
|
381
|
+
raise $!.inspect + "\n Available parameters are:\n" + help
|
382
|
+
end
|
383
|
+
if out['help']
|
384
|
+
puts "<< Description of options >>\n" + help
|
385
|
+
puts ' Current values=' + out.inspect
|
386
|
+
raise Misc::HelpMessagingException, '** help messaging done **'
|
387
|
+
end
|
388
|
+
out
|
389
|
+
end
|
390
|
+
|
391
|
+
def set(hash)
|
392
|
+
raise ArgumentError, "not a hash" if !hash.is_a?(Hash)
|
393
|
+
if hash['help']
|
394
|
+
puts "<< Description of options >>\n" + help
|
395
|
+
raise Misc::HelpMessagingException, '** help messaging done **'
|
396
|
+
end
|
397
|
+
super
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
if __FILE__ == $0
|
405
|
+
include NumRu
|
406
|
+
|
407
|
+
class Foo
|
408
|
+
@@opt_hoge = Misc::KeywordOpt.new(
|
409
|
+
['flag', false, 'whether or not ...'],
|
410
|
+
['number', 1, 'number of ...'],
|
411
|
+
['fff', 1, 'fff...'],
|
412
|
+
['help', false, 'show help message']
|
413
|
+
)
|
414
|
+
def self.change_default(hash)
|
415
|
+
@@opt_hoge.set(hash)
|
416
|
+
end
|
417
|
+
def hoge(regular_arg1, regular_arg2, options=nil)
|
418
|
+
opt = @@opt_hoge.interpret(options)
|
419
|
+
if opt['help']
|
420
|
+
puts "* Description of options:\n" + @@opt_hoge.help
|
421
|
+
puts ' Current values='+opt.inspect
|
422
|
+
raise Misc::HelpMessagingException, '** show help message and raise **'
|
423
|
+
end
|
424
|
+
# do what you want below
|
425
|
+
# (options are set in the Hash opt: opt['flag'] and opt['number'])
|
426
|
+
p opt
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
foo = Foo.new
|
431
|
+
x = 1
|
432
|
+
y = 1
|
433
|
+
print "### 0 ###\n"
|
434
|
+
foo.hoge( x, y, {'flag'=>true, 'number'=>10} )
|
435
|
+
foo.hoge( x, y )
|
436
|
+
print "### 1 ###\n"
|
437
|
+
foo.hoge( x, y, 'fla'=>true, 'num'=>10 )
|
438
|
+
print "### 2 ###\n"
|
439
|
+
begin
|
440
|
+
foo.hoge( x, y, 'help'=>true )
|
441
|
+
rescue
|
442
|
+
puts $!
|
443
|
+
end
|
444
|
+
print "### 3 ###\n"
|
445
|
+
Foo.change_default( {'number'=>3} )
|
446
|
+
begin
|
447
|
+
foo.hoge( x, y, 'fla'=>true, 'num'=>10, 'help'=>true)
|
448
|
+
rescue
|
449
|
+
puts $!
|
450
|
+
end
|
451
|
+
print "### 4 ###\n"
|
452
|
+
begin
|
453
|
+
foo.hoge( x, y, 'dummy'=>nil)
|
454
|
+
rescue
|
455
|
+
puts $!
|
456
|
+
end
|
457
|
+
print "### 5 ###\n"
|
458
|
+
begin
|
459
|
+
foo.hoge( x, y, 'f'=>nil)
|
460
|
+
rescue
|
461
|
+
puts $!
|
462
|
+
end
|
463
|
+
|
464
|
+
print "\n###### test of KeywordOptAutoHelp ######\n"
|
465
|
+
opt = Misc::KeywordOptAutoHelp.new(
|
466
|
+
['flag', false, 'whether or not ...'],
|
467
|
+
['number', 1, 'number of ...']
|
468
|
+
)
|
469
|
+
print "### 11 ###\n"
|
470
|
+
begin
|
471
|
+
opt.interpret('flag'=>10,'help'=>true)
|
472
|
+
rescue
|
473
|
+
puts $!
|
474
|
+
end
|
475
|
+
print "### 12 ###\n"
|
476
|
+
begin
|
477
|
+
opt.interpret('nnn'=>10)
|
478
|
+
rescue
|
479
|
+
puts $!
|
480
|
+
end
|
481
|
+
|
482
|
+
print "### 13 ###\n"
|
483
|
+
opt2 = Misc::KeywordOptAutoHelp.new(
|
484
|
+
['flafla', false, 'whether or not ...']
|
485
|
+
)
|
486
|
+
opt3 = Misc::KeywordOptAutoHelp.new( opt, opt2 )
|
487
|
+
p opt3.interpret('flag'=>true)
|
488
|
+
begin
|
489
|
+
opt3.interpret('help'=>true)
|
490
|
+
rescue
|
491
|
+
puts $!
|
492
|
+
end
|
493
|
+
|
494
|
+
print "### 14 ###\n"
|
495
|
+
p opt2.keys, opt.keys
|
496
|
+
p opt.select_existent({"flag"=>99, "num"=>88, 'acb'=>333})
|
497
|
+
p opt.select_existent(["flag", "num", 'acb'])
|
498
|
+
end
|