rsql 0.2.11 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/rsql CHANGED
@@ -63,11 +63,13 @@ ARGV.map!{|a| a.sub(/^--/,'-')}
63
63
  if i = ARGV.index('-rc')
64
64
  ARGV.delete_at(i)
65
65
  rc_fn = ARGV.delete_at(i)
66
- else
66
+ elsif ENV['HOME']
67
67
  rc_fn = File.join(ENV['HOME'], ".#{bn}rc")
68
68
  end
69
69
 
70
- eval_context.load(rc_fn, :skip_init_registrations) if File.exists?(rc_fn)
70
+ if rc_fn && File.exists?(rc_fn)
71
+ eval_context.load(rc_fn, :skip_init_registrations)
72
+ end
71
73
 
72
74
  def get_password(prompt)
73
75
  STDOUT.print(prompt)
@@ -160,10 +162,14 @@ if i = ARGV.index('-ssh')
160
162
  (opts.ssh_host, opts.ssh_user, opts.ssh_password, opts.ssh_port) =
161
163
  split_login(ARGV.delete_at(i))
162
164
 
163
- default_config = File.join(ENV['HOME'], '.ssh', 'config')
164
- if File.exists?(default_config)
165
- opts.ssh_config = default_config
166
- opts.ssh_cfghash = Net::SSH::Config.load(default_config, opts.ssh_host)
165
+ opts.ssh_cfghash = {}
166
+
167
+ if ENV['HOME']
168
+ default_config = File.join(ENV['HOME'], '.ssh', 'config')
169
+ if File.exists?(default_config)
170
+ opts.ssh_config = default_config
171
+ opts.ssh_cfghash = Net::SSH::Config.load(default_config, opts.ssh_host)
172
+ end
167
173
  end
168
174
 
169
175
  if opts.ssh_user.nil? || opts.ssh_user.empty?
@@ -237,7 +243,7 @@ MySQLResults.database_name = ARGV.shift
237
243
 
238
244
  if !$stdin.tty? && opts.batch_input.nil?
239
245
  # accept commands from stdin
240
- opts.batch_input = $stdin.read.gsub(/\r?\n/,';')
246
+ opts.batch_input = $stdin.read
241
247
  end
242
248
 
243
249
  # make sure we remove any duplicates when we add to the history to
@@ -381,7 +387,8 @@ if opts.ssh_host
381
387
  ipc_state = ''
382
388
  15.times do
383
389
  sleep(1)
384
- ipc_state = IO.read(ipc_fn).strip
390
+ # if the fork fails, it'll call our at_exit which removes the ipc file
391
+ ipc_state = File.exists?(ipc_fn) ? IO.read(ipc_fn).strip : 'fail'
385
392
  break if ipc_state == 'ready' || ipc_state == 'fail'
386
393
  end
387
394
 
@@ -428,9 +435,11 @@ rescue Exception => ex
428
435
  exit 1
429
436
  end
430
437
 
431
- history_fn = File.join(ENV['HOME'], ".#{bn}_history")
432
- if File.exists?(history_fn) && 0 < File.size(history_fn)
433
- YAML.load_file(history_fn).each {|i| Readline::HISTORY.push(i)}
438
+ if ENV['HOME']
439
+ history_fn = File.join(ENV['HOME'], ".#{bn}_history")
440
+ if File.exists?(history_fn) && 0 < File.size(history_fn)
441
+ YAML.load_file(history_fn).each {|i| Readline::HISTORY.push(i)}
442
+ end
434
443
  end
435
444
 
436
445
  Readline.completion_proc = eval_context.method(:complete)
@@ -515,6 +524,7 @@ end
515
524
 
516
525
  child_pids << kill_pid
517
526
  opts.delete_field(:mysql_password)
527
+ # fixme: consider emulating a shell prompt and have this cancel any current cmd line
518
528
  Signal.trap('INT', nil)
519
529
 
520
530
  Signal.trap('CHLD') do
@@ -544,7 +554,7 @@ end
544
554
 
545
555
  sleep(0.3)
546
556
 
547
- if Readline::HISTORY.any?
557
+ if Readline::HISTORY.any? && history_fn
548
558
  if 100 < Readline::HISTORY.size
549
559
  (Readline::HISTORY.size - 100).times do |i|
550
560
  Readline::HISTORY.delete_at(i)
@@ -5,7 +5,25 @@
5
5
  # information.
6
6
  #
7
7
  module RSQL
8
- VERSION = '0.2.11'
8
+ VERSION = [0,3,1]
9
+
10
+ def VERSION.to_s
11
+ self.join('.')
12
+ end
13
+
14
+ # Set up our version to be comparable to version strings.
15
+ #
16
+ VERSION.extend(Comparable)
17
+ def VERSION.eql?(version)
18
+ self.<=>(version) == 0
19
+ end
20
+ def VERSION.<=>(version)
21
+ version = version.split('.').map!{|v|v.to_i}
22
+ r = self[0] <=> version[0]
23
+ r = self[1] <=> version[1] if r == 0
24
+ r = self[2] <=> version[2] if r == 0
25
+ r
26
+ end
9
27
 
10
28
  require 'rsql/mysql_results'
11
29
  require 'rsql/eval_context'
@@ -21,6 +21,16 @@
21
21
 
22
22
  module RSQL
23
23
 
24
+ # FIXME:
25
+ #
26
+ # select Count(*) from BcheckResults where LastResult != 'Succeeded' and LastResultTime > DATE_SUB(UTC_TIMESTAMP, INTERVAL 1 DAY);
27
+ # NoMethodError: undefined method `+' for nil:NilClass
28
+ # /usr/lib/ruby/gems/1.8/gems/rsql-0.2.11/lib/rsql/commands.rb:82:in `initialize'
29
+ # /usr/lib/ruby/gems/1.8/gems/rsql-0.2.11/bin/rsql:535:in `join'
30
+ # /usr/lib/ruby/gems/1.8/gems/rsql-0.2.11/bin/rsql:535
31
+ # /bin/rsql:19:in `load'
32
+ # /bin/rsql:19
33
+
24
34
  require 'stringio'
25
35
 
26
36
  EvalResults = Struct.new(:value, :stdout)
@@ -125,7 +125,7 @@ module RSQL
125
125
  if Exception === ret
126
126
  @loaded_fns_state[fn] = :failed
127
127
  if @verbose
128
- $stderr.puts("#{ex.class}: #{ex.message}", ex.backtrace)
128
+ $stderr.puts("#{ret.class}: #{ret.message}", ex.backtrace)
129
129
  else
130
130
  bt = ret.backtrace.collect{|line| line.start_with?(fn) ? line : nil}.compact
131
131
  $stderr.puts("#{ret.class}: #{ret.message}", bt, '')
@@ -513,15 +513,57 @@ EOF
513
513
  nil
514
514
  end
515
515
 
516
- # Call MySQLResults#grep to remove (or show) only those lines that
517
- # have content matching the patterrn.
516
+ # Remove all rows that do NOT match the expression. Returns true if
517
+ # any matches were found.
518
518
  #
519
- def grep(*args) # :doc:
520
- if @results.grep(*args)
521
- @results
519
+ # Options:
520
+ # :fixed => indicates that the string should be escaped of any
521
+ # special characters
522
+ # :nocolor => will not add color escape codes to indicate the
523
+ # match
524
+ # :inverse => reverses the regular expression match
525
+ #
526
+ def grep(pattern, *gopts) # :doc:
527
+ nocolor = gopts.include?(:nocolor)
528
+
529
+ if inverted = gopts.include?(:inverse)
530
+ # there's no point in coloring matches we are removing
531
+ nocolor = true
532
+ end
533
+
534
+ if gopts.include?(:fixed)
535
+ regexp = Regexp.new(/#{Regexp.escape(pattern.to_str)}/)
536
+ elsif Regexp === pattern
537
+ regexp = pattern
522
538
  else
523
- $stderr.puts 'No matches found'
539
+ regexp = Regexp.new(/#{pattern.to_str}/)
540
+ end
541
+
542
+ rval = inverted
543
+
544
+ @results.delete_if do |row|
545
+ matched = false
546
+ row.each do |val|
547
+ val = val.to_s unless String === val
548
+ if nocolor
549
+ if matched = !val.match(regexp).nil?
550
+ rval = inverted ? false : true
551
+ break
552
+ end
553
+ else
554
+ # in the color case, we want to colorize all hits in
555
+ # all columns, so we can't early terminate our
556
+ # search
557
+ if val.gsub!(regexp){|m| "\e[31;1m#{m}\e[0m"}
558
+ matched = true
559
+ rval = inverted ? false : true
560
+ end
561
+ end
562
+ end
563
+ inverted ? matched : !matched
524
564
  end
565
+
566
+ return rval
525
567
  end
526
568
 
527
569
  # Register bangs to evaluate on all displayers as long as a column
@@ -63,8 +63,8 @@ module RSQL
63
63
  #
64
64
  def conn; @@conn; end
65
65
 
66
- # Set the underlying MySQL connection object to use which
67
- # implicitly resets the name cache.
66
+ # Set the underlying MySQL connection object to use which implicitly
67
+ # resets the name cache.
68
68
  #
69
69
  def conn=(conn)
70
70
  if @@conn = conn
@@ -73,23 +73,21 @@ module RSQL
73
73
  reset_cache
74
74
  end
75
75
 
76
- # Get the field separator to use when writing rows in
77
- # columns.
76
+ # Get the field separator to use when writing rows in columns.
78
77
  #
79
78
  def field_separator; @@field_separator; end
80
79
 
81
- # Set the field separator to use when writing rows in
82
- # columns.
80
+ # Set the field separator to use when writing rows in columns.
83
81
  #
84
82
  def field_separator=(sep); @@field_separator = sep; end
85
83
 
86
- # Get the maximum number of rows to process before
87
- # throwing a MaxRowsException.
84
+ # Get the maximum number of rows to process before throwing a
85
+ # MaxRowsException.
88
86
  #
89
87
  def max_rows; @@max_rows; end
90
88
 
91
- # Set the maximum number of rows to process before
92
- # throwing a MaxRowsException.
89
+ # Set the maximum number of rows to process before throwing a
90
+ # MaxRowsException.
93
91
  #
94
92
  def max_rows=(cnt); @@max_rows = cnt; end
95
93
 
@@ -131,8 +129,8 @@ module RSQL
131
129
  @@name_cache.keys.sort
132
130
  end
133
131
 
134
- # Get the list of tables available for the current
135
- # database or a specific one.
132
+ # Get the list of tables available for the current database or a
133
+ # specific one.
136
134
  #
137
135
  def tables(database=@@database_name)
138
136
  @@name_cache[database] || []
@@ -206,7 +204,7 @@ module RSQL
206
204
  @@conn.select_db(@@database_name) if @@database_name
207
205
  end
208
206
 
209
- @@history.shift if @@max_history <= @@history.size
207
+ @@history.shift if @@max_history <= @@history.size
210
208
  @@history << sql
211
209
 
212
210
  start = Time.now.to_f
@@ -215,80 +213,115 @@ module RSQL
215
213
 
216
214
  affected_rows = @@conn.affected_rows
217
215
  unless results && 0 < results.num_rows
218
- return new(sql, elapsed, affected_rows)
216
+ return new(nil, nil, affected_rows, sql, elapsed)
219
217
  end
220
218
 
221
219
  if max_rows < results.num_rows
222
220
  raise MaxRowsException.new(results.num_rows, max_rows)
223
221
  end
224
222
 
225
- # extract mysql results into our own table so we can predetermine the
226
- # lengths of columns and give users a chance to reformat column data
227
- # before it's displayed (via the bang maps)
223
+ # extract mysql results into our own table so we can
224
+ # predetermine the lengths of columns and give users a chance to
225
+ # reformat column data before it's displayed (via the bang maps)
228
226
 
229
227
  fields = results.fetch_fields
230
- fields.collect! do |field|
231
- def field.longest_length=(len); @longest_length = len; end
232
- def field.longest_length; @longest_length; end
233
- field.longest_length = field.name.length
234
- field
235
- end
228
+ extend_fields!(fields)
236
229
 
237
230
  results_table = []
238
231
  while vals = results.fetch_row
239
232
  row = []
240
233
  fields.each_with_index do |field, i|
241
- if raw
242
- val = vals[i]
243
- else
244
- val = eval_context.bang_eval(field.name, vals[i])
234
+ val = vals[i]
235
+ orig_vlen = val.respond_to?(:length) ? val.length : 0
236
+ if val && field.is_num?
237
+ val = field.decimals == 0 ? val.to_i : val.to_f
238
+ end
239
+ unless raw
240
+ val = eval_context.bang_eval(field.name, val)
245
241
  if val.nil?
246
242
  val = 'NULL'
247
243
  elsif HEX_RANGE.include?(field.type) && val =~ /[^[:print:]\s]/
248
244
  val = eval_context.to_hexstr(val)
249
245
  end
250
246
  end
251
- vlen = val.respond_to?(:length) ? val.length : 0
252
- if field.longest_length < vlen
253
- if String === val
254
- # consider only the longest line length since some
255
- # output contains multiple lines like "show create table"
256
- longest_line = val.split(/\r?\n/).collect{|l|l.length}.max
257
- if field.longest_length < longest_line
258
- field.longest_length = longest_line
259
- end
260
- else
261
- field.longest_length = val.length
262
- end
263
- end
247
+ update_longest!(field, val, orig_vlen)
264
248
  row << val
265
249
  end
266
250
  results_table << row
267
251
  end
268
252
 
269
- return new(sql, elapsed, affected_rows, fields, results_table)
253
+ return new(fields, results_table, affected_rows, sql, elapsed)
270
254
  end
271
255
 
256
+ ########################################
257
+ private
258
+
259
+ def extend_fields!(fields)
260
+ fields.collect! do |field|
261
+ def field.name; to_s; end unless field.respond_to?(:name)
262
+ def field.longest_length=(len); @longest_length = len; end
263
+ def field.longest_length; @longest_length; end
264
+ field.longest_length = field.name.length
265
+ field
266
+ end
267
+ end
268
+
269
+ def update_longest!(field, val, default_vlen=nil)
270
+ vlen = if val.respond_to?(:length)
271
+ val.length
272
+ elsif default_vlen
273
+ default_vlen
274
+ else
275
+ val.to_s.length
276
+ end
277
+
278
+ if field.longest_length < vlen
279
+ if String === val
280
+ # consider only the longest line length since some
281
+ # output contains multiple lines like "show create
282
+ # table"
283
+ longest_line = val.split(/\r?\n/).collect{|l|l.length}.max
284
+ if field.longest_length < longest_line
285
+ field.longest_length = longest_line
286
+ end
287
+ else
288
+ field.longest_length = vlen
289
+ end
290
+ end
291
+ end
292
+
272
293
  end # class << self
273
294
 
274
295
  ########################################
275
296
 
276
- def initialize(sql, elapsed, affected_rows,
277
- fields=nil, table=nil, field_separator=@@field_separator)
278
- @sql = sql;
279
- @elapsed = elapsed;
280
- @affected_rows = affected_rows;
297
+ def initialize(fields, table, affected_rows=nil, sql=nil, elapsed=nil,
298
+ field_separator=@@field_separator)
299
+
281
300
  @fields = fields
282
301
  @table = table
302
+ @affected_rows = affected_rows || table.size
303
+ @sql = sql
304
+ @elapsed = elapsed
283
305
  @field_separator = field_separator
284
306
 
285
- # we set this here so that (a) it occurs _after_ we are
286
- # successful and so we can show an appropriate messge in a
287
- # displayer
288
- if @sql.match(/use\s+(\S+)/i)
307
+ # we set this here so that it occurs _after_ we are successful and
308
+ # so we can show an appropriate messge in a displayer
309
+ if @sql && @sql.match(/use\s+(\S+)/i)
289
310
  @database_changed = true
290
311
  @@database_name = $1
291
312
  end
313
+
314
+ # if we're not given fields from a query we need to find the column
315
+ # widths
316
+ if @fields && @table && @fields.size > 0 &&
317
+ !@fields.first.respond_to?(:longest_length)
318
+ self.class.send(:extend_fields!, @fields)
319
+ @table.each do |row|
320
+ @fields.each_with_index do |field, i|
321
+ self.class.send(:update_longest!, field, row[i])
322
+ end
323
+ end
324
+ end
292
325
  end
293
326
 
294
327
  # Get the query associated with these results.
@@ -321,19 +354,33 @@ module RSQL
321
354
  @table ? @table.size : 0
322
355
  end
323
356
 
324
- # Get a row from the table hashed with the field names.
357
+ # Convenience helper to grab the value in the very first entry for those
358
+ # times when the query only has one expected value returned.
325
359
  #
326
- def [](index)
327
- if !@fields || !@table
328
- return nil
329
- end
330
- if row = @table[index]
331
- hash = {}
332
- @fields.each_with_index {|f,i| hash[f.name] = row[i]}
333
- return hash
334
- else
335
- return nil
360
+ def scalar
361
+ self[0,0]
362
+ end
363
+
364
+ # Get an entire row as a hash or a single value from the table of
365
+ # results. The value of row_j may be an integer index or a string column
366
+ # name.
367
+ #
368
+ def [](row_i, row_j=nil)
369
+ if @table
370
+ if row = @table[row_i]
371
+ if row_j
372
+ return row[row_j] if Integer === row_j
373
+ if row_j = @fields.index{|f| f.name == row_j}
374
+ return row[row_j]
375
+ end
376
+ else
377
+ hash = {}
378
+ @fields.each_with_index{|f,i| hash[f.name] = row[i]}
379
+ return hash
380
+ end
381
+ end
336
382
  end
383
+ return nil
337
384
  end
338
385
 
339
386
  # Iterate through each row of the table hashed with the field names.
@@ -348,59 +395,21 @@ module RSQL
348
395
  end
349
396
  end
350
397
 
351
- # Remove all rows that do NOT match the expression. Returns true if any
352
- # matches were found.
353
- #
354
- # Options:
355
- # :fixed => indicates that the string should be escaped of any special characters
356
- # :nocolor => will not add color escape codes to indicate the match
357
- # :inverse => reverses the regular expression match
398
+ # Conditionally delete rows from the results.
358
399
  #
359
- def grep(pattern, *gopts)
400
+ def delete_if(opts=nil, &block)
360
401
  if @table
361
- nocolor = gopts.include?(:nocolor)
362
-
363
- if inverted = gopts.include?(:inverse)
364
- # there's no point in coloring matches we are removing
365
- nocolor = true
366
- end
367
-
368
- if gopts.include?(:fixed)
369
- regexp = Regexp.new(/#{Regexp.escape(pattern.to_str)}/)
370
- elsif Regexp === pattern
371
- regexp = pattern
372
- else
373
- regexp = Regexp.new(/#{pattern.to_str}/)
374
- end
375
-
376
- rval = inverted
377
-
378
402
  @table.delete_if do |row|
379
- matched = false
380
- row.each do |val|
381
- val = val.to_str unless String === val
382
- if nocolor
383
- if matched = !val.match(regexp).nil?
384
- rval = inverted ? false : true
385
- break
386
- end
387
- else
388
- # in the color case, we want to colorize all hits in
389
- # all columns, so we can't early terminate our
390
- # search
391
- if val.gsub!(regexp){|m| "\e[31;1m#{m}\e[0m"}
392
- matched = true
393
- rval = inverted ? false : true
394
- end
395
- end
403
+ if opts == :row_hash
404
+ hash = {}
405
+ @fields.each_with_index{|f,i| hash[f.name] = row[i]}
406
+ yield(hash)
407
+ else
408
+ yield(row)
396
409
  end
397
- inverted ? matched : !matched
398
410
  end
399
-
400
- return rval
401
- else
402
- return false
403
411
  end
412
+ self
404
413
  end
405
414
 
406
415
  # Show a set of results in a decent fashion.
@@ -458,19 +467,20 @@ module RSQL
458
467
  # Show a summary line of the results.
459
468
  #
460
469
  def display_stats(io=$stdout, hdr='')
470
+ estr = @elapsed ? " (#{'%0.2f'%@elapsed} sec)" : ''
461
471
  if @table
462
472
  if @database_changed
463
473
  io.puts(hdr, "Database changed");
464
474
  hdr = ''
465
475
  end
466
476
  s = 1 == @table.size ? 'row' : 'rows'
467
- io.puts(hdr, "#{@table.size} #{s} in set (#{'%0.2f'%@elapsed} sec)")
477
+ io.puts(hdr, "#{@table.size} #{s} in set#{estr}")
468
478
  else
469
479
  if @database_changed
470
480
  io.puts(hdr, "Database changed");
471
481
  else
472
482
  s = 1 == @affected_rows ? 'row' : 'rows'
473
- io.puts(hdr, "Query OK, #{@affected_rows} #{s} affected (#{'%0.2f'%@elapsed} sec)")
483
+ io.puts(hdr, "Query OK, #{@affected_rows} #{s} affected#{estr}")
474
484
  end
475
485
  end
476
486
  end
@@ -179,4 +179,39 @@ class TestEvalContext < Test::Unit::TestCase
179
179
  File.unlink(tf+'~') if File.exists?(tf+'~')
180
180
  end
181
181
 
182
+ def test_grep
183
+ out = StringIO.new
184
+
185
+ res = [[11,'one$'],[12,'one and two'],[13,'four and none']]
186
+ @ctx.safe_eval('grep "two"', res, out)
187
+ assert(out.string.empty?)
188
+ assert_equal([[12,"one and \e[31;1mtwo\e[0m"]], res)
189
+
190
+ res = [[11,'one'],[12,'one and two'],[13,'four and none']]
191
+ out = StringIO.new
192
+ @ctx.safe_eval('grep /four/, :nocolor', res, out)
193
+ assert(out.string.empty?)
194
+ assert_equal([[13,'four and none']], res)
195
+
196
+ res = [[11,'one'],[12,'one and two'],[13,'four and none']]
197
+ out = StringIO.new
198
+ @ctx.safe_eval('grep "and", :inverse, :fixed', res, out)
199
+ assert(out.string.empty?)
200
+ assert_equal([[11,'one']], res)
201
+ end
202
+
203
+ def test_help
204
+ out = StringIO.new
205
+ @ctx.safe_eval('help', nil, out)
206
+ assert_match(/only rows/, out.string)
207
+ end
208
+
209
+ def test_exception
210
+ orig_err = $stderr
211
+ $stderr = err = StringIO.new
212
+ @ctx.safe_eval('this should fail', nil, nil)
213
+ $stderr = orig_err
214
+ assert_match(/test_eval_context.rb/, err.string)
215
+ end
216
+
182
217
  end # class TestEvalContext
@@ -76,9 +76,14 @@ class TestMySQLResults < Test::Unit::TestCase
76
76
  f1 = mock('f1')
77
77
  f1.expects(:name).returns('c1').times(12)
78
78
  f1.expects(:type).returns(1).times(2)
79
+ f1.stubs(:is_num?).returns(false)
80
+ f1.stubs(:longest_length)
81
+
79
82
  f2 = mock('f2')
80
83
  f2.expects(:name).returns('c2').times(11)
81
84
  f2.expects(:type).returns(1).times(2)
85
+ f2.stubs(:is_num?).returns(false)
86
+ f2.stubs(:longest_length)
82
87
 
83
88
  res = mock('results')
84
89
  res.expects(:num_rows).returns(2).times(2)
@@ -132,68 +137,31 @@ class TestMySQLResults < Test::Unit::TestCase
132
137
  dout.string.gsub(/\s+/,''))
133
138
  end
134
139
 
135
- def test_grep
136
- f1 = mock('f1')
137
- f1.stubs(:name).returns('c1')
138
- f1.stubs(:type).returns(1)
139
- f2 = mock('f2')
140
- f2.stubs(:name).returns('c2')
141
- f2.stubs(:type).returns(1)
142
-
143
- res = mock('results')
144
- res.stubs(:num_rows).returns(2)
145
- res.stubs(:fetch_fields).returns([f1,f2])
146
-
147
- rows = sequence(:rows)
148
- res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
149
- res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
150
- res.expects(:fetch_row).in_sequence(rows).returns(nil)
140
+ def test_init
141
+ mres = MySQLResults.new([], [])
142
+ assert_equal(0, mres.num_rows)
151
143
 
152
- conn = mock('Mysql')
153
- conn.expects(:reconnect=)
154
- conn.stubs(:list_dbs).returns([])
155
- conn.stubs(:query).with(instance_of(String)).returns(res)
156
- conn.stubs(:affected_rows).returns(1)
157
- conn.expects(:reconnected?).times(4)
158
- MySQLResults.reset_history
159
- MySQLResults.conn = conn
160
-
161
- mres = MySQLResults.query('ignored1', eval_context=nil, raw=true)
162
- assert_equal(false, mres.grep(/val/))
163
-
164
- rows = sequence(:rows)
165
- res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
166
- res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
167
- res.expects(:fetch_row).in_sequence(rows).returns(nil)
168
-
169
- mres = MySQLResults.query('ignored2', eval_context=nil, raw=true)
170
- assert_equal(true, mres.grep('v1', :fixed))
171
- assert_equal("\e[31;1mv1\e[0m.1", mres[0]['c1'])
172
- assert_equal("\e[31;1mv1\e[0m.2", mres[0]['c2'])
173
-
174
- rows = sequence(:rows)
175
- res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
176
- res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
177
- res.expects(:fetch_row).in_sequence(rows).returns(nil)
178
-
179
- mres = MySQLResults.query('ignored3', eval_context=nil, raw=true)
180
- assert_equal(false, mres.grep('v', :fixed, :inverse))
181
-
182
- rows = sequence(:rows)
183
- res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
184
- res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
185
- res.expects(:fetch_row).in_sequence(rows).returns(nil)
144
+ mres = MySQLResults.new(['col1', 'col2'], [[1,2], [3,4]])
145
+ assert_equal(2, mres.num_rows)
146
+ dout = StringIO.new
147
+ mres.display_by_column(dout)
148
+ assert_equal('col1col2----------1234----------2rowsinset',
149
+ dout.string.gsub(/\s+/,''))
150
+ end
186
151
 
187
- mres = MySQLResults.query('ignored4', eval_context=nil, raw=true)
188
- assert_equal(true, mres.grep('v1.1', :nocolor))
189
- assert_equal("v1.1", mres[0]['c1'])
152
+ def test_row_access
153
+ mres = MySQLResults.new(['col1', 'col2'], [[1,2], [3,4]])
154
+ assert_equal(3, mres[1,0])
155
+ assert_equal(4, mres[1,'col2'])
156
+ assert_equal({'col1'=>3,'col2'=>4}, mres[1])
157
+ end
190
158
 
191
- # fixme: technically should be in it's only test...
192
- cmds = []
193
- 4.times{|i| cmds << "ignored#{i+1}"}
194
- assert_equal(cmds, MySQLResults.history)
195
- assert_equal([cmds.last], MySQLResults.history(1))
196
- assert_equal(cmds, MySQLResults.history(15))
159
+ def test_delete
160
+ mres = MySQLResults.new(['col1', 'col2'], [[1,2], [3,4]])
161
+ mres.delete_if{|r| r[1] == 4}
162
+ assert_equal(nil, mres[1])
163
+ mres.delete_if(:row_hash){|r| r['col1'] == 1}
164
+ assert_equal(nil, mres[0])
197
165
  end
198
166
 
199
167
  end # class TestMySQLResults
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsql
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 11
10
- version: 0.2.11
8
+ - 3
9
+ - 1
10
+ version: 0.3.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brad Robel-Forrest
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-24 00:00:00 Z
18
+ date: 2012-04-12 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: net-ssh
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  requirements: []
166
166
 
167
167
  rubyforge_project:
168
- rubygems_version: 1.8.17
168
+ rubygems_version: 1.8.21
169
169
  signing_key:
170
170
  specification_version: 3
171
171
  summary: Ruby-based MySQL command line with recipes.