treequel 1.0.1 → 1.0.4

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.
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