pg 0.9.0-x86-mingw32 → 0.11.0pre229-x86-mingw32
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 +2 -0
- data/ChangeLog +214 -1
- data/Contributors +2 -0
- data/README +2 -2
- data/README.windows +7 -4
- data/Rakefile +51 -29
- data/Rakefile.local +229 -160
- data/ext/compat.c +2 -2
- data/ext/compat.h +4 -0
- data/ext/extconf.rb +27 -94
- data/ext/pg.c +300 -121
- data/ext/pg.h +4 -0
- data/lib/1.8/pg_ext.so +0 -0
- data/lib/1.9/pg_ext.so +0 -0
- data/lib/pg.rb +11 -6
- data/rake/documentation.rb +123 -0
- data/rake/helpers.rb +375 -308
- data/rake/hg.rb +51 -6
- data/rake/manual.rb +11 -6
- data/rake/packaging.rb +7 -1
- data/rake/publishing.rb +158 -91
- data/rake/testing.rb +53 -88
- data/spec/lib/helpers.rb +40 -18
- data/spec/m17n_spec.rb +36 -24
- data/spec/pgconn_spec.rb +276 -20
- data/spec/pgresult_spec.rb +12 -6
- metadata +61 -20
- metadata.gz.sig +0 -0
- data/rake/rdoc.rb +0 -30
data/ext/pg.h
CHANGED
data/lib/1.8/pg_ext.so
CHANGED
Binary file
|
data/lib/1.9/pg_ext.so
CHANGED
Binary file
|
data/lib/pg.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
if RUBY_PLATFORM =~/(mswin|mingw)/i
|
5
|
-
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
|
6
|
-
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
7
|
-
require "#{major_minor}/pg_ext"
|
8
|
-
else
|
3
|
+
begin
|
9
4
|
require 'pg_ext'
|
5
|
+
rescue LoadError => err
|
6
|
+
# If it's a Windows binary gem, try the <major>.<minor> subdirectory
|
7
|
+
if RUBY_PLATFORM =~/(mswin|mingw)/i
|
8
|
+
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
|
9
|
+
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
10
|
+
require "#{major_minor}/pg_ext"
|
11
|
+
else
|
12
|
+
raise
|
13
|
+
end
|
14
|
+
|
10
15
|
end
|
11
16
|
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#
|
2
|
+
# Documentation Rake tasks
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'rake/clean'
|
6
|
+
|
7
|
+
|
8
|
+
# Append docs/lib to the load path if it exists for documentation
|
9
|
+
# helpers.
|
10
|
+
DOCSLIB = DOCSDIR + 'lib'
|
11
|
+
$LOAD_PATH.unshift( DOCSLIB.to_s ) if DOCSLIB.exist?
|
12
|
+
|
13
|
+
# Make relative string paths of all the stuff we need to generate docs for
|
14
|
+
DOCFILES = Rake::FileList[ LIB_FILES + EXT_FILES + GEMSPEC.extra_rdoc_files ]
|
15
|
+
|
16
|
+
# Documentation coverage constants
|
17
|
+
COVERAGE_DIR = BASEDIR + 'coverage'
|
18
|
+
COVERAGE_REPORT = COVERAGE_DIR + 'documentation.txt'
|
19
|
+
|
20
|
+
|
21
|
+
# Prefer YARD, fallback to RDoc
|
22
|
+
begin
|
23
|
+
require 'yard'
|
24
|
+
require 'yard/rake/yardoc_task'
|
25
|
+
|
26
|
+
# Undo the lazy-assed monkeypatch yard/globals.rb installs and
|
27
|
+
# re-install them as mixins as they should have been from the
|
28
|
+
# start
|
29
|
+
# <metamonkeypatch>
|
30
|
+
class Object
|
31
|
+
remove_method :log
|
32
|
+
remove_method :P
|
33
|
+
end
|
34
|
+
|
35
|
+
module YardGlobals
|
36
|
+
def P(namespace, name = nil)
|
37
|
+
namespace, name = nil, namespace if name.nil?
|
38
|
+
YARD::Registry.resolve(namespace, name, false, true)
|
39
|
+
end
|
40
|
+
|
41
|
+
def log
|
42
|
+
YARD::Logger.instance
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class YARD::CLI::Base; include YardGlobals; end
|
47
|
+
class YARD::CLI::Command; include YardGlobals; end
|
48
|
+
class YARD::Parser::SourceParser; extend YardGlobals; include YardGlobals; end
|
49
|
+
class YARD::Parser::CParser; include YardGlobals; end
|
50
|
+
class YARD::CodeObjects::Base; include YardGlobals; end
|
51
|
+
class YARD::Handlers::Base; include YardGlobals; end
|
52
|
+
class YARD::Handlers::Processor; include YardGlobals; end
|
53
|
+
class YARD::Serializers::Base; include YardGlobals; end
|
54
|
+
class YARD::RegistryStore; include YardGlobals; end
|
55
|
+
class YARD::Docstring; include YardGlobals; end
|
56
|
+
module YARD::Templates::Helpers::ModuleHelper; include YardGlobals; end
|
57
|
+
module YARD::Templates::Helpers::HtmlHelper; include YardGlobals; end
|
58
|
+
|
59
|
+
if vvec(RUBY_VERSION) >= vvec("1.9.1")
|
60
|
+
# Monkeypatched to allow more than two '#' characters at the beginning
|
61
|
+
# of the comment line.
|
62
|
+
# Patched from yard-0.5.8
|
63
|
+
require 'yard/parser/ruby/ruby_parser'
|
64
|
+
class YARD::Parser::Ruby::RipperParser < Ripper
|
65
|
+
def on_comment(comment)
|
66
|
+
visit_ns_token(:comment, comment)
|
67
|
+
case comment
|
68
|
+
when /\A# @group\s+(.+)\s*\Z/
|
69
|
+
@groups.unshift [lineno, $1]
|
70
|
+
return
|
71
|
+
when /\A# @endgroup\s*\Z/
|
72
|
+
@groups.unshift [lineno, nil]
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
comment = comment.gsub(/^\#+\s{0,1}/, '').chomp
|
77
|
+
append_comment = @comments[lineno - 1]
|
78
|
+
|
79
|
+
if append_comment && @comments_last_column == column
|
80
|
+
@comments.delete(lineno - 1)
|
81
|
+
comment = append_comment + "\n" + comment
|
82
|
+
end
|
83
|
+
|
84
|
+
@comments[lineno] = comment
|
85
|
+
@comments_last_column = column
|
86
|
+
end
|
87
|
+
end # class YARD::Parser::Ruby::RipperParser
|
88
|
+
end
|
89
|
+
|
90
|
+
# </metamonkeypatch>
|
91
|
+
|
92
|
+
YARD_OPTIONS = [] unless defined?( YARD_OPTIONS )
|
93
|
+
|
94
|
+
yardoctask = YARD::Rake::YardocTask.new( :apidocs ) do |task|
|
95
|
+
task.files = DOCFILES
|
96
|
+
task.options = YARD_OPTIONS
|
97
|
+
task.options << '--debug' << '--verbose' if $trace
|
98
|
+
end
|
99
|
+
yardoctask.before = lambda {
|
100
|
+
trace "Calling yardoc like:",
|
101
|
+
" yardoc %s" % [ quotelist(yardoctask.options + yardoctask.files).join(' ') ]
|
102
|
+
}
|
103
|
+
|
104
|
+
YARDOC_CACHE = BASEDIR + '.yardoc'
|
105
|
+
CLOBBER.include( YARDOC_CACHE.to_s )
|
106
|
+
|
107
|
+
rescue LoadError
|
108
|
+
require 'rdoc/task'
|
109
|
+
|
110
|
+
desc "Build API documentation in #{API_DOCSDIR}"
|
111
|
+
RDoc::Task.new( :apidocs ) do |task|
|
112
|
+
task.main = "README"
|
113
|
+
task.rdoc_files.include( DOCFILES )
|
114
|
+
task.rdoc_dir = API_DOCSDIR.to_s
|
115
|
+
task.options = RDOC_OPTIONS
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Need the DOCFILES to exist to build the API docs
|
120
|
+
task :apidocs => DOCFILES
|
121
|
+
|
122
|
+
CLEAN.include( API_DOCSDIR.to_s )
|
123
|
+
|
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,417 +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
|
-
### 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
46
|
|
47
|
+
MULTILINE_PROMPT = <<-'EOF'
|
48
|
+
Enter one or more values for '%s'.
|
49
|
+
A blank line finishes input.
|
50
|
+
EOF
|
70
51
|
|
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
52
|
|
53
|
+
CLEAR_TO_EOL = "\e[K"
|
54
|
+
CLEAR_CURRENT_LINE = "\e[2K"
|
55
|
+
|
56
|
+
|
57
|
+
TAR_OPTS = { :compression => :gzip }
|
58
|
+
|
59
|
+
|
60
|
+
###############
|
61
|
+
module_function
|
62
|
+
###############
|
63
|
+
|
64
|
+
### Output a logging message
|
65
|
+
def log( *msg )
|
66
|
+
output = colorize( msg.flatten.join(' '), 'cyan' )
|
67
|
+
$stderr.puts( output )
|
68
|
+
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 )
|
76
|
+
end
|
76
77
|
|
77
|
-
### Run the specified command +cmd+ with system(), failing if the execution
|
78
|
-
### fails.
|
79
|
-
def run( *cmd )
|
80
|
-
cmd.flatten!
|
81
78
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
trace( cmd )
|
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}
|
86
82
|
end
|
87
83
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
84
|
+
|
85
|
+
### Run the specified command +cmd+ with system(), failing if the execution
|
86
|
+
### fails.
|
87
|
+
def run( *cmd )
|
88
|
+
cmd.flatten!
|
89
|
+
|
90
|
+
if cmd.length > 1
|
91
|
+
trace( quotelist(*cmd) )
|
92
|
+
else
|
93
|
+
trace( cmd )
|
94
|
+
end
|
95
|
+
|
96
|
+
if $dryrun
|
97
|
+
$stderr.puts "(dry run mode)"
|
98
|
+
else
|
99
|
+
system( *cmd )
|
100
|
+
unless $?.success?
|
101
|
+
fail "Command failed: [%s]" % [cmd.join(' ')]
|
102
|
+
end
|
94
103
|
end
|
95
104
|
end
|
96
|
-
end
|
97
105
|
|
98
106
|
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
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
|
106
114
|
|
107
115
|
|
108
|
-
### Run a subordinate Rake process with the same options and the specified +targets+.
|
109
|
-
def rake( *targets )
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
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
|
114
122
|
|
115
123
|
|
116
|
-
### Open a pipe to a process running the given +cmd+ and call the given block with it.
|
117
|
-
def pipeto( *cmd )
|
118
|
-
|
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
|
119
127
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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|
|
126
134
|
|
127
|
-
|
128
|
-
|
129
|
-
|
135
|
+
# Parent
|
136
|
+
if io
|
137
|
+
yield( io )
|
130
138
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
139
|
+
# Child
|
140
|
+
else
|
141
|
+
exec( *cmd )
|
142
|
+
fail "Command failed: [%s]" % [cmd.join(' ')]
|
143
|
+
end
|
135
144
|
end
|
136
145
|
end
|
137
146
|
end
|
138
|
-
end
|
139
147
|
|
140
148
|
|
141
|
-
### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
|
142
|
-
def download( sourceuri, targetfile=nil )
|
143
|
-
|
144
|
-
|
145
|
-
|
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'
|
146
154
|
|
147
|
-
|
155
|
+
targetpath = Pathname.new( targetfile )
|
148
156
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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 = ''
|
156
164
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
165
|
+
while ifh.read( 16384, buf )
|
166
|
+
until buf.empty?
|
167
|
+
bytes = ofh.write( buf )
|
168
|
+
buf.slice!( 0, bytes )
|
169
|
+
end
|
161
170
|
end
|
171
|
+
|
172
|
+
log "Done."
|
162
173
|
end
|
163
174
|
|
164
|
-
log "Done."
|
165
175
|
end
|
166
176
|
|
177
|
+
return targetpath
|
178
|
+
ensure
|
179
|
+
$stdout.sync = oldsync
|
167
180
|
end
|
168
181
|
|
169
|
-
return targetpath
|
170
|
-
ensure
|
171
|
-
$stdout.sync = oldsync
|
172
|
-
end
|
173
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
|
174
190
|
|
175
|
-
### Return the fully-qualified path to the specified +program+ in the PATH.
|
176
|
-
def which( program )
|
177
|
-
return nil unless ENV['PATH']
|
178
|
-
ENV['PATH'].split(/:/).
|
179
|
-
collect {|dir| Pathname.new(dir) + program }.
|
180
|
-
find {|path| path.exist? && path.executable? }
|
181
|
-
end
|
182
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(';')
|
183
200
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
|
191
|
-
attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
|
192
|
-
|
193
|
-
# $stderr.puts " attr is: %p" % [attributes]
|
194
|
-
if attributes.empty?
|
195
|
-
return ''
|
196
|
-
else
|
197
|
-
return "\e[%sm" % attributes
|
201
|
+
# $stderr.puts " attr is: %p" % [attributes]
|
202
|
+
if attributes.empty?
|
203
|
+
return ''
|
204
|
+
else
|
205
|
+
return "\e[%sm" % attributes
|
206
|
+
end
|
198
207
|
end
|
199
|
-
end
|
200
208
|
|
201
209
|
|
202
|
-
### Colorize the given +string+ with the specified +attributes+ and return it, handling
|
203
|
-
### line-endings, color reset, etc.
|
204
|
-
def colorize( *args )
|
205
|
-
|
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 = ''
|
214
|
+
|
215
|
+
if block_given?
|
216
|
+
string = yield
|
217
|
+
else
|
218
|
+
string = args.shift
|
219
|
+
end
|
220
|
+
|
221
|
+
ending = string[/(\s)$/] || ''
|
222
|
+
string = string.rstrip
|
206
223
|
|
207
|
-
|
208
|
-
string = yield
|
209
|
-
else
|
210
|
-
string = args.shift
|
224
|
+
return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
|
211
225
|
end
|
212
226
|
|
213
|
-
ending = string[/(\s)$/] || ''
|
214
|
-
string = string.rstrip
|
215
227
|
|
216
|
-
|
217
|
-
|
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
|
218
234
|
|
219
235
|
|
220
|
-
###
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
end
|
225
|
-
alias :error :error_message
|
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
|
226
240
|
|
227
241
|
|
228
|
-
###
|
229
|
-
|
230
|
-
|
231
|
-
|
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?
|
232
260
|
|
261
|
+
return response
|
262
|
+
end
|
233
263
|
|
234
|
-
### Output the specified <tt>prompt_string</tt> as a prompt (in green) and
|
235
|
-
### return the user's input with leading and trailing spaces removed. If a
|
236
|
-
### test is provided, the prompt will repeat until the test returns true.
|
237
|
-
### An optional failure message can also be passed in.
|
238
|
-
def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
|
239
|
-
prompt_string.chomp!
|
240
|
-
prompt_string << ":" unless /\W$/.match( prompt_string )
|
241
|
-
response = nil
|
242
|
-
|
243
|
-
begin
|
244
|
-
prompt = make_prompt_string( prompt_string )
|
245
|
-
response = readline( prompt ) || ''
|
246
|
-
response.strip!
|
247
|
-
if block_given? && ! yield( response )
|
248
|
-
error_message( failure_msg + "\n\n" )
|
249
|
-
response = nil
|
250
|
-
end
|
251
|
-
end while response.nil?
|
252
264
|
|
253
|
-
|
254
|
-
|
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
|
255
271
|
|
272
|
+
begin
|
273
|
+
default ||= '~'
|
274
|
+
response = prompt( "%s [%s]" % [ prompt_string, default ] )
|
275
|
+
response = default.to_s if !response.nil? && response.empty?
|
256
276
|
|
257
|
-
|
258
|
-
### substituting the given <tt>default</tt> if the user doesn't input
|
259
|
-
### anything. If a test is provided, the prompt will repeat until the test
|
260
|
-
### returns true. An optional failure message can also be passed in.
|
261
|
-
def prompt_with_default( prompt_string, default, failure_msg="Try again." )
|
262
|
-
response = nil
|
277
|
+
trace "Validating response %p" % [ response ]
|
263
278
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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?
|
268
287
|
|
269
|
-
|
288
|
+
return nil if response == '~'
|
289
|
+
return response
|
290
|
+
end
|
270
291
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
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 ]
|
277
298
|
end
|
278
|
-
end while response.nil?
|
279
299
|
|
280
|
-
|
281
|
-
|
282
|
-
end
|
300
|
+
results = []
|
301
|
+
result = nil
|
283
302
|
|
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?
|
284
311
|
|
285
|
-
|
286
|
-
def prompt_for_multiple_values( label, default=nil )
|
287
|
-
$stderr.puts( MULTILINE_PROMPT % [label] )
|
288
|
-
if default
|
289
|
-
$stderr.puts "Enter a single blank line to keep the default:\n %p" % [ default ]
|
312
|
+
return results.flatten
|
290
313
|
end
|
291
314
|
|
292
|
-
results = []
|
293
|
-
result = nil
|
294
315
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
results << default if default && results.empty?
|
299
|
-
else
|
300
|
-
results << result
|
301
|
-
end
|
302
|
-
end until result.nil? || result.empty?
|
316
|
+
### Turn echo and masking of input on/off.
|
317
|
+
def noecho( masked=false )
|
318
|
+
require 'termios'
|
303
319
|
|
304
|
-
|
305
|
-
|
320
|
+
rval = nil
|
321
|
+
term = Termios.getattr( $stdin )
|
306
322
|
|
323
|
+
begin
|
324
|
+
newt = term.dup
|
325
|
+
newt.c_lflag &= ~Termios::ECHO
|
326
|
+
newt.c_lflag &= ~Termios::ICANON if masked
|
307
327
|
|
308
|
-
|
309
|
-
def noecho( masked=false )
|
310
|
-
require 'termios'
|
328
|
+
Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
|
311
329
|
|
312
|
-
|
313
|
-
|
330
|
+
rval = yield
|
331
|
+
ensure
|
332
|
+
Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
|
333
|
+
end
|
314
334
|
|
315
|
-
|
316
|
-
|
317
|
-
newt.c_lflag &= ~Termios::ECHO
|
318
|
-
newt.c_lflag &= ~Termios::ICANON if masked
|
335
|
+
return rval
|
336
|
+
end
|
319
337
|
|
320
|
-
Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
|
321
338
|
|
322
|
-
|
323
|
-
|
324
|
-
|
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
|
325
346
|
end
|
326
347
|
|
327
|
-
return rval
|
328
|
-
end
|
329
348
|
|
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
|
330
366
|
|
331
|
-
|
332
|
-
### available.
|
333
|
-
def prompt_for_password( prompt="Password: " )
|
334
|
-
return noecho( true ) do
|
335
|
-
$stderr.print( prompt )
|
336
|
-
($stdin.gets || '').chomp
|
367
|
+
return false
|
337
368
|
end
|
338
|
-
|
369
|
+
alias :prompt_for_confirmation :ask_for_confirmation
|
339
370
|
|
340
371
|
|
341
|
-
###
|
342
|
-
###
|
343
|
-
###
|
344
|
-
|
345
|
-
|
346
|
-
puts description
|
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
|
347
377
|
|
348
|
-
|
349
|
-
|
350
|
-
|
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
|
351
384
|
|
352
|
-
|
353
|
-
return yield
|
354
|
-
elsif abort_on_decline
|
355
|
-
error "Aborted."
|
356
|
-
fail
|
385
|
+
return rval
|
357
386
|
end
|
358
387
|
|
359
|
-
return false
|
360
|
-
end
|
361
|
-
alias :prompt_for_confirmation :ask_for_confirmation
|
362
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 = []
|
363
396
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
def find_pattern_in_file( regexp, file )
|
368
|
-
rval = nil
|
397
|
+
log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
|
398
|
+
Open3.popen3( *cmd ) do |stdin, stdout, stderr|
|
399
|
+
stdin.close
|
369
400
|
|
370
|
-
|
371
|
-
|
372
|
-
rval = match.captures.empty? ? match[0] : match.captures
|
373
|
-
break
|
401
|
+
output << stdout.gets until stdout.eof?
|
402
|
+
output << stderr.gets until stderr.eof?
|
374
403
|
end
|
375
|
-
end
|
376
404
|
|
377
|
-
|
378
|
-
|
405
|
+
result = output.find { |line| regexp.match(line) }
|
406
|
+
return $1 || result
|
407
|
+
end
|
379
408
|
|
380
409
|
|
381
|
-
###
|
382
|
-
###
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
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
|
387
419
|
|
388
|
-
log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
|
389
|
-
Open3.popen3( *cmd ) do |stdin, stdout, stderr|
|
390
|
-
stdin.close
|
391
420
|
|
392
|
-
|
393
|
-
|
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
|
394
425
|
end
|
395
426
|
|
396
|
-
result = output.find { |line| regexp.match(line) }
|
397
|
-
return $1 || result
|
398
|
-
end
|
399
427
|
|
428
|
+
### Log a subdirectory change, execute a block, and exit the subdirectory
|
429
|
+
def in_subdirectory( subdir )
|
430
|
+
block = Proc.new
|
400
431
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
|
405
|
-
system editor, filename
|
406
|
-
unless $?.success? || editor =~ /vim/i
|
407
|
-
fail "Editor exited uncleanly."
|
432
|
+
log "Entering #{subdir}"
|
433
|
+
Dir.chdir( subdir, &block )
|
434
|
+
log "Leaving #{subdir}"
|
408
435
|
end
|
409
|
-
end
|
410
436
|
|
411
437
|
|
412
|
-
###
|
413
|
-
def
|
414
|
-
|
415
|
-
|
416
|
-
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
|
417
442
|
|
418
443
|
|
419
|
-
###
|
420
|
-
|
421
|
-
|
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?
|
422
450
|
|
423
|
-
|
424
|
-
Dir.chdir( subdir, &block )
|
425
|
-
log "Leaving #{subdir}"
|
426
|
-
end
|
451
|
+
reader = Archive::Tar::Reader.new( tarfile.to_s, TAR_OPTS )
|
427
452
|
|
453
|
+
mkdir_p( targetdir )
|
454
|
+
reader.each( true ) do |header, body|
|
455
|
+
path = targetdir + header[:path]
|
456
|
+
# trace "Header is: %p" % [ header ]
|
428
457
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
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
|
433
499
|
|
500
|
+
end # module Rakefile::Helpers
|
434
501
|
|
435
502
|
|