linguistics 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # 1.9.1 fixes
2
+
3
+
4
+ # Make Pathname compatible with 1.8.7 Pathname.
5
+ unless Pathname.instance_methods.include?( :=~ )
6
+ class Pathname
7
+ def self::glob( *args ) # :yield: p
8
+ args = args.collect {|p| p.to_s }
9
+ if block_given?
10
+ Dir.glob(*args) {|f| yield self.new(f) }
11
+ else
12
+ Dir.glob(*args).map {|f| self.new(f) }
13
+ end
14
+ end
15
+
16
+ def =~( other )
17
+ self.to_s =~ other
18
+ end
19
+
20
+ def to_str
21
+ self.to_s
22
+ end
23
+ end
24
+ end
25
+
26
+
@@ -0,0 +1,76 @@
1
+ #
2
+ # Dependency-checking and Installation Rake Tasks
3
+
4
+ #
5
+
6
+ require 'rubygems/dependency_installer'
7
+ require 'rubygems/source_index'
8
+ require 'rubygems/requirement'
9
+ require 'rubygems/doc_manager'
10
+
11
+ ### Install the specified +gems+ if they aren't already installed.
12
+ def install_gems( gems )
13
+
14
+ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
15
+ :generate_rdoc => true,
16
+ :generate_ri => true,
17
+ :install_dir => Gem.dir,
18
+ :format_executable => false,
19
+ :test => false,
20
+ :version => Gem::Requirement.default,
21
+ })
22
+
23
+ # Check for root
24
+ if Process.euid != 0
25
+ $stderr.puts "This probably won't work, as you aren't root, but I'll try anyway"
26
+ end
27
+
28
+ gemindex = Gem::SourceIndex.from_installed_gems
29
+
30
+ gems.each do |gemname, reqstring|
31
+ requirement = Gem::Requirement.new( reqstring )
32
+ trace "requirement is: %p" % [ requirement ]
33
+
34
+ trace "Searching for an installed #{gemname}..."
35
+ specs = gemindex.find_name( gemname )
36
+ trace "...found %d specs: %s" %
37
+ [ specs.length, specs.collect {|s| "%s %s" % [s.name, s.version] }.join(', ') ]
38
+
39
+ if spec = specs.find {|spec| requirement.satisfied_by?(spec.version) }
40
+ log "Version %s of %s is already installed (needs %s); skipping..." %
41
+ [ spec.version, spec.name, requirement ]
42
+ next
43
+ end
44
+
45
+ rgv = Gem::Version.new( Gem::RubyGemsVersion )
46
+ installer = nil
47
+
48
+ log "Trying to install #{gemname.inspect} #{requirement}..."
49
+ if rgv >= Gem::Version.new( '1.1.1' )
50
+ installer = Gem::DependencyInstaller.new
51
+ installer.install( gemname, requirement )
52
+ else
53
+ installer = Gem::DependencyInstaller.new( gemname )
54
+ installer.install
55
+ end
56
+
57
+ installer.installed_gems.each do |spec|
58
+ log "Installed: %s" % [ spec.full_name ]
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+
65
+ ### Task: install runtime dependencies
66
+ desc "Install runtime dependencies as gems"
67
+ task :install_dependencies do
68
+ install_gems( DEPENDENCIES )
69
+ end
70
+
71
+ ### Task: install gems for development tasks
72
+ desc "Install development dependencies as gems"
73
+ task :install_dev_dependencies do
74
+ install_gems( DEVELOPMENT_DEPENDENCIES )
75
+ end
76
+
data/rake/helpers.rb ADDED
@@ -0,0 +1,434 @@
1
+ # encoding: utf-8
2
+ #####################################################################
3
+ ### 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
+ require 'pathname'
8
+ require 'uri'
9
+ require 'open3'
10
+
11
+ begin
12
+ require 'readline'
13
+ include Readline
14
+ rescue LoadError
15
+ # Fall back to a plain prompt
16
+ def readline( text )
17
+ $stderr.print( text.chomp )
18
+ return $stdin.gets
19
+ end
20
+ end
21
+
22
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
23
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
24
+ ANSI_ATTRIBUTES = {
25
+ 'clear' => 0,
26
+ 'reset' => 0,
27
+ 'bold' => 1,
28
+ 'dark' => 2,
29
+ 'underline' => 4,
30
+ 'underscore' => 4,
31
+ 'blink' => 5,
32
+ 'reverse' => 7,
33
+ 'concealed' => 8,
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
61
+
62
+
63
+ ### Output a logging message if tracing is on
64
+ def trace( *msg )
65
+ return unless $trace
66
+ output = colorize( msg.flatten.join(' '), 'yellow' )
67
+ $stderr.puts( output )
68
+ end
69
+
70
+
71
+ ### Return the specified args as a string, quoting any that have a space.
72
+ def quotelist( *args )
73
+ return args.flatten.collect {|part| part =~ /\s/ ? part.inspect : part}
74
+ end
75
+
76
+
77
+ ### Run the specified command +cmd+ with system(), failing if the execution
78
+ ### fails.
79
+ def run( *cmd )
80
+ cmd.flatten!
81
+
82
+ if cmd.length > 1
83
+ trace( quotelist(*cmd) )
84
+ else
85
+ trace( cmd )
86
+ end
87
+
88
+ if $dryrun
89
+ $stderr.puts "(dry run mode)"
90
+ else
91
+ system( *cmd )
92
+ unless $?.success?
93
+ fail "Command failed: [%s]" % [cmd.join(' ')]
94
+ end
95
+ end
96
+ end
97
+
98
+
99
+ ### Run the given +cmd+ with the specified +args+ without interpolation by the shell and
100
+ ### return anything written to its STDOUT.
101
+ def read_command_output( cmd, *args )
102
+ trace "Reading output from: %s" % [ cmd, quotelist(cmd, *args) ]
103
+ output = IO.read( '|-' ) or exec cmd, *args
104
+ return output
105
+ end
106
+
107
+
108
+ ### Run a subordinate Rake process with the same options and the specified +targets+.
109
+ def rake( *targets )
110
+ opts = ARGV.select {|arg| arg[0,1] == '-' }
111
+ args = opts + targets.map {|t| t.to_s }
112
+ run 'rake', '-N', *args
113
+ end
114
+
115
+
116
+ ### Open a pipe to a process running the given +cmd+ and call the given block with it.
117
+ def pipeto( *cmd )
118
+ $DEBUG = true
119
+
120
+ cmd.flatten!
121
+ log( "Opening a pipe to: ", cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
122
+ if $dryrun
123
+ $stderr.puts "(dry run mode)"
124
+ else
125
+ open( '|-', 'w+' ) do |io|
126
+
127
+ # Parent
128
+ if io
129
+ yield( io )
130
+
131
+ # Child
132
+ else
133
+ exec( *cmd )
134
+ fail "Command failed: [%s]" % [cmd.join(' ')]
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+
141
+ ### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
142
+ def download( sourceuri, targetfile=nil )
143
+ oldsync = $stdout.sync
144
+ $stdout.sync = true
145
+ require 'open-uri'
146
+
147
+ targetpath = Pathname.new( targetfile )
148
+
149
+ log "Downloading %s to %s" % [sourceuri, targetfile]
150
+ trace " connecting..."
151
+ ifh = open( sourceuri ) do |ifh|
152
+ trace " connected..."
153
+ targetpath.open( File::WRONLY|File::TRUNC|File::CREAT, 0644 ) do |ofh|
154
+ log "Downloading..."
155
+ buf = ''
156
+
157
+ while ifh.read( 16384, buf )
158
+ until buf.empty?
159
+ bytes = ofh.write( buf )
160
+ buf.slice!( 0, bytes )
161
+ end
162
+ end
163
+
164
+ log "Done."
165
+ end
166
+
167
+ end
168
+
169
+ return targetpath
170
+ ensure
171
+ $stdout.sync = oldsync
172
+ end
173
+
174
+
175
+ ### Return the fully-qualified path to the specified +program+ in the PATH.
176
+ def which( program )
177
+ ENV['PATH'].split(/:/).
178
+ collect {|dir| Pathname.new(dir) + program }.
179
+ find {|path| path.exist? && path.executable? }
180
+ end
181
+
182
+
183
+ ### Create a string that contains the ANSI codes specified and return it
184
+ def ansi_code( *attributes )
185
+ attributes.flatten!
186
+ attributes.collect! {|at| at.to_s }
187
+ # $stderr.puts "Returning ansicode for TERM = %p: %p" %
188
+ # [ ENV['TERM'], attributes ]
189
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
190
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
191
+
192
+ # $stderr.puts " attr is: %p" % [attributes]
193
+ if attributes.empty?
194
+ return ''
195
+ else
196
+ return "\e[%sm" % attributes
197
+ end
198
+ end
199
+
200
+
201
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
202
+ ### line-endings, color reset, etc.
203
+ def colorize( *args )
204
+ string = ''
205
+
206
+ if block_given?
207
+ string = yield
208
+ else
209
+ string = args.shift
210
+ end
211
+
212
+ ending = string[/(\s)$/] || ''
213
+ string = string.rstrip
214
+
215
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
216
+ end
217
+
218
+
219
+ ### Output the specified <tt>msg</tt> as an ANSI-colored error message
220
+ ### (white on red).
221
+ def error_message( msg, details='' )
222
+ $stderr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
223
+ end
224
+ alias :error :error_message
225
+
226
+
227
+ ### Highlight and embed a prompt control character in the given +string+ and return it.
228
+ def make_prompt_string( string )
229
+ return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
230
+ end
231
+
232
+
233
+ ### Output the specified <tt>prompt_string</tt> as a prompt (in green) and
234
+ ### return the user's input with leading and trailing spaces removed. If a
235
+ ### test is provided, the prompt will repeat until the test returns true.
236
+ ### An optional failure message can also be passed in.
237
+ def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
238
+ prompt_string.chomp!
239
+ prompt_string << ":" unless /\W$/.match( prompt_string )
240
+ response = nil
241
+
242
+ begin
243
+ prompt = make_prompt_string( prompt_string )
244
+ response = readline( prompt ) || ''
245
+ response.strip!
246
+ if block_given? && ! yield( response )
247
+ error_message( failure_msg + "\n\n" )
248
+ response = nil
249
+ end
250
+ end while response.nil?
251
+
252
+ return response
253
+ end
254
+
255
+
256
+ ### Prompt the user with the given <tt>prompt_string</tt> via #prompt,
257
+ ### substituting the given <tt>default</tt> if the user doesn't input
258
+ ### anything. If a test is provided, the prompt will repeat until the test
259
+ ### returns true. An optional failure message can also be passed in.
260
+ def prompt_with_default( prompt_string, default, failure_msg="Try again." )
261
+ response = nil
262
+
263
+ begin
264
+ default ||= '~'
265
+ response = prompt( "%s [%s]" % [ prompt_string, default ] )
266
+ response = default.to_s if !response.nil? && response.empty?
267
+
268
+ trace "Validating response %p" % [ response ]
269
+
270
+ # the block is a validator. We need to make sure that the user didn't
271
+ # enter '~', because if they did, it's nil and we should move on. If
272
+ # they didn't, then call the block.
273
+ if block_given? && response != '~' && ! yield( response )
274
+ error_message( failure_msg + "\n\n" )
275
+ response = nil
276
+ end
277
+ end while response.nil?
278
+
279
+ return nil if response == '~'
280
+ return response
281
+ end
282
+
283
+
284
+ ### Prompt for an array of values
285
+ def prompt_for_multiple_values( label, default=nil )
286
+ $stderr.puts( MULTILINE_PROMPT % [label] )
287
+ if default
288
+ $stderr.puts "Enter a single blank line to keep the default:\n %p" % [ default ]
289
+ end
290
+
291
+ results = []
292
+ result = nil
293
+
294
+ begin
295
+ result = readline( make_prompt_string("> ") )
296
+ if result.nil? || result.empty?
297
+ results << default if default && results.empty?
298
+ else
299
+ results << result
300
+ end
301
+ end until result.nil? || result.empty?
302
+
303
+ return results.flatten
304
+ end
305
+
306
+
307
+ ### Turn echo and masking of input on/off.
308
+ def noecho( masked=false )
309
+ require 'termios'
310
+
311
+ rval = nil
312
+ term = Termios.getattr( $stdin )
313
+
314
+ begin
315
+ newt = term.dup
316
+ newt.c_lflag &= ~Termios::ECHO
317
+ newt.c_lflag &= ~Termios::ICANON if masked
318
+
319
+ Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
320
+
321
+ rval = yield
322
+ ensure
323
+ Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
324
+ end
325
+
326
+ return rval
327
+ end
328
+
329
+
330
+ ### Prompt the user for her password, turning off echo if the 'termios' module is
331
+ ### available.
332
+ def prompt_for_password( prompt="Password: " )
333
+ return noecho( true ) do
334
+ $stderr.print( prompt )
335
+ ($stdin.gets || '').chomp
336
+ end
337
+ end
338
+
339
+
340
+ ### Display a description of a potentially-dangerous task, and prompt
341
+ ### for confirmation. If the user answers with anything that begins
342
+ ### with 'y', yield to the block. If +abort_on_decline+ is +true+,
343
+ ### any non-'y' answer will fail with an error message.
344
+ def ask_for_confirmation( description, abort_on_decline=true )
345
+ puts description
346
+
347
+ answer = prompt_with_default( "Continue?", 'n' ) do |input|
348
+ input =~ /^[yn]/i
349
+ end
350
+
351
+ if answer =~ /^y/i
352
+ return yield
353
+ elsif abort_on_decline
354
+ error "Aborted."
355
+ fail
356
+ end
357
+
358
+ return false
359
+ end
360
+ alias :prompt_for_confirmation :ask_for_confirmation
361
+
362
+
363
+ ### Search line-by-line in the specified +file+ for the given +regexp+, returning the
364
+ ### first match, or nil if no match was found. If the +regexp+ has any capture groups,
365
+ ### those will be returned in an Array, else the whole matching line is returned.
366
+ def find_pattern_in_file( regexp, file )
367
+ rval = nil
368
+
369
+ File.open( file, 'r' ).each do |line|
370
+ if (( match = regexp.match(line) ))
371
+ rval = match.captures.empty? ? match[0] : match.captures
372
+ break
373
+ end
374
+ end
375
+
376
+ return rval
377
+ end
378
+
379
+
380
+ ### Search line-by-line in the output of the specified +cmd+ for the given +regexp+,
381
+ ### returning the first match, or nil if no match was found. If the +regexp+ has any
382
+ ### capture groups, those will be returned in an Array, else the whole matching line
383
+ ### is returned.
384
+ def find_pattern_in_pipe( regexp, *cmd )
385
+ output = []
386
+
387
+ log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
388
+ Open3.popen3( *cmd ) do |stdin, stdout, stderr|
389
+ stdin.close
390
+
391
+ output << stdout.gets until stdout.eof?
392
+ output << stderr.gets until stderr.eof?
393
+ end
394
+
395
+ result = output.find { |line| regexp.match(line) }
396
+ return $1 || result
397
+ end
398
+
399
+
400
+ ### Invoke the user's editor on the given +filename+ and return the exit code
401
+ ### from doing so.
402
+ def edit( filename )
403
+ editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
404
+ system editor, filename
405
+ unless $?.success? || editor =~ /vim/i
406
+ fail "Editor exited uncleanly."
407
+ end
408
+ end
409
+
410
+
411
+ ### Extract all the non Rake-target arguments from ARGV and return them.
412
+ def get_target_args
413
+ args = ARGV.reject {|arg| arg =~ /^-/ || Rake::Task.task_defined?(arg) }
414
+ return args
415
+ end
416
+
417
+
418
+ ### Log a subdirectory change, execute a block, and exit the subdirectory
419
+ def in_subdirectory( subdir )
420
+ block = Proc.new
421
+
422
+ log "Entering #{subdir}"
423
+ Dir.chdir( subdir, &block )
424
+ log "Leaving #{subdir}"
425
+ end
426
+
427
+
428
+ ### Make an easily-comparable version vector out of +ver+ and return it.
429
+ def vvec( ver )
430
+ return ver.split('.').collect {|char| char.to_i }.pack('N*')
431
+ end
432
+
433
+
434
+