treequel 1.0.1 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/ChangeLog +176 -14
  2. data/LICENSE +1 -1
  3. data/Rakefile +61 -45
  4. data/Rakefile.local +20 -0
  5. data/bin/treequel +502 -269
  6. data/examples/ldap-rack-auth.rb +2 -0
  7. data/lib/treequel.rb +221 -18
  8. data/lib/treequel/branch.rb +410 -201
  9. data/lib/treequel/branchcollection.rb +25 -13
  10. data/lib/treequel/branchset.rb +42 -40
  11. data/lib/treequel/constants.rb +233 -3
  12. data/lib/treequel/control.rb +95 -0
  13. data/lib/treequel/controls/contentsync.rb +138 -0
  14. data/lib/treequel/controls/pagedresults.rb +162 -0
  15. data/lib/treequel/controls/sortedresults.rb +216 -0
  16. data/lib/treequel/directory.rb +212 -65
  17. data/lib/treequel/exceptions.rb +11 -12
  18. data/lib/treequel/filter.rb +1 -12
  19. data/lib/treequel/mixins.rb +83 -47
  20. data/lib/treequel/monkeypatches.rb +29 -0
  21. data/lib/treequel/schema.rb +23 -19
  22. data/lib/treequel/schema/attributetype.rb +33 -3
  23. data/lib/treequel/schema/ldapsyntax.rb +0 -11
  24. data/lib/treequel/schema/matchingrule.rb +0 -11
  25. data/lib/treequel/schema/matchingruleuse.rb +0 -11
  26. data/lib/treequel/schema/objectclass.rb +36 -10
  27. data/lib/treequel/schema/table.rb +159 -0
  28. data/lib/treequel/sequel_integration.rb +7 -7
  29. data/lib/treequel/utils.rb +4 -66
  30. data/rake/documentation.rb +89 -0
  31. data/rake/helpers.rb +375 -307
  32. data/rake/hg.rb +16 -2
  33. data/rake/manual.rb +11 -6
  34. data/rake/packaging.rb +20 -35
  35. data/rake/publishing.rb +22 -62
  36. data/spec/lib/constants.rb +20 -0
  37. data/spec/lib/control_behavior.rb +44 -0
  38. data/spec/lib/matchers.rb +51 -0
  39. data/spec/treequel/branch_spec.rb +88 -29
  40. data/spec/treequel/branchcollection_spec.rb +24 -1
  41. data/spec/treequel/branchset_spec.rb +123 -51
  42. data/spec/treequel/control_spec.rb +48 -0
  43. data/spec/treequel/controls/contentsync_spec.rb +38 -0
  44. data/spec/treequel/controls/pagedresults_spec.rb +138 -0
  45. data/spec/treequel/controls/sortedresults_spec.rb +171 -0
  46. data/spec/treequel/directory_spec.rb +186 -16
  47. data/spec/treequel/mixins_spec.rb +42 -3
  48. data/spec/treequel/schema/attributetype_spec.rb +22 -20
  49. data/spec/treequel/schema/objectclass_spec.rb +67 -46
  50. data/spec/treequel/schema/table_spec.rb +134 -0
  51. data/spec/treequel_spec.rb +277 -15
  52. metadata +89 -108
  53. data/bin/treequel.orig +0 -963
  54. data/examples/ldap-monitor.rb +0 -143
  55. data/examples/ldap-monitor/public/css/master.css +0 -328
  56. data/examples/ldap-monitor/public/images/card_small.png +0 -0
  57. data/examples/ldap-monitor/public/images/chain_small.png +0 -0
  58. data/examples/ldap-monitor/public/images/globe_small.png +0 -0
  59. data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
  60. data/examples/ldap-monitor/public/images/plug.png +0 -0
  61. data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
  62. data/examples/ldap-monitor/public/images/tick.png +0 -0
  63. data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
  64. data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
  65. data/examples/ldap-monitor/views/backends.erb +0 -41
  66. data/examples/ldap-monitor/views/connections.erb +0 -74
  67. data/examples/ldap-monitor/views/databases.erb +0 -39
  68. data/examples/ldap-monitor/views/dump_subsystem.erb +0 -14
  69. data/examples/ldap-monitor/views/index.erb +0 -14
  70. data/examples/ldap-monitor/views/layout.erb +0 -35
  71. data/examples/ldap-monitor/views/listeners.erb +0 -30
  72. data/rake/rdoc.rb +0 -30
  73. data/rake/win32.rb +0 -190
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,416 +19,484 @@ rescue LoadError
19
19
  end
20
20
  end
21
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
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
- if cmd.length > 1
83
- trace( quotelist(*cmd) )
84
- else
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
- if $dryrun
89
- $stderr.puts "(dry run mode)"
90
- else
91
- system( *cmd )
92
- unless $?.success?
93
- fail "Command failed: [%s]" % [cmd.join(' ')]
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
- trace "Reading output from: %s" % [ cmd, quotelist(cmd, *args) ]
103
- output = IO.read( '|-' ) or exec cmd, *args
104
- return output
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
- opts = ARGV.select {|arg| arg[0,1] == '-' }
111
- args = opts + targets.map {|t| t.to_s }
112
- run 'rake', '-N', *args
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
- $DEBUG = true
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
- 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|
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
- # Parent
128
- if io
129
- yield( io )
135
+ # Parent
136
+ if io
137
+ yield( io )
130
138
 
131
- # Child
132
- else
133
- exec( *cmd )
134
- fail "Command failed: [%s]" % [cmd.join(' ')]
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
- oldsync = $stdout.sync
144
- $stdout.sync = true
145
- require 'open-uri'
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
- targetpath = Pathname.new( targetfile )
155
+ targetpath = Pathname.new( targetfile )
148
156
 
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 = ''
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
- while ifh.read( 16384, buf )
158
- until buf.empty?
159
- bytes = ofh.write( buf )
160
- buf.slice!( 0, bytes )
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
- ENV['PATH'].split(/:/).
178
- collect {|dir| Pathname.new(dir) + program }.
179
- find {|path| path.exist? && path.executable? }
180
- end
181
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(';')
182
200
 
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
201
+ # $stderr.puts " attr is: %p" % [attributes]
202
+ if attributes.empty?
203
+ return ''
204
+ else
205
+ return "\e[%sm" % attributes
206
+ end
197
207
  end
198
- end
199
208
 
200
209
 
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 = ''
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
205
223
 
206
- if block_given?
207
- string = yield
208
- else
209
- string = args.shift
224
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
210
225
  end
211
226
 
212
- ending = string[/(\s)$/] || ''
213
- string = string.rstrip
214
227
 
215
- return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
216
- end
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
217
234
 
218
235
 
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
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
225
240
 
226
241
 
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
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?
231
260
 
261
+ return response
262
+ end
232
263
 
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
264
 
252
- return response
253
- end
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
254
271
 
272
+ begin
273
+ default ||= '~'
274
+ response = prompt( "%s [%s]" % [ prompt_string, default ] )
275
+ response = default.to_s if !response.nil? && response.empty?
255
276
 
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
277
+ trace "Validating response %p" % [ response ]
262
278
 
263
- begin
264
- default ||= '~'
265
- response = prompt( "%s [%s]" % [ prompt_string, default ] )
266
- response = default.to_s if !response.nil? && response.empty?
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?
267
287
 
268
- trace "Validating response %p" % [ response ]
288
+ return nil if response == '~'
289
+ return response
290
+ end
269
291
 
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
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 ]
276
298
  end
277
- end while response.nil?
278
299
 
279
- return nil if response == '~'
280
- return response
281
- end
300
+ results = []
301
+ result = nil
282
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?
283
311
 
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 ]
312
+ return results.flatten
289
313
  end
290
314
 
291
- results = []
292
- result = nil
293
315
 
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?
316
+ ### Turn echo and masking of input on/off.
317
+ def noecho( masked=false )
318
+ require 'termios'
302
319
 
303
- return results.flatten
304
- end
320
+ rval = nil
321
+ term = Termios.getattr( $stdin )
305
322
 
323
+ begin
324
+ newt = term.dup
325
+ newt.c_lflag &= ~Termios::ECHO
326
+ newt.c_lflag &= ~Termios::ICANON if masked
306
327
 
307
- ### Turn echo and masking of input on/off.
308
- def noecho( masked=false )
309
- require 'termios'
328
+ Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
310
329
 
311
- rval = nil
312
- term = Termios.getattr( $stdin )
330
+ rval = yield
331
+ ensure
332
+ Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
333
+ end
313
334
 
314
- begin
315
- newt = term.dup
316
- newt.c_lflag &= ~Termios::ECHO
317
- newt.c_lflag &= ~Termios::ICANON if masked
335
+ return rval
336
+ end
318
337
 
319
- Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
320
338
 
321
- rval = yield
322
- ensure
323
- Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
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
324
346
  end
325
347
 
326
- return rval
327
- end
328
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
329
366
 
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
367
+ return false
336
368
  end
337
- end
369
+ alias :prompt_for_confirmation :ask_for_confirmation
338
370
 
339
371
 
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
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
346
377
 
347
- answer = prompt_with_default( "Continue?", 'n' ) do |input|
348
- input =~ /^[yn]/i
349
- end
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
350
384
 
351
- if answer =~ /^y/i
352
- return yield
353
- elsif abort_on_decline
354
- error "Aborted."
355
- fail
385
+ return rval
356
386
  end
357
387
 
358
- return false
359
- end
360
- alias :prompt_for_confirmation :ask_for_confirmation
361
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 = []
362
396
 
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
397
+ log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
398
+ Open3.popen3( *cmd ) do |stdin, stdout, stderr|
399
+ stdin.close
368
400
 
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
401
+ output << stdout.gets until stdout.eof?
402
+ output << stderr.gets until stderr.eof?
373
403
  end
374
- end
375
404
 
376
- return rval
377
- end
405
+ result = output.find { |line| regexp.match(line) }
406
+ return $1 || result
407
+ end
378
408
 
379
409
 
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 = []
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
386
419
 
387
- log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
388
- Open3.popen3( *cmd ) do |stdin, stdout, stderr|
389
- stdin.close
390
420
 
391
- output << stdout.gets until stdout.eof?
392
- output << stderr.gets until stderr.eof?
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
393
425
  end
394
426
 
395
- result = output.find { |line| regexp.match(line) }
396
- return $1 || result
397
- end
398
427
 
428
+ ### Log a subdirectory change, execute a block, and exit the subdirectory
429
+ def in_subdirectory( subdir )
430
+ block = Proc.new
399
431
 
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."
432
+ log "Entering #{subdir}"
433
+ Dir.chdir( subdir, &block )
434
+ log "Leaving #{subdir}"
407
435
  end
408
- end
409
436
 
410
437
 
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
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
416
442
 
417
443
 
418
- ### Log a subdirectory change, execute a block, and exit the subdirectory
419
- def in_subdirectory( subdir )
420
- block = Proc.new
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?
421
450
 
422
- log "Entering #{subdir}"
423
- Dir.chdir( subdir, &block )
424
- log "Leaving #{subdir}"
425
- end
451
+ reader = Archive::Tar::Reader.new( tarfile.to_s, TAR_OPTS )
426
452
 
453
+ mkdir_p( targetdir )
454
+ reader.each( true ) do |header, body|
455
+ path = targetdir + header[:path]
456
+ # trace "Header is: %p" % [ header ]
427
457
 
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
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
432
499
 
500
+ end # module Rakefile::Helpers
433
501
 
434
502