linkparser 1.0.4 → 1.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.tar.gz.sig +0 -0
- data/ChangeLog +134 -506
- data/LICENSE +1 -1
- data/README.md +95 -0
- data/Rakefile +145 -95
- data/Rakefile.local +31 -39
- data/ext/dictionary.c +103 -37
- data/ext/extconf.rb +79 -32
- data/ext/linkage.c +245 -210
- data/ext/linkparser.c +3 -2
- data/ext/linkparser.h +21 -17
- data/ext/parseoptions.c +65 -65
- data/ext/sentence.c +75 -64
- data/lib/linkparser.rb +16 -26
- data/lib/linkparser/linkage.rb +68 -50
- data/lib/linkparser/mixins.rb +38 -0
- data/lib/linkparser/sentence.rb +15 -40
- data/rake/dependencies.rb +1 -1
- data/rake/documentation.rb +123 -0
- data/rake/helpers.rb +400 -310
- data/rake/hg.rb +318 -0
- data/rake/manual.rb +84 -79
- data/rake/packaging.rb +33 -37
- data/rake/publishing.rb +166 -146
- data/rake/style.rb +1 -1
- data/rake/svn.rb +577 -549
- data/rake/testing.rb +55 -106
- data/spec/bugfixes_spec.rb +12 -6
- data/spec/linkparser/dictionary_spec.rb +45 -29
- data/spec/linkparser/linkage_spec.rb +90 -94
- data/spec/linkparser/mixins_spec.rb +67 -0
- data/spec/linkparser/parseoptions_spec.rb +19 -22
- data/spec/linkparser/sentence_spec.rb +19 -17
- data/spec/linkparser_spec.rb +11 -5
- metadata +64 -147
- metadata.gz.sig +0 -0
- data/README +0 -61
- data/rake/rdoc.rb +0 -40
- data/rake/win32.rb +0 -186
data/rake/helpers.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
2
3
|
#####################################################################
|
3
4
|
### G L O B A L H E L P E R F U N C T I O N S
|
4
5
|
#####################################################################
|
@@ -6,7 +7,6 @@
|
|
6
7
|
|
7
8
|
require 'pathname'
|
8
9
|
require 'uri'
|
9
|
-
require 'open3'
|
10
10
|
|
11
11
|
begin
|
12
12
|
require 'readline'
|
@@ -19,394 +19,484 @@ rescue LoadError
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
'black' => 30, 'on_black' => 40,
|
36
|
-
'red' => 31, 'on_red' => 41,
|
37
|
-
'green' => 32, 'on_green' => 42,
|
38
|
-
'yellow' => 33, 'on_yellow' => 43,
|
39
|
-
'blue' => 34, 'on_blue' => 44,
|
40
|
-
'magenta' => 35, 'on_magenta' => 45,
|
41
|
-
'cyan' => 36, 'on_cyan' => 46,
|
42
|
-
'white' => 37, 'on_white' => 47
|
43
|
-
}
|
44
|
-
|
45
|
-
|
46
|
-
MULTILINE_PROMPT = <<-'EOF'
|
47
|
-
Enter one or more values for '%s'.
|
48
|
-
A blank line finishes input.
|
49
|
-
EOF
|
50
|
-
|
51
|
-
|
52
|
-
CLEAR_TO_EOL = "\e[K"
|
53
|
-
CLEAR_CURRENT_LINE = "\e[2K"
|
54
|
-
|
55
|
-
|
56
|
-
### Output a logging message
|
57
|
-
def log( *msg )
|
58
|
-
output = colorize( msg.flatten.join(' '), 'cyan' )
|
59
|
-
$stderr.puts( output )
|
60
|
-
end
|
22
|
+
module RakefileHelpers
|
23
|
+
# Set some ANSI escape code constants (Shamelessly stolen from Perl's
|
24
|
+
# Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
|
25
|
+
ANSI_ATTRIBUTES = {
|
26
|
+
'clear' => 0,
|
27
|
+
'reset' => 0,
|
28
|
+
'bold' => 1,
|
29
|
+
'dark' => 2,
|
30
|
+
'underline' => 4,
|
31
|
+
'underscore' => 4,
|
32
|
+
'blink' => 5,
|
33
|
+
'reverse' => 7,
|
34
|
+
'concealed' => 8,
|
61
35
|
|
36
|
+
'black' => 30, 'on_black' => 40,
|
37
|
+
'red' => 31, 'on_red' => 41,
|
38
|
+
'green' => 32, 'on_green' => 42,
|
39
|
+
'yellow' => 33, 'on_yellow' => 43,
|
40
|
+
'blue' => 34, 'on_blue' => 44,
|
41
|
+
'magenta' => 35, 'on_magenta' => 45,
|
42
|
+
'cyan' => 36, 'on_cyan' => 46,
|
43
|
+
'white' => 37, 'on_white' => 47
|
44
|
+
}
|
62
45
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
46
|
+
|
47
|
+
MULTILINE_PROMPT = <<-'EOF'
|
48
|
+
Enter one or more values for '%s'.
|
49
|
+
A blank line finishes input.
|
50
|
+
EOF
|
51
|
+
|
52
|
+
|
53
|
+
CLEAR_TO_EOL = "\e[K"
|
54
|
+
CLEAR_CURRENT_LINE = "\e[2K"
|
55
|
+
|
56
|
+
|
57
|
+
TAR_OPTS = { :compression => :gzip }
|
69
58
|
|
70
59
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
cmd.flatten!
|
60
|
+
###############
|
61
|
+
module_function
|
62
|
+
###############
|
75
63
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
64
|
+
### Output a logging message
|
65
|
+
def log( *msg )
|
66
|
+
output = colorize( msg.flatten.join(' '), 'cyan' )
|
67
|
+
$stderr.puts( output )
|
80
68
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
69
|
+
|
70
|
+
|
71
|
+
### Output a logging message if tracing is on
|
72
|
+
def trace( *msg )
|
73
|
+
return unless $trace
|
74
|
+
output = colorize( msg.flatten.join(' '), 'yellow' )
|
75
|
+
$stderr.puts( output )
|
89
76
|
end
|
90
|
-
end
|
91
77
|
|
92
78
|
|
93
|
-
###
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
run 'rake', '-N', *args
|
98
|
-
end
|
79
|
+
### Return the specified args as a string, quoting any that have a space.
|
80
|
+
def quotelist( *args )
|
81
|
+
return args.flatten.collect {|part| part =~ /\s/ ? part.inspect : part}
|
82
|
+
end
|
99
83
|
|
100
84
|
|
101
|
-
###
|
102
|
-
|
103
|
-
|
85
|
+
### Run the specified command +cmd+ with system(), failing if the execution
|
86
|
+
### fails.
|
87
|
+
def run( *cmd )
|
88
|
+
cmd.flatten!
|
104
89
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
open( '|-', 'w+' ) do |io|
|
111
|
-
|
112
|
-
# Parent
|
113
|
-
if io
|
114
|
-
yield( io )
|
90
|
+
if cmd.length > 1
|
91
|
+
trace( quotelist(*cmd) )
|
92
|
+
else
|
93
|
+
trace( cmd )
|
94
|
+
end
|
115
95
|
|
116
|
-
|
117
|
-
|
118
|
-
|
96
|
+
if $dryrun
|
97
|
+
$stderr.puts "(dry run mode)"
|
98
|
+
else
|
99
|
+
system( *cmd )
|
100
|
+
unless $?.success?
|
119
101
|
fail "Command failed: [%s]" % [cmd.join(' ')]
|
120
102
|
end
|
121
103
|
end
|
122
104
|
end
|
123
|
-
end
|
124
105
|
|
125
106
|
|
126
|
-
###
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
107
|
+
### Run the given +cmd+ with the specified +args+ without interpolation by the shell and
|
108
|
+
### return anything written to its STDOUT.
|
109
|
+
def read_command_output( cmd, *args )
|
110
|
+
trace "Reading output from: %s" % [ cmd, quotelist(cmd, *args) ]
|
111
|
+
output = IO.read( '|-' ) or exec cmd, *args
|
112
|
+
return output
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
### Run a subordinate Rake process with the same options and the specified +targets+.
|
117
|
+
def rake( *targets )
|
118
|
+
opts = ARGV.select {|arg| arg[0,1] == '-' }
|
119
|
+
args = opts + targets.map {|t| t.to_s }
|
120
|
+
run 'rake', '-N', *args
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
### Open a pipe to a process running the given +cmd+ and call the given block with it.
|
125
|
+
def pipeto( *cmd )
|
126
|
+
$DEBUG = true
|
127
|
+
|
128
|
+
cmd.flatten!
|
129
|
+
log( "Opening a pipe to: ", cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
|
130
|
+
if $dryrun
|
131
|
+
$stderr.puts "(dry run mode)"
|
132
|
+
else
|
133
|
+
open( '|-', 'w+' ) do |io|
|
134
|
+
|
135
|
+
# Parent
|
136
|
+
if io
|
137
|
+
yield( io )
|
138
|
+
|
139
|
+
# Child
|
140
|
+
else
|
141
|
+
exec( *cmd )
|
142
|
+
fail "Command failed: [%s]" % [cmd.join(' ')]
|
146
143
|
end
|
147
144
|
end
|
148
|
-
|
149
|
-
log "Done."
|
150
145
|
end
|
151
|
-
|
152
146
|
end
|
153
|
-
|
154
|
-
return targetpath
|
155
|
-
ensure
|
156
|
-
$stdout.sync = oldsync
|
157
|
-
end
|
158
147
|
|
159
148
|
|
160
|
-
###
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
end
|
149
|
+
### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
|
150
|
+
def download( sourceuri, targetfile=nil )
|
151
|
+
oldsync = $stdout.sync
|
152
|
+
$stdout.sync = true
|
153
|
+
require 'open-uri'
|
166
154
|
|
155
|
+
targetpath = Pathname.new( targetfile )
|
167
156
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
157
|
+
log "Downloading %s to %s" % [sourceuri, targetfile]
|
158
|
+
trace " connecting..."
|
159
|
+
ifh = open( sourceuri ) do |ifh|
|
160
|
+
trace " connected..."
|
161
|
+
targetpath.open( File::WRONLY|File::TRUNC|File::CREAT, 0644 ) do |ofh|
|
162
|
+
log "Downloading..."
|
163
|
+
buf = ''
|
164
|
+
|
165
|
+
while ifh.read( 16384, buf )
|
166
|
+
until buf.empty?
|
167
|
+
bytes = ofh.write( buf )
|
168
|
+
buf.slice!( 0, bytes )
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
log "Done."
|
173
|
+
end
|
184
174
|
|
175
|
+
end
|
185
176
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
string = ''
|
190
|
-
|
191
|
-
if block_given?
|
192
|
-
string = yield
|
193
|
-
else
|
194
|
-
string = args.shift
|
177
|
+
return targetpath
|
178
|
+
ensure
|
179
|
+
$stdout.sync = oldsync
|
195
180
|
end
|
196
|
-
|
197
|
-
ending = string[/(\s)$/] || ''
|
198
|
-
string = string.rstrip
|
199
181
|
|
200
|
-
return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
|
201
|
-
end
|
202
182
|
|
183
|
+
### Return the fully-qualified path to the specified +program+ in the PATH.
|
184
|
+
def which( program )
|
185
|
+
return nil unless ENV['PATH']
|
186
|
+
ENV['PATH'].split(/:/).
|
187
|
+
collect {|dir| Pathname.new(dir) + program }.
|
188
|
+
find {|path| path.exist? && path.executable? }
|
189
|
+
end
|
203
190
|
|
204
|
-
### Output the specified <tt>msg</tt> as an ANSI-colored error message
|
205
|
-
### (white on red).
|
206
|
-
def error_message( msg, details='' )
|
207
|
-
$stderr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
|
208
|
-
end
|
209
|
-
alias :error :error_message
|
210
191
|
|
192
|
+
### Create a string that contains the ANSI codes specified and return it
|
193
|
+
def ansi_code( *attributes )
|
194
|
+
attributes.flatten!
|
195
|
+
attributes.collect! {|at| at.to_s }
|
196
|
+
# $stderr.puts "Returning ansicode for TERM = %p: %p" %
|
197
|
+
# [ ENV['TERM'], attributes ]
|
198
|
+
return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
|
199
|
+
attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
|
200
|
+
|
201
|
+
# $stderr.puts " attr is: %p" % [attributes]
|
202
|
+
if attributes.empty?
|
203
|
+
return ''
|
204
|
+
else
|
205
|
+
return "\e[%sm" % attributes
|
206
|
+
end
|
207
|
+
end
|
211
208
|
|
212
|
-
### Highlight and embed a prompt control character in the given +string+ and return it.
|
213
|
-
def make_prompt_string( string )
|
214
|
-
return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
|
215
|
-
end
|
216
209
|
|
210
|
+
### Colorize the given +string+ with the specified +attributes+ and return it, handling
|
211
|
+
### line-endings, color reset, etc.
|
212
|
+
def colorize( *args )
|
213
|
+
string = ''
|
217
214
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
|
223
|
-
prompt_string.chomp!
|
224
|
-
prompt_string << ":" unless /\W$/.match( prompt_string )
|
225
|
-
response = nil
|
226
|
-
|
227
|
-
begin
|
228
|
-
prompt = make_prompt_string( prompt_string )
|
229
|
-
response = readline( prompt ) || ''
|
230
|
-
response.strip!
|
231
|
-
if block_given? && ! yield( response )
|
232
|
-
error_message( failure_msg + "\n\n" )
|
233
|
-
response = nil
|
215
|
+
if block_given?
|
216
|
+
string = yield
|
217
|
+
else
|
218
|
+
string = args.shift
|
234
219
|
end
|
235
|
-
end while response.nil?
|
236
220
|
|
237
|
-
|
238
|
-
|
221
|
+
ending = string[/(\s)$/] || ''
|
222
|
+
string = string.rstrip
|
239
223
|
|
224
|
+
return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
|
225
|
+
end
|
240
226
|
|
241
|
-
### Prompt the user with the given <tt>prompt_string</tt> via #prompt,
|
242
|
-
### substituting the given <tt>default</tt> if the user doesn't input
|
243
|
-
### anything. If a test is provided, the prompt will repeat until the test
|
244
|
-
### returns true. An optional failure message can also be passed in.
|
245
|
-
def prompt_with_default( prompt_string, default, failure_msg="Try again." )
|
246
|
-
response = nil
|
247
227
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
228
|
+
### Output the specified <tt>msg</tt> as an ANSI-colored error message
|
229
|
+
### (white on red).
|
230
|
+
def error_message( msg, details='' )
|
231
|
+
$stderr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
|
232
|
+
end
|
233
|
+
alias :error :error_message
|
252
234
|
|
253
|
-
trace "Validating response %p" % [ response ]
|
254
235
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
error_message( failure_msg + "\n\n" )
|
260
|
-
response = nil
|
261
|
-
end
|
262
|
-
end while response.nil?
|
236
|
+
### Highlight and embed a prompt control character in the given +string+ and return it.
|
237
|
+
def make_prompt_string( string )
|
238
|
+
return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
|
239
|
+
end
|
263
240
|
|
264
|
-
return nil if response == '~'
|
265
|
-
return response
|
266
|
-
end
|
267
241
|
|
242
|
+
### Output the specified <tt>prompt_string</tt> as a prompt (in green) and
|
243
|
+
### return the user's input with leading and trailing spaces removed. If a
|
244
|
+
### test is provided, the prompt will repeat until the test returns true.
|
245
|
+
### An optional failure message can also be passed in.
|
246
|
+
def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
|
247
|
+
prompt_string.chomp!
|
248
|
+
prompt_string << ":" unless /\W$/.match( prompt_string )
|
249
|
+
response = nil
|
250
|
+
|
251
|
+
begin
|
252
|
+
prompt = make_prompt_string( prompt_string )
|
253
|
+
response = readline( prompt ) || ''
|
254
|
+
response.strip!
|
255
|
+
if block_given? && ! yield( response )
|
256
|
+
error_message( failure_msg + "\n\n" )
|
257
|
+
response = nil
|
258
|
+
end
|
259
|
+
end while response.nil?
|
268
260
|
|
269
|
-
|
270
|
-
def prompt_for_multiple_values( label, default=nil )
|
271
|
-
$stderr.puts( MULTILINE_PROMPT % [label] )
|
272
|
-
if default
|
273
|
-
$stderr.puts "Enter a single blank line to keep the default:\n %p" % [ default ]
|
261
|
+
return response
|
274
262
|
end
|
275
263
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
264
|
+
|
265
|
+
### Prompt the user with the given <tt>prompt_string</tt> via #prompt,
|
266
|
+
### substituting the given <tt>default</tt> if the user doesn't input
|
267
|
+
### anything. If a test is provided, the prompt will repeat until the test
|
268
|
+
### returns true. An optional failure message can also be passed in.
|
269
|
+
def prompt_with_default( prompt_string, default, failure_msg="Try again." )
|
270
|
+
response = nil
|
271
|
+
|
272
|
+
begin
|
273
|
+
default ||= '~'
|
274
|
+
response = prompt( "%s [%s]" % [ prompt_string, default ] )
|
275
|
+
response = default.to_s if !response.nil? && response.empty?
|
276
|
+
|
277
|
+
trace "Validating response %p" % [ response ]
|
278
|
+
|
279
|
+
# the block is a validator. We need to make sure that the user didn't
|
280
|
+
# enter '~', because if they did, it's nil and we should move on. If
|
281
|
+
# they didn't, then call the block.
|
282
|
+
if block_given? && response != '~' && ! yield( response )
|
283
|
+
error_message( failure_msg + "\n\n" )
|
284
|
+
response = nil
|
285
|
+
end
|
286
|
+
end while response.nil?
|
287
|
+
|
288
|
+
return nil if response == '~'
|
289
|
+
return response
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
### Prompt for an array of values
|
294
|
+
def prompt_for_multiple_values( label, default=nil )
|
295
|
+
$stderr.puts( MULTILINE_PROMPT % [label] )
|
296
|
+
if default
|
297
|
+
$stderr.puts "Enter a single blank line to keep the default:\n %p" % [ default ]
|
285
298
|
end
|
286
|
-
end until result.nil? || result.empty?
|
287
|
-
|
288
|
-
return results.flatten
|
289
|
-
end
|
290
299
|
|
300
|
+
results = []
|
301
|
+
result = nil
|
291
302
|
|
292
|
-
|
293
|
-
|
294
|
-
|
303
|
+
begin
|
304
|
+
result = readline( make_prompt_string("> ") )
|
305
|
+
if result.nil? || result.empty?
|
306
|
+
results << default if default && results.empty?
|
307
|
+
else
|
308
|
+
results << result
|
309
|
+
end
|
310
|
+
end until result.nil? || result.empty?
|
295
311
|
|
296
|
-
|
297
|
-
|
312
|
+
return results.flatten
|
313
|
+
end
|
298
314
|
|
299
|
-
begin
|
300
|
-
newt = term.dup
|
301
|
-
newt.c_lflag &= ~Termios::ECHO
|
302
|
-
newt.c_lflag &= ~Termios::ICANON if masked
|
303
315
|
|
304
|
-
|
316
|
+
### Turn echo and masking of input on/off.
|
317
|
+
def noecho( masked=false )
|
318
|
+
require 'termios'
|
305
319
|
|
306
|
-
rval =
|
307
|
-
|
308
|
-
|
320
|
+
rval = nil
|
321
|
+
term = Termios.getattr( $stdin )
|
322
|
+
|
323
|
+
begin
|
324
|
+
newt = term.dup
|
325
|
+
newt.c_lflag &= ~Termios::ECHO
|
326
|
+
newt.c_lflag &= ~Termios::ICANON if masked
|
327
|
+
|
328
|
+
Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
|
329
|
+
|
330
|
+
rval = yield
|
331
|
+
ensure
|
332
|
+
Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
|
333
|
+
end
|
334
|
+
|
335
|
+
return rval
|
309
336
|
end
|
310
|
-
|
311
|
-
return rval
|
312
|
-
end
|
313
337
|
|
314
338
|
|
315
|
-
### Prompt the user for her password, turning off echo if the 'termios' module is
|
316
|
-
### available.
|
317
|
-
def prompt_for_password( prompt="Password: " )
|
318
|
-
|
319
|
-
|
320
|
-
|
339
|
+
### Prompt the user for her password, turning off echo if the 'termios' module is
|
340
|
+
### available.
|
341
|
+
def prompt_for_password( prompt="Password: " )
|
342
|
+
return noecho( true ) do
|
343
|
+
$stderr.print( prompt )
|
344
|
+
($stdin.gets || '').chomp
|
345
|
+
end
|
321
346
|
end
|
322
|
-
end
|
323
347
|
|
324
348
|
|
325
|
-
### Display a description of a potentially-dangerous task, and prompt
|
326
|
-
### for confirmation. If the user answers with anything that begins
|
327
|
-
### with 'y', yield to the block. If +abort_on_decline+ is +true+,
|
328
|
-
### any non-'y' answer will fail with an error message.
|
329
|
-
def ask_for_confirmation( description, abort_on_decline=true )
|
330
|
-
|
349
|
+
### Display a description of a potentially-dangerous task, and prompt
|
350
|
+
### for confirmation. If the user answers with anything that begins
|
351
|
+
### with 'y', yield to the block. If +abort_on_decline+ is +true+,
|
352
|
+
### any non-'y' answer will fail with an error message.
|
353
|
+
def ask_for_confirmation( description, abort_on_decline=true )
|
354
|
+
puts description
|
355
|
+
|
356
|
+
answer = prompt_with_default( "Continue?", 'n' ) do |input|
|
357
|
+
input =~ /^[yn]/i
|
358
|
+
end
|
359
|
+
|
360
|
+
if answer =~ /^y/i
|
361
|
+
return yield
|
362
|
+
elsif abort_on_decline
|
363
|
+
error "Aborted."
|
364
|
+
fail
|
365
|
+
end
|
331
366
|
|
332
|
-
|
333
|
-
input =~ /^[yn]/i
|
367
|
+
return false
|
334
368
|
end
|
369
|
+
alias :prompt_for_confirmation :ask_for_confirmation
|
370
|
+
|
335
371
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
372
|
+
### Search line-by-line in the specified +file+ for the given +regexp+, returning the
|
373
|
+
### first match, or nil if no match was found. If the +regexp+ has any capture groups,
|
374
|
+
### those will be returned in an Array, else the whole matching line is returned.
|
375
|
+
def find_pattern_in_file( regexp, file )
|
376
|
+
rval = nil
|
377
|
+
|
378
|
+
File.open( file, 'r' ).each do |line|
|
379
|
+
if (( match = regexp.match(line) ))
|
380
|
+
rval = match.captures.empty? ? match[0] : match.captures
|
381
|
+
break
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
return rval
|
341
386
|
end
|
342
387
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
388
|
+
|
389
|
+
### Search line-by-line in the output of the specified +cmd+ for the given +regexp+,
|
390
|
+
### returning the first match, or nil if no match was found. If the +regexp+ has any
|
391
|
+
### capture groups, those will be returned in an Array, else the whole matching line
|
392
|
+
### is returned.
|
393
|
+
def find_pattern_in_pipe( regexp, *cmd )
|
394
|
+
require 'open3'
|
395
|
+
output = []
|
396
|
+
|
397
|
+
log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
|
398
|
+
Open3.popen3( *cmd ) do |stdin, stdout, stderr|
|
399
|
+
stdin.close
|
400
|
+
|
401
|
+
output << stdout.gets until stdout.eof?
|
402
|
+
output << stderr.gets until stderr.eof?
|
358
403
|
end
|
404
|
+
|
405
|
+
result = output.find { |line| regexp.match(line) }
|
406
|
+
return $1 || result
|
359
407
|
end
|
360
408
|
|
361
|
-
return rval
|
362
|
-
end
|
363
409
|
|
410
|
+
### Invoke the user's editor on the given +filename+ and return the exit code
|
411
|
+
### from doing so.
|
412
|
+
def edit( filename )
|
413
|
+
editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
|
414
|
+
system editor, filename
|
415
|
+
unless $?.success? || editor =~ /vim/i
|
416
|
+
fail "Editor exited uncleanly."
|
417
|
+
end
|
418
|
+
end
|
364
419
|
|
365
|
-
### Search line-by-line in the output of the specified +cmd+ for the given +regexp+,
|
366
|
-
### returning the first match, or nil if no match was found. If the +regexp+ has any
|
367
|
-
### capture groups, those will be returned in an Array, else the whole matching line
|
368
|
-
### is returned.
|
369
|
-
def find_pattern_in_pipe( regexp, *cmd )
|
370
|
-
output = []
|
371
|
-
|
372
|
-
log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
|
373
|
-
Open3.popen3( *cmd ) do |stdin, stdout, stderr|
|
374
|
-
stdin.close
|
375
420
|
|
376
|
-
|
377
|
-
|
421
|
+
### Extract all the non Rake-target arguments from ARGV and return them.
|
422
|
+
def get_target_args
|
423
|
+
args = ARGV.reject {|arg| arg =~ /^-/ || Rake::Task.task_defined?(arg) }
|
424
|
+
return args
|
378
425
|
end
|
379
|
-
|
380
|
-
result = output.find { |line| regexp.match(line) }
|
381
|
-
return $1 || result
|
382
|
-
end
|
383
426
|
|
384
427
|
|
385
|
-
###
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
428
|
+
### Log a subdirectory change, execute a block, and exit the subdirectory
|
429
|
+
def in_subdirectory( subdir )
|
430
|
+
block = Proc.new
|
431
|
+
|
432
|
+
log "Entering #{subdir}"
|
433
|
+
Dir.chdir( subdir, &block )
|
434
|
+
log "Leaving #{subdir}"
|
392
435
|
end
|
393
|
-
end
|
394
436
|
|
395
437
|
|
396
|
-
###
|
397
|
-
def
|
398
|
-
|
399
|
-
|
400
|
-
end
|
438
|
+
### Make an easily-comparable version vector out of +ver+ and return it.
|
439
|
+
def vvec( ver )
|
440
|
+
return ver.split('.').collect {|char| char.to_i }.pack('N*')
|
441
|
+
end
|
401
442
|
|
402
443
|
|
403
|
-
###
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
444
|
+
### Archive::Tar::Reader#extract (as of 0.9.0) is broken w.r.t.
|
445
|
+
### permissions, so we have to do this ourselves.
|
446
|
+
def untar( tarfile, targetdir )
|
447
|
+
require 'archive/tar'
|
448
|
+
targetdir = Pathname( targetdir )
|
449
|
+
raise "No such directory: #{targetdir}" unless targetdir.directory?
|
450
|
+
|
451
|
+
reader = Archive::Tar::Reader.new( tarfile.to_s, TAR_OPTS )
|
452
|
+
|
453
|
+
mkdir_p( targetdir )
|
454
|
+
reader.each( true ) do |header, body|
|
455
|
+
path = targetdir + header[:path]
|
456
|
+
# trace "Header is: %p" % [ header ]
|
457
|
+
|
458
|
+
case header[:type]
|
459
|
+
when :file
|
460
|
+
trace " #{path}"
|
461
|
+
path.open( File::WRONLY|File::EXCL|File::CREAT|File::TRUNC, header[:mode] ) do |fio|
|
462
|
+
bytesize = header[:size]
|
463
|
+
fio.write( body[0,bytesize] )
|
464
|
+
end
|
465
|
+
|
466
|
+
when :directory
|
467
|
+
trace " #{path}"
|
468
|
+
path.mkpath
|
469
|
+
|
470
|
+
when :link
|
471
|
+
linktarget = targetdir + header[:dest]
|
472
|
+
trace " #{path} => #{linktarget}"
|
473
|
+
path.make_link( linktarget.to_s )
|
474
|
+
|
475
|
+
when :symlink
|
476
|
+
linktarget = targetdir + header[:dest]
|
477
|
+
trace " #{path} -> #{linktarget}"
|
478
|
+
path.make_symlink( linktarget )
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
end
|
483
|
+
|
484
|
+
|
485
|
+
### Extract the contents of the specified +zipfile+ into the given +targetdir+.
|
486
|
+
def unzip( zipfile, targetdir, *files )
|
487
|
+
require 'zip/zip'
|
488
|
+
targetdir = Pathname( targetdir )
|
489
|
+
raise "No such directory: #{targetdir}" unless targetdir.directory?
|
490
|
+
|
491
|
+
Zip::ZipFile.foreach( zipfile ) do |entry|
|
492
|
+
# trace " entry is: %p" % [ entry ]
|
493
|
+
next unless files.empty? || files.include?( entry.name )
|
494
|
+
target_path = targetdir + entry.name
|
495
|
+
# trace " would extract to: %s" % [ target_path ]
|
496
|
+
entry.extract( target_path ) { true }
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
end # module Rakefile::Helpers
|
501
|
+
|
412
502
|
|