vcs 0.1 → 0.2.148
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/Rakefile +17 -3
- data/bin/vcs +57 -34
- data/doc/jamis.rb +564 -0
- data/ruby_ex/abstract.rb +254 -0
- data/ruby_ex/abstract_node.rb +85 -0
- data/ruby_ex/algorithms/simulated_annealing.rb +140 -0
- data/ruby_ex/array_each_pair.rb +18 -0
- data/ruby_ex/ask.rb +101 -0
- data/ruby_ex/attributed_class.rb +302 -0
- data/ruby_ex/cache.rb +373 -0
- data/ruby_ex/checkout.rb +12 -0
- data/ruby_ex/choose.rb +271 -0
- data/ruby_ex/commands.rb +18 -0
- data/ruby_ex/commands/command.rb +401 -0
- data/ruby_ex/commands/datas.rb +16 -0
- data/ruby_ex/commands/datas/data.rb +33 -0
- data/ruby_ex/commands/datas/factory.rb +66 -0
- data/ruby_ex/commands/factory.rb +66 -0
- data/ruby_ex/commands/helpers.rb +67 -0
- data/ruby_ex/commands/pipe.rb +64 -0
- data/ruby_ex/commands/runners.rb +17 -0
- data/ruby_ex/commands/runners/exec.rb +49 -0
- data/ruby_ex/commands/runners/fork.rb +97 -0
- data/ruby_ex/commands/runners/runner.rb +107 -0
- data/ruby_ex/commands/seq.rb +27 -0
- data/ruby_ex/config_file.rb +96 -0
- data/ruby_ex/const_regexp.rb +59 -0
- data/ruby_ex/daemon.rb +134 -0
- data/ruby_ex/diff.rb +667 -0
- data/ruby_ex/dlogger.rb +62 -0
- data/ruby_ex/drb/dispatcher.rb +252 -0
- data/ruby_ex/drb/dispatcher_server_test.rb +29 -0
- data/ruby_ex/drb/drb_observable.rb +97 -0
- data/ruby_ex/drb/drb_observable_pool.rb +27 -0
- data/ruby_ex/drb/drb_service.rb +43 -0
- data/ruby_ex/drb/drb_undumped_attributes.rb +55 -0
- data/ruby_ex/drb/drb_undumped_indexed_object.rb +54 -0
- data/ruby_ex/drb/insecure_protected_methods.rb +103 -0
- data/ruby_ex/drb/session_client_test.rb +40 -0
- data/ruby_ex/drb/session_manager.rb +246 -0
- data/ruby_ex/drb/session_server.rb +53 -0
- data/ruby_ex/dtime.rb +143 -0
- data/ruby_ex/dumpable_proc.rb +63 -0
- data/ruby_ex/exception.rb +32 -0
- data/ruby_ex/filetype.rb +229 -0
- data/ruby_ex/fileutils_ex.rb +44 -0
- data/ruby_ex/fold.rb +58 -0
- data/ruby_ex/generate_id.rb +44 -0
- data/ruby_ex/hookable.rb +262 -0
- data/ruby_ex/hooker.rb +54 -0
- data/ruby_ex/inactive_timeout.rb +137 -0
- data/ruby_ex/indexed_node.rb +66 -0
- data/ruby_ex/io_marshal.rb +100 -0
- data/ruby_ex/ioo.rb +194 -0
- data/ruby_ex/labeled_node.rb +63 -0
- data/ruby_ex/logger_observer.rb +23 -0
- data/ruby_ex/md5sum.rb +66 -0
- data/ruby_ex/mktemp.rb +208 -0
- data/ruby_ex/module/attr_once.rb +36 -0
- data/ruby_ex/module/autoload_tree.rb +75 -0
- data/ruby_ex/module/hierarchy.rb +335 -0
- data/ruby_ex/module/instance_method_visibility.rb +73 -0
- data/ruby_ex/module_ex.rb +11 -0
- data/ruby_ex/node.rb +80 -0
- data/ruby_ex/object_monitor.rb +145 -0
- data/ruby_ex/object_monitor_activity.rb +33 -0
- data/ruby_ex/observable.rb +140 -0
- data/ruby_ex/observable_pool.rb +293 -0
- data/ruby_ex/orderedhash.rb +252 -0
- data/ruby_ex/pathname_ex.rb +152 -0
- data/ruby_ex/pp_hierarchy.rb +29 -0
- data/ruby_ex/pseudo_cache.rb +190 -0
- data/ruby_ex/queue.rb +56 -0
- data/ruby_ex/random_generators.rb +25 -0
- data/ruby_ex/random_generators/random_generator.rb +31 -0
- data/ruby_ex/random_generators/ruby.rb +23 -0
- data/ruby_ex/safe_eval.rb +348 -0
- data/ruby_ex/sendmail.rb +215 -0
- data/ruby_ex/service_manager.rb +121 -0
- data/ruby_ex/session/administrable.rb +120 -0
- data/ruby_ex/session/client.rb +153 -0
- data/ruby_ex/session/const.rb +18 -0
- data/ruby_ex/session/dispatcher.rb +184 -0
- data/ruby_ex/session/error.rb +21 -0
- data/ruby_ex/session/fetchable.rb +57 -0
- data/ruby_ex/session/fetcher.rb +62 -0
- data/ruby_ex/session/hookable.rb +26 -0
- data/ruby_ex/session/profile.rb +110 -0
- data/ruby_ex/session/server.rb +582 -0
- data/ruby_ex/session/test/administrable_test.rb +337 -0
- data/ruby_ex/session/test/basic_test.rb +523 -0
- data/ruby_ex/session/test/dispatcher_test.rb +409 -0
- data/ruby_ex/session/test/fetchable_test.rb +119 -0
- data/ruby_ex/session/test/sub_server_test.rb +188 -0
- data/ruby_ex/shuffle.rb +30 -0
- data/ruby_ex/spring.rb +136 -0
- data/ruby_ex/spring_set.rb +137 -0
- data/ruby_ex/string_ex.rb +28 -0
- data/ruby_ex/symtbl.rb +106 -0
- data/ruby_ex/synflow.rb +474 -0
- data/ruby_ex/test/unit/ui/yaml/testrunner.rb +164 -0
- data/ruby_ex/thread_mutex.rb +10 -0
- data/ruby_ex/timeout_ex.rb +81 -0
- data/ruby_ex/top_down.rb +73 -0
- data/ruby_ex/trace.rb +26 -0
- data/ruby_ex/uri/druby.rb +81 -0
- data/ruby_ex/uri/file.rb +65 -0
- data/ruby_ex/uri/ftp_ex.rb +37 -0
- data/ruby_ex/uri/http_ex.rb +43 -0
- data/ruby_ex/uri/ssh.rb +92 -0
- data/ruby_ex/uri/svn.rb +118 -0
- data/ruby_ex/uri_ex.rb +45 -0
- data/ruby_ex/verbose_object.rb +30 -0
- data/ruby_ex/version.rb +66 -0
- data/ruby_ex/yaml/basenode_ext.rb +63 -0
- data/ruby_ex/yaml/chop_header.rb +23 -0
- data/ruby_ex/yaml/transform.rb +449 -0
- data/ruby_ex/yaml/yregexpath.rb +76 -0
- data/src/changelog.rb +28 -18
- data/src/conflict.rb +20 -0
- data/src/diff.rb +18 -0
- data/src/diffstat.rb +9 -3
- data/src/last_changed_date.rb +18 -0
- data/src/mail.rb +33 -65
- data/src/message.rb +15 -9
- data/src/mycommit.rb +29 -14
- data/src/news.rb +24 -3
- data/src/status.rb +17 -0
- data/src/svn.rb +2 -2
- data/src/vcs.rb +24 -3
- metadata +124 -5
- data/lrdetools.rb +0 -12
data/ruby_ex/checkout.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
|
|
2
|
+
# Author: Nicolas Despres <polrop@lrde.epita.fr>.
|
|
3
|
+
# License: Gnu General Public License.
|
|
4
|
+
|
|
5
|
+
# $LastChangedBy: polrop $
|
|
6
|
+
# $Id: checkout.rb 159 2005-02-18 12:07:23Z polrop $
|
|
7
|
+
|
|
8
|
+
require 'uri_ex'
|
|
9
|
+
require 'uri/http_ex'
|
|
10
|
+
require 'uri/ftp_ex'
|
|
11
|
+
require 'uri/file'
|
|
12
|
+
require 'uri/svn'
|
data/ruby_ex/choose.rb
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
|
+
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
|
+
# License:: GNU General Public License (GPL).
|
|
4
|
+
# Revision:: $Id: choose.rb 219 2005-05-09 12:26:29Z ertai $
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'random_generators'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Integer
|
|
11
|
+
|
|
12
|
+
def self.choose ( limit=nil, generator=nil )
|
|
13
|
+
limit.to_i.choose(generator)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def choose ( generator=nil )
|
|
17
|
+
(generator || RandomGenerators.default).choose_integer(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end # class Integer
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Float
|
|
25
|
+
|
|
26
|
+
def self.choose ( limit=nil, generator=nil )
|
|
27
|
+
(limit || 1.0).to_f.choose(generator)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def choose ( generator=nil )
|
|
31
|
+
(generator || RandomGenerators.default).choose_float(self)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end # class Float
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Hash
|
|
39
|
+
|
|
40
|
+
def choose ( limit=nil, generator=nil )
|
|
41
|
+
self[keys.choose(limit, generator)]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end # class Hash
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Array
|
|
49
|
+
|
|
50
|
+
def choose ( limit=nil, generator=nil )
|
|
51
|
+
n = size
|
|
52
|
+
|
|
53
|
+
# Out of limits: limit > size (FIXME warn)
|
|
54
|
+
limit = n if limit.nil? or limit > n
|
|
55
|
+
|
|
56
|
+
self[limit.choose(generator)]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end # class Array
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
module Enumerable
|
|
64
|
+
|
|
65
|
+
def choose ( limit=nil, generator=nil )
|
|
66
|
+
to_a.choose(limit, generator)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end # module Enumerable
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Range
|
|
74
|
+
|
|
75
|
+
def choose ( generator=nil )
|
|
76
|
+
case first
|
|
77
|
+
when Integer
|
|
78
|
+
first + (last + 1 - first).choose(generator)
|
|
79
|
+
else
|
|
80
|
+
super(nil, generator)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end # class Array
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if $0 == __FILE__
|
|
89
|
+
|
|
90
|
+
class MockRandomGenerator < RandomGenerators::RandomGenerator
|
|
91
|
+
concrete
|
|
92
|
+
attr_reader :ref
|
|
93
|
+
def initialize ( tab_i, tab_f )
|
|
94
|
+
raise unless tab_i.is_a? Array
|
|
95
|
+
raise unless tab_f.is_a? Array
|
|
96
|
+
@ref, @ref_f, @pos = tab_i, tab_f, -1
|
|
97
|
+
end
|
|
98
|
+
def get ( tab )
|
|
99
|
+
tab[@pos = (@pos + 1) % tab.size]
|
|
100
|
+
end
|
|
101
|
+
def choose_integer ( n )
|
|
102
|
+
check_choose_integer(n)
|
|
103
|
+
get(@ref) % n
|
|
104
|
+
end
|
|
105
|
+
def choose_float ( f=1.0 )
|
|
106
|
+
check_choose_float(f)
|
|
107
|
+
get(@ref_f) * f
|
|
108
|
+
end
|
|
109
|
+
end # class MockRandomGenerator
|
|
110
|
+
|
|
111
|
+
class RandomMockRandomGenerator < MockRandomGenerator
|
|
112
|
+
def initialize ( n )
|
|
113
|
+
tab_i = (0 .. n - 1).to_a.shuffle
|
|
114
|
+
tab_f = []
|
|
115
|
+
n.times { tab_f << Float.choose() }
|
|
116
|
+
super(tab_i, tab_f)
|
|
117
|
+
end
|
|
118
|
+
end # class RandomMockRandomGenerator
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
require 'test/unit'
|
|
122
|
+
require 'set'
|
|
123
|
+
|
|
124
|
+
class ChooseTest < Test::Unit::TestCase
|
|
125
|
+
|
|
126
|
+
REF_5 = [1, 2, 4, 0, 3]
|
|
127
|
+
REF_F_5 = [0.187528345035389, 0.368857819121331, 0.06954449811019,
|
|
128
|
+
0.188887464581057, 0.213757352204993]
|
|
129
|
+
REF_50 = [
|
|
130
|
+
28, 12, 17, 10, 1, 37, 46, 25, 47, 2, 45, 36, 20, 5, 30, 14, 18, 39,
|
|
131
|
+
8, 15, 16, 43, 27, 33, 22, 7, 32, 3, 35, 9, 19, 13, 21, 6, 23, 26, 4,
|
|
132
|
+
40, 44, 11, 49, 29, 42, 24, 31, 48, 34, 0, 38, 41
|
|
133
|
+
]
|
|
134
|
+
REF_F_50 = [
|
|
135
|
+
0.643785412423313, 0.765736325876787, 0.604515956481919,
|
|
136
|
+
0.133839202811942, 0.350400971015915, 0.966710213804618,
|
|
137
|
+
0.904755113180727, 0.88459848286584, 0.163951952941716,
|
|
138
|
+
0.808023529592901, 0.387806572020054, 0.659689131658524,
|
|
139
|
+
0.289974238490686, 0.813535830006003, 0.565311754588038,
|
|
140
|
+
0.889200099511072, 0.0668127918615937, 0.992091996129602,
|
|
141
|
+
0.604403987759724, 0.89254002738744, 0.601499371463433,
|
|
142
|
+
0.754795648856089, 0.751190354581922, 0.434389552799985,
|
|
143
|
+
0.761369747575372, 0.174903159961104, 0.940325234550983,
|
|
144
|
+
0.536097376374528, 0.957906004507095, 0.693120914744213,
|
|
145
|
+
0.23448576242663, 0.638284107437357, 0.423910334473476,
|
|
146
|
+
0.251349127152935, 0.457931924844161, 0.20579389645718,
|
|
147
|
+
0.256057573715225, 0.584466924890876, 0.609375598141924,
|
|
148
|
+
0.0856487881392241, 0.437958877766505, 0.432312048738822,
|
|
149
|
+
0.81560374982655, 0.142059376928955, 0.118824432371184,
|
|
150
|
+
0.958816710859537, 0.69899930502288, 0.118259542621672,
|
|
151
|
+
0.52218786906451, 0.339233995880932
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
def checker_n ( n, ref_i, ref_f, inp, ref, lim=nil )
|
|
155
|
+
mrg = MockRandomGenerator.new(ref_i, ref_f)
|
|
156
|
+
my = []
|
|
157
|
+
if lim == :no
|
|
158
|
+
n.times { my << inp.choose(mrg) }
|
|
159
|
+
else
|
|
160
|
+
n.times { my << inp.choose(lim, mrg) }
|
|
161
|
+
end
|
|
162
|
+
assert_equal(ref, my)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def checker_5 ( inp, ref, lim=nil )
|
|
166
|
+
checker_n(5, REF_5, REF_F_5, inp, ref, lim)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def checker_50 ( inp, ref, lim=nil )
|
|
170
|
+
checker_n(50, REF_50, REF_F_50, inp, ref, lim)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_integer
|
|
177
|
+
checker_5 Integer, REF_5, 5
|
|
178
|
+
checker_50 Integer, REF_50, 50
|
|
179
|
+
assert(Integer.choose(1000) < 1000)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def test_float
|
|
183
|
+
checker_5 Float, REF_F_5, 1.0
|
|
184
|
+
checker_50 Float, REF_F_50, 1.0
|
|
185
|
+
assert(Float.choose(2.0) < 2)
|
|
186
|
+
assert(Float.choose < 1)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def test_array_5
|
|
190
|
+
checker_5 [4], [4] * 5, 5
|
|
191
|
+
checker_5 [4, 5], [5, 4, 4, 4, 5], 5
|
|
192
|
+
checker_5 [4, 5, 6], [5, 6, 5, 4, 4], 5
|
|
193
|
+
checker_5 [2, 1, 0, 5, 4, 3], [1, 0, 4, 2, 5], 5
|
|
194
|
+
10.times { assert(REF_50.include?(REF_50.choose)) }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def test_set_5
|
|
198
|
+
checker_5 Set[4], [4] * 5, 5
|
|
199
|
+
checker_5 Set[4, 5], [4, 5, 5, 5, 4], 5
|
|
200
|
+
checker_5 Set[4, 5, 6], [6, 4, 6, 5, 5], 5
|
|
201
|
+
checker_5 Set[2, 1, 0, 5, 4, 3], [0, 1, 3, 5, 2], 5
|
|
202
|
+
set = REF_50.to_set
|
|
203
|
+
10.times { assert(set.include?(set.choose)) }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_hash
|
|
207
|
+
checker_5({ 1 => 2 }, [2] * 5, 5)
|
|
208
|
+
checker_5({ 1 => 2, 3 => 4 }, [4, 2, 2, 2, 4], 5)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def test_array_50
|
|
212
|
+
checker_50 [4], [4] * 50, 50
|
|
213
|
+
checker_50 [
|
|
214
|
+
36, 16, 15, 6, 27, 33, 17, 10, 18, 39, 8, 43, 19, 23, 49, 40, 46, 37,
|
|
215
|
+
9, 41, 35, 0, 38, 26, 22, 4, 7, 25, 21, 12, 42, 45, 28, 5, 31, 24, 34,
|
|
216
|
+
48, 32, 30, 13, 29, 11, 2, 1, 20, 3, 44, 47, 14
|
|
217
|
+
], [
|
|
218
|
+
21, 19, 37, 8, 16, 48, 3, 4, 44, 15, 20, 34, 35, 33, 42, 49, 9, 30,
|
|
219
|
+
18, 40, 46, 2, 25, 5, 38, 10, 28, 6, 24, 39, 41, 23, 0, 17, 26, 7, 27,
|
|
220
|
+
13, 1, 43, 14, 12, 11, 22, 45, 47, 31, 36, 32, 29
|
|
221
|
+
], 50
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def test_integer_instances
|
|
225
|
+
assert_raise(ArgumentError) { checker_5 0, [], :no }
|
|
226
|
+
checker_5 1, [0] * 5, :no
|
|
227
|
+
checker_5 4, [1, 2, 0, 0, 3], :no
|
|
228
|
+
checker_5 10, REF_5, :no
|
|
229
|
+
assert(1000.choose < 1000)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def test_float_instances
|
|
233
|
+
assert_raise(ArgumentError) { checker_5 0.0, [], :no }
|
|
234
|
+
checker_5 1.0, [ 0.187528345035389, 0.368857819121331, 0.06954449811019,
|
|
235
|
+
0.188887464581057, 0.213757352204993 ], :no
|
|
236
|
+
assert(2.0.choose < 2)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def test_integer_range_instances
|
|
240
|
+
checker_5 1..1, [1] * 5, :no
|
|
241
|
+
checker_5 1..2, [2, 1, 1, 1, 2], :no
|
|
242
|
+
checker_5 0..1, [1, 0, 0, 0, 1], :no
|
|
243
|
+
checker_5 0..4, [1, 2, 4, 0, 3], :no
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
class Xs
|
|
247
|
+
include Comparable
|
|
248
|
+
attr_reader :length
|
|
249
|
+
def initialize ( n )
|
|
250
|
+
@length = n
|
|
251
|
+
end
|
|
252
|
+
def succ
|
|
253
|
+
Xs.new(@length.succ)
|
|
254
|
+
end
|
|
255
|
+
def <=> ( rhs )
|
|
256
|
+
@length <=> rhs.length
|
|
257
|
+
end
|
|
258
|
+
def inspect
|
|
259
|
+
'x' * @length
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def test_range_instances
|
|
264
|
+
checker_5 'a'..'a', ['a'] * 5, :no
|
|
265
|
+
checker_5 'a'..'b', ['b', 'a', 'a', 'a', 'b'], :no
|
|
266
|
+
checker_5 Xs.new(1)..Xs.new(4), [2, 3, 1, 1, 4].map { |x| Xs.new(x) }, :no
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
end # class ChooseTest
|
|
270
|
+
|
|
271
|
+
end
|
data/ruby_ex/commands.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
|
+
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
|
+
# License:: GNU General Public License (GPL).
|
|
4
|
+
# Revision:: $Id: commands.rb 221 2005-05-09 12:40:57Z ertai $
|
|
5
|
+
|
|
6
|
+
require 'module/autoload_tree'
|
|
7
|
+
require 'pathname_ex'
|
|
8
|
+
require 'abstract'
|
|
9
|
+
require 'commands/helpers'
|
|
10
|
+
require 'mktemp'
|
|
11
|
+
require 'commands/runners'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module Commands
|
|
15
|
+
|
|
16
|
+
autoloaded_module(__FILE__)
|
|
17
|
+
|
|
18
|
+
end # module Commands
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
|
+
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
|
+
# License:: GNU General Public License (GPL).
|
|
4
|
+
# Revision:: $Id: command.rb 221 2005-05-09 12:40:57Z ertai $
|
|
5
|
+
|
|
6
|
+
module Commands
|
|
7
|
+
|
|
8
|
+
class Command
|
|
9
|
+
|
|
10
|
+
attr_accessor :command
|
|
11
|
+
attr_accessor :dir
|
|
12
|
+
attr_accessor :input
|
|
13
|
+
attr_accessor :output
|
|
14
|
+
attr_accessor :error
|
|
15
|
+
attr_accessor :arg_list
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Construction methods.
|
|
19
|
+
#
|
|
20
|
+
|
|
21
|
+
def initialize ( command_name, *args )
|
|
22
|
+
@command = command_name
|
|
23
|
+
@arg_list = args.dup
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# Command['wc -l'].sh
|
|
28
|
+
#
|
|
29
|
+
def self.[] ( *args )
|
|
30
|
+
new(*args)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Running methods.
|
|
37
|
+
#
|
|
38
|
+
|
|
39
|
+
# Simulate Kernel#system() to run the command (No shell expansion).
|
|
40
|
+
def system
|
|
41
|
+
unless defined? @@fork_runner
|
|
42
|
+
@@fork_runner = Runners::Fork.new
|
|
43
|
+
end
|
|
44
|
+
run(@@fork_runner)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Use the ExecRunner.
|
|
49
|
+
def exec
|
|
50
|
+
unless defined? @@exec_runner
|
|
51
|
+
@@exec_runner = Runners::Exec.new
|
|
52
|
+
end
|
|
53
|
+
run(@@exec_runner)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Use Kernel#system() but with a string to have the shell expansion.
|
|
58
|
+
def sh
|
|
59
|
+
Kernel.system(to_sh)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Use Kernel#exec() but with a string to have the shell expansion.
|
|
64
|
+
def sh!
|
|
65
|
+
Kernel.exec(to_sh)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Use Kernel#` and return the resulting string.
|
|
70
|
+
def expand
|
|
71
|
+
`#{to_sh}`
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# FIXME design me!
|
|
76
|
+
def ruby
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Use a Commands::Runners::Runner and return a Commands::Datas::Data.
|
|
81
|
+
def run ( runner=@runner )
|
|
82
|
+
runner.run(self)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
#
|
|
88
|
+
# Operators.
|
|
89
|
+
#
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Pipe two commands.
|
|
93
|
+
def | ( rhs )
|
|
94
|
+
Pipe.new(self, rhs)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Chain two commands (like a `;' in shell), and return a new one.
|
|
99
|
+
def + ( rhs )
|
|
100
|
+
Seq.new(self, rhs)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Return a new command with the given command input.
|
|
105
|
+
def < ( arg )
|
|
106
|
+
cmd = dup
|
|
107
|
+
cmd.input = arg
|
|
108
|
+
cmd
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# Return a new command with the given command output.
|
|
113
|
+
def > ( arg )
|
|
114
|
+
cmd = dup
|
|
115
|
+
cmd.output = arg.dup
|
|
116
|
+
cmd.output.open_mode = 'w' if cmd.output.is_a? Pathname
|
|
117
|
+
cmd
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# Like > but open the output in append mode.
|
|
122
|
+
def >> ( arg )
|
|
123
|
+
cmd = dup
|
|
124
|
+
cmd.output = arg.dup
|
|
125
|
+
cmd.output.open_mode = 'a' if cmd.output.is_a? Pathname
|
|
126
|
+
cmd
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# Add an argument to a command.
|
|
131
|
+
# cmd = Command.new('ls')
|
|
132
|
+
# cmd << '-la'
|
|
133
|
+
# cmd.sh
|
|
134
|
+
def << ( arg )
|
|
135
|
+
@arg_list << arg
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
#
|
|
141
|
+
# Misc
|
|
142
|
+
#
|
|
143
|
+
|
|
144
|
+
def arg_list
|
|
145
|
+
@arg_list.dup
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def arg_string
|
|
150
|
+
@arg_list.map { |arg| arg.dump }.join(' ').freeze
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
#
|
|
155
|
+
# Conversion methods
|
|
156
|
+
#
|
|
157
|
+
|
|
158
|
+
def to_s
|
|
159
|
+
str = @command.dump
|
|
160
|
+
arg_str = arg_string
|
|
161
|
+
str += ' ' + arg_str unless arg_str.empty?
|
|
162
|
+
str
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def to_sh
|
|
167
|
+
"#{to_s}#{sh_args}"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def sh_args
|
|
172
|
+
args = ''
|
|
173
|
+
args += " < #{@input.to_s.dump}" if @input
|
|
174
|
+
args += " > #{@output.to_s.dump}" if @output
|
|
175
|
+
args += " 2> #{@error.to_s.dump}" if @error
|
|
176
|
+
args
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def to_a
|
|
181
|
+
[@command, *@arg_list].freeze
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end # class Command
|
|
185
|
+
|
|
186
|
+
if $0 == __FILE__
|
|
187
|
+
require 'test/unit'
|
|
188
|
+
|
|
189
|
+
class CommandTest < Test::Unit::TestCase
|
|
190
|
+
|
|
191
|
+
def setup
|
|
192
|
+
@file = TempPath.new
|
|
193
|
+
@file.open('w') { |f| f.puts "bla bla\n foo bar"}
|
|
194
|
+
ENV['TEST_FILE'] = @file.to_s
|
|
195
|
+
@touch = Command.new('touch')
|
|
196
|
+
@cp = Command.new('cp')
|
|
197
|
+
@cat = Command.new('cat')
|
|
198
|
+
@wc = Command.new('wc')
|
|
199
|
+
@dne = Command.new('ThisCommandDoesNotExist')
|
|
200
|
+
@dev_null = Pathname.new('/dev/null')
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_0_initialize
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_self_hook # test_self.[]
|
|
207
|
+
@cat << 'foo' << 'bar'
|
|
208
|
+
assert_equal(@cat.to_a, Command[*%w[cat foo bar]].to_a)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def test_system
|
|
212
|
+
TempPath.new do |tmp|
|
|
213
|
+
@touch << tmp.to_s
|
|
214
|
+
@touch.system
|
|
215
|
+
assert_equal(0, $?)
|
|
216
|
+
assert(tmp.exist?)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def test_sh_and_pipe
|
|
221
|
+
TempPath.new do |tmp|
|
|
222
|
+
((@cat | @wc) < @file > tmp).sh
|
|
223
|
+
assert_equal(0, $?)
|
|
224
|
+
assert(tmp.exist?)
|
|
225
|
+
assert_match(/\s*2\s*4\s*17\n/, tmp.read)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def test_system_and_pipe
|
|
230
|
+
TempPath.new do |tmp|
|
|
231
|
+
((@cat | @wc) < @file.open('r+') > tmp).system
|
|
232
|
+
assert_equal(0, $?)
|
|
233
|
+
assert(tmp.exist?)
|
|
234
|
+
assert_match(/\s*2\s*4\s*17\n/, tmp.read)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def test_system_is_not_sh
|
|
239
|
+
TempPath.new do |tmp|
|
|
240
|
+
@cat << '$TEST_FILE'
|
|
241
|
+
@cat.error = @dev_null
|
|
242
|
+
(@cat > tmp).system
|
|
243
|
+
assert_not_equal(0, $?)
|
|
244
|
+
assert(!tmp.exist? || tmp.zero?)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def test_exec
|
|
249
|
+
TempPath.new do |tmp|
|
|
250
|
+
@cp << @file << tmp
|
|
251
|
+
pid = fork do
|
|
252
|
+
@cp.exec
|
|
253
|
+
end
|
|
254
|
+
Process.waitpid pid
|
|
255
|
+
assert_equal(0, $?)
|
|
256
|
+
assert(tmp.exist?)
|
|
257
|
+
assert_equal(@file.read, tmp.read)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def test_exec_not_sh!
|
|
262
|
+
TempPath.new do |tmp|
|
|
263
|
+
@cat << '$TEST_FILE'
|
|
264
|
+
@cat.error = @dev_null
|
|
265
|
+
pid = fork do
|
|
266
|
+
(@cat > tmp).exec
|
|
267
|
+
end
|
|
268
|
+
Process.waitpid pid
|
|
269
|
+
assert_not_equal(0, $?)
|
|
270
|
+
assert(!tmp.exist? || tmp.size.zero?)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def test_sh
|
|
275
|
+
TempPath.new do |tmp|
|
|
276
|
+
@cat << '$TEST_FILE'
|
|
277
|
+
(@cat > tmp).sh
|
|
278
|
+
assert_equal(0, $?)
|
|
279
|
+
assert(tmp.exist?)
|
|
280
|
+
assert_equal(@file.read, tmp.read)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
TempPath.new do |tmp|
|
|
284
|
+
((@cat | @wc) < @file > tmp).sh
|
|
285
|
+
assert_equal(0, $?)
|
|
286
|
+
assert(tmp.exist?)
|
|
287
|
+
assert_match(/\s*2\s*4\s*17\n/, tmp.read)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def test_sh!
|
|
292
|
+
TempPath.new do |tmp|
|
|
293
|
+
@cat << '$TEST_FILE'
|
|
294
|
+
pid = fork do
|
|
295
|
+
(@cat > tmp).sh!
|
|
296
|
+
end
|
|
297
|
+
Process.waitpid pid
|
|
298
|
+
assert_equal(0, $?)
|
|
299
|
+
assert(tmp.exist?)
|
|
300
|
+
assert_equal(@file.read, tmp.read)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def test_expand
|
|
305
|
+
@cat << '$TEST_FILE'
|
|
306
|
+
str = (@cat).expand
|
|
307
|
+
assert_equal(0, $?)
|
|
308
|
+
assert_equal(@file.read, str)
|
|
309
|
+
|
|
310
|
+
str = ((@cat | @wc) < @file).expand
|
|
311
|
+
assert_equal(0, $?)
|
|
312
|
+
assert_match(/\s*2\s*4\s*17\n/, str)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def test_ruby
|
|
316
|
+
# FIXME
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def test_pipe # test_|
|
|
320
|
+
assert_kind_of(Pipe, (@cat | @wc) < @file)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def test_chain # test_+
|
|
324
|
+
assert_kind_of(Seq, (@cat + @wc) < @file)
|
|
325
|
+
TempPath.new do |tmp|
|
|
326
|
+
TempPath.new do |tmp2|
|
|
327
|
+
((@cat < @file > tmp) + (@wc < tmp > tmp2)).sh
|
|
328
|
+
assert_equal(0, $?)
|
|
329
|
+
assert(tmp.exist?)
|
|
330
|
+
assert(tmp2.exist?)
|
|
331
|
+
assert_equal(@file.read, tmp.read)
|
|
332
|
+
assert_match(/\s*2\s*4\s*17\n/, tmp2.read)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def test_add_input # test_<
|
|
338
|
+
@cat.input = 'foo'
|
|
339
|
+
assert_equal('foo', @cat.input)
|
|
340
|
+
cmd = @cat < 'bar'
|
|
341
|
+
assert_equal('bar', cmd.input)
|
|
342
|
+
assert_not_equal(cmd.object_id, @bar.object_id)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def test_add_output # test_>
|
|
346
|
+
@cat.output = 'foo'
|
|
347
|
+
assert_equal('foo', @cat.output)
|
|
348
|
+
cmd = @cat > 'bar'
|
|
349
|
+
assert_equal('bar', cmd.output)
|
|
350
|
+
assert_not_equal(cmd.object_id, @bar.object_id)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def test_append_output # test_>>
|
|
354
|
+
@cat.output = 'foo'
|
|
355
|
+
assert_equal('foo', @cat.output)
|
|
356
|
+
cmd = @cat >> 'bar'
|
|
357
|
+
assert_equal('bar', cmd.output)
|
|
358
|
+
assert_not_equal(cmd.object_id, @bar.object_id)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def test_arg_list
|
|
362
|
+
assert_equal([], @cp.arg_list)
|
|
363
|
+
@cat << 'foo bar' << 'baz'
|
|
364
|
+
assert_equal(['foo bar', 'baz'], @cat.arg_list)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def test_arg_string
|
|
368
|
+
assert_equal('', @cp.arg_string)
|
|
369
|
+
@cat << 'foo bar' << 'baz'
|
|
370
|
+
assert_equal('"foo bar" "baz"', @cat.arg_string)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def test_to_s
|
|
374
|
+
assert_equal('"cp"', @cp.to_s)
|
|
375
|
+
@cat << 'foo bar' << 'baz'
|
|
376
|
+
assert_equal('"cat" "foo bar" "baz"', @cat.to_s)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def test_to_sh
|
|
380
|
+
assert_equal('"cp" > "ya"', (@cp > 'ya').to_sh)
|
|
381
|
+
@cat << 'foo > bar' << '< baz'
|
|
382
|
+
assert_equal(
|
|
383
|
+
'(("cat" "foo > bar" "< baz" < "yay") | ("cp" > "yoy"))',
|
|
384
|
+
((@cat | @cp) < 'yay' > 'yoy').to_sh)
|
|
385
|
+
@wc.error = 'foo'
|
|
386
|
+
assert_equal('"wc" 2> "foo"', @wc.to_sh)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def test_complex
|
|
390
|
+
TempPath.new do |tmp|
|
|
391
|
+
ls = 'ls'.to_cmd
|
|
392
|
+
wc = 'wc'.to_cmd
|
|
393
|
+
(ls | wc) < @file > tmp
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
end # class CommandTest
|
|
398
|
+
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
end # module Commands
|