facets 2.0.5 → 2.1.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/WHATSNEW +11 -0
- data/lib/core/facets/enumerable/collect.rb +7 -2
- data/lib/core/facets/enumerable/permutation.rb +7 -26
- data/lib/core/facets/module/alias.rb +36 -12
- data/lib/core/facets/module/attr.rb +83 -35
- data/lib/core/facets/module/include.rb +10 -0
- data/lib/methods/facets/module/alias_accessor.rb +1 -1
- data/lib/methods/facets/module/alias_reader.rb +1 -0
- data/lib/methods/facets/module/alias_setter.rb +1 -0
- data/lib/methods/facets/module/alias_switcher.rb +1 -0
- data/lib/methods/facets/module/alias_tester.rb +1 -0
- data/lib/methods/facets/module/alias_toggler.rb +1 -0
- data/lib/methods/facets/module/alias_validator.rb +1 -0
- data/lib/methods/facets/module/alias_writer.rb +1 -0
- data/lib/methods/facets/module/attr_accessor.rb +1 -0
- data/lib/methods/facets/module/attr_reader.rb +1 -0
- data/lib/methods/facets/module/attr_switcher.rb +1 -0
- data/lib/methods/facets/module/attr_writer.rb +1 -0
- data/lib/more/facets/arguments.rb +2 -1
- data/lib/more/facets/command.rb +258 -395
- data/lib/more/facets/crypt.rb +242 -28
- data/lib/more/facets/ziputils.rb +1 -1
- data/log/changelog.txt +0 -0
- data/log/history.txt +22 -0
- data/log/release.txt +10 -0
- data/log/todo.txt +4 -0
- data/meta/{facets-2.0.5.roll → facets-2.1.0.roll} +0 -0
- data/meta/google_ad.html +15 -0
- data/meta/icli.yaml +3 -3
- data/meta/manifest.txt +24 -3
- data/task/clobber/package +10 -0
- data/task/config/general.yaml +21 -3
- data/task/isotest +2 -1
- data/task/loadtest +2 -0
- data/task/prepare +4 -2
- data/task/rdoc +122 -73
- data/task/release +12 -0
- data/task/special/quickopts +15 -0
- data/task/syntax +2 -0
- data/task/test +3 -1
- data/test/unit/enumerable/test_collect.rb +17 -0
- data/test/unit/enumerable/test_permutation.rb +20 -30
- data/test/unit/test_crypt.rb +29 -36
- metadata +40 -12
- data/RELEASE +0 -12
- data/test/unit/test_command.rb +0 -286
data/WHATSNEW
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Facets 2.1.0 is in the wild!
|
2
|
+
|
3
|
+
Major changes include a new and much-improved
|
4
|
+
command.rb, a new BiCrypt class for simple two-way
|
5
|
+
crypotgraphy, as well as attr_reader!, attr_writer!
|
6
|
+
and attr_accessor! for flag attributes, plus
|
7
|
+
alias_xxx methods for all attr_xxx methods.
|
8
|
+
|
9
|
+
Enjoy!
|
10
|
+
|
11
|
+
http://facets.rubyforge.org
|
@@ -113,6 +113,12 @@ module Enumerable
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
+
# TODO Faster implementation? Verify equivalency.
|
117
|
+
#def injecting(res, &block)
|
118
|
+
# ([res]*length).zip(to_a).each(&block)
|
119
|
+
# res
|
120
|
+
#end
|
121
|
+
|
116
122
|
# The #group_by method is best explained by example.
|
117
123
|
#
|
118
124
|
# (1..5).partition_by { |n| n % 3 }
|
@@ -150,8 +156,7 @@ module Enumerable
|
|
150
156
|
# CREDIT Erik Veenstra
|
151
157
|
|
152
158
|
def cluster_by(&b)
|
153
|
-
#group_by(&b).values
|
154
|
-
group_by(&b).sort.transpose.pop
|
159
|
+
group_by(&b).sort.transpose.pop || [] # group_by(&b).values ?
|
155
160
|
end
|
156
161
|
|
157
162
|
# Split on matching pattern.
|
@@ -6,7 +6,6 @@
|
|
6
6
|
#
|
7
7
|
# AUTHORS:
|
8
8
|
# - Florian Gross
|
9
|
-
# - Thomas Sawyer
|
10
9
|
|
11
10
|
require 'facets/integer/factorial'
|
12
11
|
|
@@ -17,6 +16,8 @@ module Enumerable
|
|
17
16
|
# Each is index by a permutation number. The maximum number of
|
18
17
|
# arrangements is the factorial of the size of the array.
|
19
18
|
#
|
19
|
+
# CREDIT Florian Gross
|
20
|
+
|
20
21
|
def permutation(number)
|
21
22
|
arr = to_a
|
22
23
|
out = arr[0..0]
|
@@ -34,7 +35,10 @@ module Enumerable
|
|
34
35
|
end
|
35
36
|
alias :permute :permutation
|
36
37
|
|
38
|
+
# Calculate permutation number.
|
37
39
|
#
|
40
|
+
# CREDIT Florian Gross
|
41
|
+
|
38
42
|
def permutation_number(original_array=self.to_a.sort)
|
39
43
|
arr = to_a
|
40
44
|
m = 1
|
@@ -62,6 +66,8 @@ module Enumerable
|
|
62
66
|
# cab
|
63
67
|
# cba
|
64
68
|
#
|
69
|
+
# CREDIT Florian Gross
|
70
|
+
|
65
71
|
def each_permutation()
|
66
72
|
arr = to_a
|
67
73
|
size = arr.size
|
@@ -98,28 +104,3 @@ module Enumerable
|
|
98
104
|
#++
|
99
105
|
|
100
106
|
end
|
101
|
-
|
102
|
-
|
103
|
-
# _____ _
|
104
|
-
# |_ _|__ ___| |_
|
105
|
-
# | |/ _ \/ __| __|
|
106
|
-
# | | __/\__ \ |_
|
107
|
-
# |_|\___||___/\__|
|
108
|
-
#
|
109
|
-
=begin test
|
110
|
-
|
111
|
-
require 'test/unit'
|
112
|
-
require 'set'
|
113
|
-
|
114
|
-
class TestEnumerablePermutation < Test::Unit::TestCase
|
115
|
-
|
116
|
-
def test_permutation
|
117
|
-
o = Set.new
|
118
|
-
%w[a b c].each_permutation { |x| o << x.join('') }
|
119
|
-
r = Set.new(['abc','acb','bac','bca','cab','cba'])
|
120
|
-
assert_equal( r, o )
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
=end
|
@@ -23,15 +23,6 @@ class Module
|
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
|
-
# Like module_funtion but makes the instance method
|
27
|
-
# public rather than private. This can not work
|
28
|
-
# as a sectional modifier however.
|
29
|
-
|
30
|
-
def module_method *meth
|
31
|
-
module_function *meth
|
32
|
-
public *meth
|
33
|
-
end
|
34
|
-
|
35
26
|
# As with alias_method, but alias both reader and writer.
|
36
27
|
#
|
37
28
|
# attr_accessor :x
|
@@ -41,9 +32,42 @@ class Module
|
|
41
32
|
# self.y = 2
|
42
33
|
# x #=> 2
|
43
34
|
|
44
|
-
def alias_accessor(
|
45
|
-
|
46
|
-
|
35
|
+
def alias_accessor(*args)
|
36
|
+
orig = args.last
|
37
|
+
args = args - [orig]
|
38
|
+
args.each do |name|
|
39
|
+
alias_method(name, orig)
|
40
|
+
alias_method("#{name}=", "#{orig}=")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# As with alias_accessor, but just for the reader.
|
45
|
+
|
46
|
+
def alias_reader(*args)
|
47
|
+
orig = args.last
|
48
|
+
args = args - [orig]
|
49
|
+
args.each do |name|
|
50
|
+
alias_method(name, orig)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# As with alias_method but does the writer instead.
|
55
|
+
|
56
|
+
def alias_writer(*args)
|
57
|
+
orig = args.last
|
58
|
+
args = args - [orig]
|
59
|
+
args.each do |name|
|
60
|
+
alias_method("#{name}=", "#{orig}=")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Like module_funtion but makes the instance method
|
65
|
+
# public rather than private. This can not work
|
66
|
+
# as a sectional modifier however.
|
67
|
+
|
68
|
+
def module_method *meth
|
69
|
+
module_function *meth
|
70
|
+
public *meth
|
47
71
|
end
|
48
72
|
|
49
73
|
# Alias a module function so that the alias is also
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# TITLE:
|
2
2
|
#
|
3
|
-
# Attributes Methods
|
3
|
+
# Extra Attributes Methods
|
4
4
|
#
|
5
5
|
# SUMMARY:
|
6
6
|
#
|
@@ -9,9 +9,6 @@
|
|
9
9
|
# AUTHORS:
|
10
10
|
#
|
11
11
|
# - Thomas Sawyer
|
12
|
-
#
|
13
|
-
# TODO:
|
14
|
-
# - Need better name for attr_toggler, since it's not realy a "toggle".
|
15
12
|
|
16
13
|
#
|
17
14
|
class Module
|
@@ -30,7 +27,16 @@ class Module
|
|
30
27
|
end
|
31
28
|
made << "#{symbol}=".to_sym
|
32
29
|
end
|
33
|
-
|
30
|
+
made
|
31
|
+
end
|
32
|
+
|
33
|
+
def alias_validator(*args)
|
34
|
+
orig = args.last
|
35
|
+
args = args - [orig]
|
36
|
+
args.each do |name|
|
37
|
+
alias_method(name, orig)
|
38
|
+
alias_method("#{name}=", "#{orig}=")
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
# TODO Perhaps need to make a check against overriding annotated version.
|
@@ -62,31 +68,62 @@ class Module
|
|
62
68
|
made << "#{a}".to_sym
|
63
69
|
end
|
64
70
|
module_eval code
|
65
|
-
|
71
|
+
made
|
72
|
+
end
|
73
|
+
|
74
|
+
def alias_setter(*args)
|
75
|
+
args = args - [orig]
|
76
|
+
args.each do |name|
|
77
|
+
alias_method(name, orig)
|
78
|
+
end
|
66
79
|
end
|
67
80
|
|
68
|
-
# Create
|
69
|
-
# each given
|
70
|
-
#
|
81
|
+
# Create a toggle attribute. This creates two methods for
|
82
|
+
# each given name. One is a form of tester and the other
|
83
|
+
# is used to toggle the value.
|
71
84
|
#
|
72
|
-
#
|
85
|
+
# attr_accessor! :a
|
73
86
|
#
|
74
87
|
# is equivalent to
|
75
88
|
#
|
76
89
|
# def a?
|
77
|
-
# @a
|
90
|
+
# @a
|
78
91
|
# end
|
79
92
|
#
|
80
|
-
# def a!(
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
93
|
+
# def a!(value=true)
|
94
|
+
# @a = value
|
95
|
+
# self
|
96
|
+
# end
|
97
|
+
|
98
|
+
def attr_accessor!(*args)
|
99
|
+
attr_reader!(*args) + attr_writer!(*args)
|
100
|
+
end
|
101
|
+
alias_method :attr_switcher, :attr_accessor!
|
102
|
+
alias_method :attr_toggler, :attr_accessor!
|
103
|
+
|
104
|
+
def alias_accessor!(*args)
|
105
|
+
orig = args.last
|
106
|
+
args = args - [orig]
|
107
|
+
args.each do |name|
|
108
|
+
alias_method("#{name}?", "#{orig}?")
|
109
|
+
alias_method("#{name}!", "#{orig}!")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
alias_method :alias_switcher, :alias_accessor!
|
113
|
+
alias_method :alias_toggler, :alias_accessor!
|
114
|
+
|
115
|
+
# Create an tester attribute. This creates a single methods
|
116
|
+
# used to test the attribute for truth.
|
117
|
+
#
|
118
|
+
# attr_reader! :a
|
119
|
+
#
|
120
|
+
# is equivalent to
|
121
|
+
#
|
122
|
+
# def a?
|
123
|
+
# @a ? true : @a
|
87
124
|
# end
|
88
125
|
|
89
|
-
def
|
126
|
+
def attr_reader!(*args)
|
90
127
|
code, made = '', []
|
91
128
|
args.each do |a|
|
92
129
|
code << %{
|
@@ -97,43 +134,54 @@ class Module
|
|
97
134
|
made << "#{a}?".to_sym
|
98
135
|
end
|
99
136
|
module_eval code
|
100
|
-
|
137
|
+
made
|
101
138
|
end
|
139
|
+
alias_method :attr_reader?, :attr_reader!
|
140
|
+
alias_method :attr_tester, :attr_reader!
|
102
141
|
|
103
|
-
|
104
|
-
|
105
|
-
|
142
|
+
def alias_reader!(*args)
|
143
|
+
orig = args.last
|
144
|
+
args = args - [orig]
|
145
|
+
args.each do |name|
|
146
|
+
alias_method("#{name}?", "#{orig}?")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
alias_method :alias_reader?, :alias_reader!
|
150
|
+
alias_method :alias_tester, :alias_reader!
|
151
|
+
|
152
|
+
# Create a flaggable attribute. This creates a single methods
|
153
|
+
# used to set an attribute to "true".
|
106
154
|
#
|
107
|
-
#
|
155
|
+
# attr_writer! :a
|
108
156
|
#
|
109
157
|
# is equivalent to
|
110
158
|
#
|
111
|
-
# def a?
|
112
|
-
# @a
|
113
|
-
# end
|
114
|
-
#
|
115
159
|
# def a!(value=true)
|
116
160
|
# @a = value
|
117
161
|
# self
|
118
162
|
# end
|
119
163
|
|
120
|
-
def
|
164
|
+
def attr_writer!(*args)
|
121
165
|
code, made = '', []
|
122
166
|
args.each do |a|
|
123
167
|
code << %{
|
124
168
|
def #{a}!(value=true)
|
125
|
-
|
169
|
+
@#{a} = value
|
126
170
|
self
|
127
171
|
end
|
128
|
-
def #{a}?
|
129
|
-
@#{a}
|
130
|
-
end
|
131
172
|
}
|
132
173
|
made << "#{a}!".to_sym
|
133
|
-
made << "#{a}?".to_sym
|
134
174
|
end
|
135
175
|
module_eval code
|
136
|
-
|
176
|
+
made
|
177
|
+
end
|
178
|
+
|
179
|
+
def alias_writer!(*args)
|
180
|
+
orig = args.last
|
181
|
+
args = args - [orig]
|
182
|
+
args.each do |name|
|
183
|
+
alias_method("#{name}!", "#{orig}!")
|
184
|
+
end
|
137
185
|
end
|
138
186
|
|
139
187
|
end
|
@@ -10,6 +10,16 @@ class Module
|
|
10
10
|
#
|
11
11
|
alias_method :is, :include
|
12
12
|
|
13
|
+
# TODO
|
14
|
+
# def is(*mods)
|
15
|
+
# mods.each do |mod|
|
16
|
+
# if mod.const_defined?(:Self)
|
17
|
+
# extend mod.const_get(:Self)
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# include(*mods)
|
21
|
+
# end
|
22
|
+
|
13
23
|
# Expirmental idea for #is.
|
14
24
|
#
|
15
25
|
# If the module has #append_function_function
|
@@ -1 +1 @@
|
|
1
|
-
require 'facets/module/
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/module/attr.rb'
|
@@ -58,7 +58,7 @@ class Console::Arguments
|
|
58
58
|
# Takes the command line string (or array) and options.
|
59
59
|
# Options have flags and end with a hash of option arity.
|
60
60
|
#
|
61
|
-
def initialize(
|
61
|
+
def initialize(line=nil, arity=nil)
|
62
62
|
@line, @argv = parse_line(line)
|
63
63
|
@arity = parse_arity(arity||{})
|
64
64
|
parse
|
@@ -237,6 +237,7 @@ class Console::Arguments
|
|
237
237
|
key, val = key.split('=')
|
238
238
|
elsif a = arity[key]
|
239
239
|
val = args.slice!(0,a)
|
240
|
+
val = val.first if a == 1
|
240
241
|
else
|
241
242
|
val = true
|
242
243
|
end
|
data/lib/more/facets/command.rb
CHANGED
@@ -20,182 +20,323 @@
|
|
20
20
|
#
|
21
21
|
# AUTHORS:
|
22
22
|
#
|
23
|
-
# -
|
24
|
-
# - Tyler Rick
|
23
|
+
# - Trans
|
25
24
|
#
|
26
|
-
#
|
25
|
+
# TODO:
|
27
26
|
#
|
28
|
-
# - Add
|
29
|
-
# -
|
30
|
-
#
|
31
|
-
# LOG:
|
32
|
-
#
|
33
|
-
# - 2007.10.31 TRANS
|
34
|
-
# Re-added support for __option notation.
|
27
|
+
# - Add global options to master command, or "all are master options" flag?
|
28
|
+
# - Add usage/help/documentation/man features.
|
35
29
|
|
36
|
-
require 'shellwords'
|
37
30
|
#require 'facets/annotations' # for help ?
|
31
|
+
#require 'facets/module/attr'
|
32
|
+
#require 'facets/kernel/constant'
|
33
|
+
#require 'shellwords'
|
34
|
+
require 'facets/arguments'
|
38
35
|
|
39
36
|
module Console
|
40
37
|
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
38
|
+
# For CommandOptions, but defined external to it, so
|
39
|
+
# that it is easy to access from user defined commands.
|
40
|
+
# (This lookup issue should be fixed in Ruby 1.9+, and then
|
41
|
+
# the class can be moved back into Command namespace.)
|
42
|
+
|
43
|
+
class NoOptionError < NoMethodError
|
44
|
+
def initialize(name, *arg)
|
45
|
+
super("unknown option -- #{name}", name, *args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class NoCommandError < NoMethodError
|
50
|
+
def initialize(name, *arg)
|
51
|
+
super("unknown subcommand -- #{name}", name, *args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Here is an example of usage:
|
50
56
|
#
|
51
|
-
#
|
57
|
+
# # General Options
|
52
58
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
59
|
+
# module GeneralOptions
|
60
|
+
# attr_accessor :dryrun ; alias_accessor :n, :noharm, :dryrun
|
61
|
+
# attr_accessor :quiet ; alias_accessor :q, :quiet
|
62
|
+
# attr_accessor :force
|
63
|
+
# attr_accessor :trace
|
56
64
|
# end
|
57
65
|
#
|
58
|
-
#
|
66
|
+
# # Build Subcommand
|
59
67
|
#
|
60
|
-
# class
|
68
|
+
# class BuildCommand < Console::Command
|
69
|
+
# include GeneralOptions
|
61
70
|
#
|
62
|
-
#
|
71
|
+
# # metadata files
|
72
|
+
# attr_accessor :file ; alias_accessor :f, :file
|
73
|
+
# attr_accessor :manifest ; alias_accessor :m, :manifest
|
63
74
|
#
|
64
|
-
#
|
65
|
-
#
|
75
|
+
# def call
|
76
|
+
# # do stuf here
|
66
77
|
# end
|
78
|
+
# end
|
67
79
|
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
80
|
+
# # Box Master Command
|
81
|
+
#
|
82
|
+
# class BoxCommand < Console::MasterCommand
|
83
|
+
# subcommand :build, BuildCommand
|
71
84
|
# end
|
72
85
|
#
|
86
|
+
# BoxCommand.start
|
73
87
|
|
74
|
-
class
|
88
|
+
class MasterCommand
|
89
|
+
|
90
|
+
#
|
75
91
|
|
76
|
-
|
92
|
+
module UniversalOptions
|
93
|
+
end
|
77
94
|
|
78
|
-
|
95
|
+
#
|
79
96
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
#cmd.instance_variable_set("@global_options",global_options)
|
84
|
-
cmd.execute(*args)
|
97
|
+
def self.option_arity(arity_hash=nil)
|
98
|
+
if arity_hash
|
99
|
+
(@option_arity ||= {}).merge!(arity_hash)
|
85
100
|
end
|
86
|
-
|
101
|
+
@option_arity
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
87
105
|
|
88
|
-
|
89
|
-
|
90
|
-
|
106
|
+
def self.start(line=nil)
|
107
|
+
cargs = Console::Arguments.new(line || ARGV, option_arity)
|
108
|
+
pre = cargs.preoptions
|
109
|
+
cmd, argv = *cargs.subcommand
|
110
|
+
args, opts = *argv
|
111
|
+
if is_a?(UniversalOptions)
|
112
|
+
new(pre, opts).call(cmd, args, opts)
|
113
|
+
else
|
114
|
+
new(pre).call(cmd, args, opts)
|
91
115
|
end
|
92
|
-
|
116
|
+
end
|
93
117
|
|
94
|
-
|
95
|
-
# or a block whihc will be used to create an Command::Options subclass.
|
118
|
+
#
|
96
119
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
120
|
+
def self.subcommand(name, command_class, options=nil)
|
121
|
+
options ||= {}
|
122
|
+
if options[:no_merge]
|
123
|
+
file, line = __FILE__, __LINE__+1
|
124
|
+
code = %{
|
125
|
+
def #{name}(args, opts)
|
126
|
+
#{command_class}.new(args, opts).call
|
127
|
+
end
|
128
|
+
}
|
129
|
+
else
|
130
|
+
file, line = __FILE__, __LINE__+1
|
131
|
+
code = %{
|
132
|
+
def #{name}(args, opts)
|
133
|
+
opts.merge(master_options)
|
134
|
+
#{command_class}.new(args, opts).call
|
135
|
+
end
|
136
|
+
}
|
104
137
|
end
|
105
|
-
|
106
|
-
|
138
|
+
class_eval(code, file, line)
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
107
142
|
|
108
|
-
|
143
|
+
attr :master_options
|
109
144
|
|
110
|
-
|
111
|
-
|
145
|
+
#
|
146
|
+
|
147
|
+
def initialize(*options)
|
148
|
+
initialize_options(*options)
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
|
153
|
+
def initialize_options(*options)
|
154
|
+
options = options.inject{ |h,o| h.merge(o) }
|
155
|
+
begin
|
156
|
+
opt, val = nil, nil
|
157
|
+
options.each do |opt, val|
|
158
|
+
send("#{opt}=", val)
|
159
|
+
end
|
160
|
+
rescue NoMethodError
|
161
|
+
option_missing(opt, val)
|
112
162
|
end
|
163
|
+
@master_options = options
|
164
|
+
end
|
165
|
+
|
166
|
+
public
|
113
167
|
|
114
|
-
|
168
|
+
#
|
115
169
|
|
116
|
-
|
117
|
-
|
170
|
+
def call(cmd, args, opts)
|
171
|
+
cmd = :default if cmd.nil?
|
172
|
+
begin
|
173
|
+
subcommand = method(cmd)
|
174
|
+
parameters = [args, opts]
|
175
|
+
rescue NameError
|
176
|
+
subcommand = method(:subcommand_missing)
|
177
|
+
parameters = [cmd, args, opts]
|
178
|
+
end
|
179
|
+
if subcommand.arity < 0
|
180
|
+
subcommand.call(*parameters[0..subcommand.arity])
|
181
|
+
else
|
182
|
+
subcommand.call(*parameters[0,subcommand.arity])
|
118
183
|
end
|
119
184
|
end
|
120
185
|
|
121
|
-
|
186
|
+
#
|
187
|
+
|
188
|
+
def help; end
|
122
189
|
|
123
|
-
|
124
|
-
|
125
|
-
|
190
|
+
def default ; help ; end
|
191
|
+
|
192
|
+
private
|
126
193
|
|
127
194
|
#
|
128
195
|
|
129
|
-
def
|
130
|
-
|
196
|
+
def subcommand_missing(cmd, args, opt)
|
197
|
+
help
|
198
|
+
#raise NoCommandError.new(cmd, args << opt)
|
199
|
+
end
|
131
200
|
|
132
|
-
|
133
|
-
g_keys = self.class.global_options
|
201
|
+
#
|
134
202
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
203
|
+
def option_missing(opt, arg=nil)
|
204
|
+
raise NoOptionError.new(opt)
|
205
|
+
end
|
139
206
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
argv = g_opts.parse(argv, :stop => true)
|
146
|
-
cmd = argv.find{ |s| s !~ /^-/ }
|
147
|
-
argv.delete_at(argv.index(cmd)) if cmd
|
148
|
-
cmd = :default unless cmd
|
149
|
-
cmd = cmd.to_sym
|
150
|
-
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# = Command base class
|
210
|
+
#
|
211
|
+
# See MasterCommand for example.
|
151
212
|
|
152
|
-
|
213
|
+
class Command
|
153
214
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
215
|
+
def self.start(line=nil)
|
216
|
+
cargs = Console::Argument.new(line || ARGV)
|
217
|
+
pre = cargs.preoptions
|
218
|
+
args, opts = *cargs.parameters
|
219
|
+
new(args, opts).call
|
220
|
+
end
|
158
221
|
|
159
|
-
|
222
|
+
attr :arguments
|
223
|
+
attr :options
|
160
224
|
|
161
|
-
|
225
|
+
#
|
226
|
+
|
227
|
+
def call
|
228
|
+
puts "Not implemented yet."
|
162
229
|
end
|
163
230
|
|
231
|
+
private
|
232
|
+
|
164
233
|
#
|
165
234
|
|
166
|
-
def
|
167
|
-
|
235
|
+
def initialize(arguments, options=nil)
|
236
|
+
initialize_arguments(*arguments)
|
237
|
+
initialize_options(options)
|
238
|
+
end
|
168
239
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
#end
|
240
|
+
#
|
241
|
+
|
242
|
+
def initialize_arguments(*arguments)
|
243
|
+
@arguments = arguments
|
244
|
+
end
|
245
|
+
|
246
|
+
#
|
247
|
+
|
248
|
+
def initialize_options(options)
|
249
|
+
options = options || {}
|
250
|
+
begin
|
251
|
+
opt, val = nil, nil
|
252
|
+
options.each do |opt, val|
|
253
|
+
send("#{opt}=", val)
|
184
254
|
end
|
255
|
+
rescue NoMethodError
|
256
|
+
option_missing(opt, val)
|
185
257
|
end
|
258
|
+
@options = options
|
186
259
|
end
|
187
260
|
|
188
|
-
#
|
189
|
-
# self.class.global_options
|
190
|
-
#end
|
261
|
+
#
|
191
262
|
|
192
263
|
def option_missing(opt, arg=nil)
|
193
|
-
raise
|
264
|
+
raise NoOptionError.new(opt)
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
class Array
|
273
|
+
|
274
|
+
# Not empty?
|
275
|
+
|
276
|
+
def not_empty?
|
277
|
+
!empty?
|
278
|
+
end
|
279
|
+
|
280
|
+
# Convert an array into command line parameters.
|
281
|
+
# The array is accepted in the format of Ruby
|
282
|
+
# method arguments --ie. [arg1, arg2, ..., hash]
|
283
|
+
|
284
|
+
def to_console
|
285
|
+
flags = (Hash===last ? pop : {})
|
286
|
+
flags = flags.to_console
|
287
|
+
flags + ' ' + join(" ")
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
class Hash
|
294
|
+
|
295
|
+
# Convert an array into command line parameters.
|
296
|
+
# The array is accepted in the format of Ruby
|
297
|
+
# method arguments --ie. [arg1, arg2, ..., hash]
|
298
|
+
|
299
|
+
def to_console
|
300
|
+
flags = collect do |f,v|
|
301
|
+
m = f.to_s.size == 1 ? '-' : '--'
|
302
|
+
case v
|
303
|
+
when Array
|
304
|
+
v.collect{ |e| "#{m}#{f}='#{e}'" }.join(' ')
|
305
|
+
when true
|
306
|
+
"#{m}#{f}"
|
307
|
+
when false, nil
|
308
|
+
''
|
309
|
+
else
|
310
|
+
"#{m}#{f}='#{v}'"
|
311
|
+
end
|
194
312
|
end
|
313
|
+
flags.join(" ")
|
314
|
+
end
|
195
315
|
|
316
|
+
# Turn a hash into arguments.
|
317
|
+
#
|
318
|
+
# h = { :list => [1,2], :base => "HI" }
|
319
|
+
# h.argumentize #=> [ [], { :list => [1,2], :base => "HI" } ]
|
320
|
+
# h.argumentize(:list) #=> [ [1,2], { :base => "HI" } ]
|
321
|
+
#
|
322
|
+
def argumentize(args_field=nil)
|
323
|
+
config = dup
|
324
|
+
if args_field
|
325
|
+
args = [config.delete(args_field)].flatten.compact
|
326
|
+
else
|
327
|
+
args = []
|
328
|
+
end
|
329
|
+
args << config
|
330
|
+
return args
|
196
331
|
end
|
197
332
|
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
# SCRAP CODE FOR REFERENCE TO POSSIBLE ADD FUTURE FEATURES
|
337
|
+
|
198
338
|
=begin
|
339
|
+
|
199
340
|
# We include a module here so you can define your own help
|
200
341
|
# command and call #super to utilize this one.
|
201
342
|
|
@@ -264,248 +405,12 @@ module Console
|
|
264
405
|
|
265
406
|
include Help
|
266
407
|
extend Help::ClassMethods
|
267
|
-
=end
|
268
|
-
|
269
|
-
|
270
|
-
# = Command::Options
|
271
|
-
#
|
272
|
-
# CommandOptions provides the basis for Command to Object Mapping (COM).
|
273
|
-
# It is an commandline options parser that uses method definitions
|
274
|
-
# as means of interprting command arguments.
|
275
|
-
#
|
276
|
-
# == Synopsis
|
277
|
-
#
|
278
|
-
# Let's make an executable called 'mycmd'.
|
279
|
-
#
|
280
|
-
# #!/usr/bin/env ruby
|
281
|
-
#
|
282
|
-
# require 'facets/command_options'
|
283
|
-
#
|
284
|
-
# class MyOptions < CommandOptions
|
285
|
-
# attr_accessor :file
|
286
|
-
#
|
287
|
-
# def v!
|
288
|
-
# @verbose = true
|
289
|
-
# end
|
290
|
-
# end
|
291
|
-
#
|
292
|
-
# opts = MyOptions.parse("-v --file hello.rb")
|
293
|
-
#
|
294
|
-
# opts.verbose #=> true
|
295
|
-
# opts.file #=> "hello.rb"
|
296
|
-
#
|
297
|
-
#--
|
298
|
-
# == Global Options
|
299
|
-
#
|
300
|
-
# You can define <i>global options</i> which are options that will be
|
301
|
-
# processed no matter where they occur in the command line. In the above
|
302
|
-
# examples only the options occuring before the subcommand are processed
|
303
|
-
# globally. Anything occuring after the subcommand belonds strictly to
|
304
|
-
# the subcommand. For instance, if we had added the following to the above
|
305
|
-
# example:
|
306
|
-
#
|
307
|
-
# global_option :_v
|
308
|
-
#
|
309
|
-
# Then -v could appear anywhere in the command line, even on the end,
|
310
|
-
# and still work as expected.
|
311
|
-
#
|
312
|
-
# % mycmd jump -h 3 -v
|
313
|
-
#++
|
314
|
-
#
|
315
|
-
# == Missing Options
|
316
|
-
#
|
317
|
-
# You can use #option_missing to catch any options that are not explicility
|
318
|
-
# defined.
|
319
|
-
#
|
320
|
-
# The method signature should look like:
|
321
|
-
#
|
322
|
-
# option_missing(option_name, args)
|
323
|
-
#
|
324
|
-
# Example:
|
325
|
-
# def option_missing(option_name, args)
|
326
|
-
# p args if $debug
|
327
|
-
# case option_name
|
328
|
-
# when 'p'
|
329
|
-
# @a = args[0].to_i
|
330
|
-
# @b = args[1].to_i
|
331
|
-
# 2
|
332
|
-
# else
|
333
|
-
# raise InvalidOptionError(option_name, args)
|
334
|
-
# end
|
335
|
-
# end
|
336
|
-
#
|
337
|
-
# Its return value should be the effective "arity" of that options -- that is,
|
338
|
-
# how many arguments it consumed ("-p a b", for example, would consume 2 args:
|
339
|
-
# "a" and "b"). An arity of 1 is assumed if nil or false is returned.
|
340
|
-
|
341
|
-
class Command::Options
|
342
|
-
|
343
|
-
def self.parse(*line_and_options)
|
344
|
-
o = new
|
345
|
-
o.parse(*line_and_options)
|
346
|
-
o
|
347
|
-
end
|
348
|
-
|
349
|
-
def initialize(delegate=nil)
|
350
|
-
@__self__ = delegate || self
|
351
|
-
end
|
352
|
-
|
353
|
-
# Parse line for options in the context self.
|
354
|
-
#
|
355
|
-
# Options:
|
356
|
-
#
|
357
|
-
# :pass => true || false
|
358
|
-
#
|
359
|
-
# Setting this to true prevents the parse_missing routine from running.
|
360
|
-
#
|
361
|
-
# :only => [ global options, ... ]
|
362
|
-
#
|
363
|
-
# When processing global options, we only want to parse selected options.
|
364
|
-
# This also set +pass+ to true.
|
365
|
-
#
|
366
|
-
# :stop => true || false
|
367
|
-
#
|
368
|
-
# If we are parsing options for the *main* command and we are allowing
|
369
|
-
# subcommands, then we want to stop as soon as we get to the first non-option,
|
370
|
-
# because that non-option will be the name of our subcommand and all options that
|
371
|
-
# follow should be parsed later when we handle the subcommand.
|
372
|
-
# This also set +pass+ to true.
|
373
|
-
|
374
|
-
def parse(*line_and_options)
|
375
|
-
__self__ = @__self__
|
376
|
-
|
377
|
-
if Hash === line_and_options.last
|
378
|
-
options = line_and_options.pop
|
379
|
-
line = line_and_options.first
|
380
|
-
else
|
381
|
-
options = {}
|
382
|
-
line = line_and_options.first
|
383
|
-
end
|
384
408
|
|
385
|
-
|
386
|
-
when String
|
387
|
-
argv = Shellwords.shellwords(line)
|
388
|
-
when Array
|
389
|
-
argv = line.dup
|
390
|
-
else
|
391
|
-
argv = ARGV.dup
|
392
|
-
end
|
393
|
-
|
394
|
-
only = options[:only] # only parse these options
|
395
|
-
stop = options[:stop] # stop at first non-option
|
396
|
-
pass = options[:pass] || only || stop # don't run options_missing
|
397
|
-
|
398
|
-
if $debug
|
399
|
-
puts(only ? "\nGlobal parsing..." : "\nParsing...")
|
400
|
-
end
|
401
|
-
|
402
|
-
puts "# line: #{argv.inspect}" if $debug
|
403
|
-
|
404
|
-
# Split single letter option groupings into separate options.
|
405
|
-
# ie. -xyz => -x -y -z
|
406
|
-
argv = argv.collect { |arg|
|
407
|
-
if md = /^-(\w{2,})/.match( arg )
|
408
|
-
md[1].split(//).collect { |c| "-#{c}" }
|
409
|
-
else
|
410
|
-
arg
|
411
|
-
end
|
412
|
-
}.flatten
|
413
|
-
|
414
|
-
index = 0
|
415
|
-
|
416
|
-
until index >= argv.size
|
417
|
-
arg = argv.at(index)
|
418
|
-
break if arg == '--' # POSIX compliance
|
419
|
-
if arg[0,1] == '-'
|
420
|
-
puts "# option: #{arg}" if $debug
|
421
|
-
cnt = (arg[0,2] == '--' ? 2 : 1)
|
422
|
-
#opt = Option.new(arg)
|
423
|
-
#name = opt.methodize
|
424
|
-
name = arg.sub(/^-{1,2}/,'')
|
425
|
-
skip = only && only.include?(name)
|
426
|
-
unam = ('__'*cnt)+name
|
427
|
-
if __self__.respond_to?(unam)
|
428
|
-
puts "# method: #{uname}" if $debug
|
429
|
-
meth = method(unam)
|
430
|
-
arity = meth.arity
|
431
|
-
if arity < 0
|
432
|
-
meth.call(*argv.slice(index+1..-1)) unless skip
|
433
|
-
arity[index..-1] = nil # Get rid of the *name* and values
|
434
|
-
elsif arity == 0
|
435
|
-
meth.call unless skip
|
436
|
-
argv.delete_at(index) # Get rid of the *name* of the option
|
437
|
-
else
|
438
|
-
meth.call(*argv.slice(index+1, arity)) unless skip
|
439
|
-
#argv.delete_at(index) # Get rid of the *name* of the option
|
440
|
-
#arity.times{ argv.delete_at(index) } # Get rid of the *value* of the option
|
441
|
-
arity[index,arity] = nil
|
442
|
-
end
|
443
|
-
elsif __self__.respond_to?(name+'=')
|
444
|
-
puts "# method: #{name}=" if $debug
|
445
|
-
__self__.send(name+'=', *argv.slice(index+1, 1)) unless skip
|
446
|
-
argv.delete_at(index) # Get rid of the *name* of the option
|
447
|
-
argv.delete_at(index) # Get rid of the *value* of the option
|
448
|
-
elsif __self__.respond_to?(name+'!')
|
449
|
-
puts "# method: #{name}!" if $debug
|
450
|
-
__self__.send(name+'!') unless skip
|
451
|
-
argv.delete_at(index) # Get rid of the *name* of the option
|
452
|
-
else
|
453
|
-
index += 1
|
454
|
-
end
|
455
|
-
else
|
456
|
-
index += 1
|
457
|
-
break if stop
|
458
|
-
end
|
459
|
-
end
|
460
|
-
# parse missing ?
|
461
|
-
argv = parse_missing(argv) unless pass
|
462
|
-
# return the remaining argv
|
463
|
-
puts "# return: #{argv.inspect}" if $debug
|
464
|
-
return argv
|
465
|
-
end
|
466
|
-
|
467
|
-
#
|
468
|
-
|
469
|
-
def parse_missing(argv)
|
470
|
-
argv.each_with_index do |a,i|
|
471
|
-
if a =~ /^-/
|
472
|
-
#raise InvalidOptionError.new(a) unless @__self__.respond_to?(:option_missing)
|
473
|
-
kept = @__self__.option_missing(a, *argv[i+1,1])
|
474
|
-
argv.delete_at(i) if kept # delete if value kept
|
475
|
-
argv.delete_at(i) # delete option
|
476
|
-
end
|
477
|
-
end
|
478
|
-
return argv
|
479
|
-
end
|
480
|
-
|
481
|
-
#
|
482
|
-
|
483
|
-
def option_missing(opt, arg=nil)
|
484
|
-
raise InvalidOptionError.new(opt)
|
485
|
-
# #$stderr << "Unknown option '#{arg}'.\n"
|
486
|
-
# #exit -1
|
487
|
-
end
|
488
|
-
|
489
|
-
#
|
490
|
-
|
491
|
-
def to_h
|
492
|
-
opts = @__self__.public_methods(true).select{ |m| m =~ /^[A-Za-z0-9]+[=!]$/ || m =~ /^[_][A-Za-z0-9]+$/ }
|
493
|
-
#opts.reject!{ |k| k =~ /_$/ }
|
494
|
-
opts.collect!{ |m| m.chomp('=').chomp('!') }
|
495
|
-
opts.inject({}) do |h, m|
|
496
|
-
k = m.sub(/^_+/, '')
|
497
|
-
v = @__self__.send(m)
|
498
|
-
h[k] = v if v
|
499
|
-
h
|
500
|
-
end
|
409
|
+
=end
|
501
410
|
|
502
|
-
|
503
|
-
# next h if v == "@__self__"
|
504
|
-
# h[v[1..-1]] = @__self__.instance_variable_get(v); h
|
505
|
-
#end
|
506
|
-
end
|
411
|
+
=begin
|
507
412
|
|
508
|
-
|
413
|
+
# Provides a very basic usage help string.
|
509
414
|
#
|
510
415
|
# TODO Add support for __options.
|
511
416
|
def usage
|
@@ -559,48 +464,6 @@ module Console
|
|
559
464
|
return c
|
560
465
|
end
|
561
466
|
|
562
|
-
# # Single Option
|
563
|
-
#
|
564
|
-
# class Option < String
|
565
|
-
#
|
566
|
-
# def initialize(option)
|
567
|
-
# @flag = option
|
568
|
-
# @long = (/^--/ =~ option)
|
569
|
-
# super(option.sub(/^-{1,2}/,''))
|
570
|
-
# end
|
571
|
-
#
|
572
|
-
# def long?
|
573
|
-
# @long
|
574
|
-
# end
|
575
|
-
#
|
576
|
-
# def short?
|
577
|
-
# !@long
|
578
|
-
# end
|
579
|
-
#
|
580
|
-
# #def demethodize
|
581
|
-
# # sub('__','--').sub('_','-')
|
582
|
-
# #end
|
583
|
-
#
|
584
|
-
# def methodize
|
585
|
-
# @flag.sub(/^-{1,2}/,'')
|
586
|
-
# end
|
587
|
-
#
|
588
|
-
# end
|
589
|
-
|
590
|
-
end
|
591
|
-
|
592
|
-
# For CommandOptions, but defined external to it, so
|
593
|
-
# that it is easy to access from user defined commands.
|
594
|
-
# (This lookup issue should be fixed in Ruby 1.9+, and then
|
595
|
-
# the class can be moved back into Command namespace.)
|
596
|
-
|
597
|
-
class InvalidOptionError < StandardError
|
598
|
-
def initialize(option_name)
|
599
|
-
@option_name = option_name
|
600
|
-
end
|
601
|
-
def message
|
602
|
-
"Unknown option '#{@option_name}'."
|
603
|
-
end
|
604
467
|
end
|
605
468
|
|
606
|
-
end
|
469
|
+
=end
|