td 0.12.0 → 0.13.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc5f6252aeace112a6886530da039c0761077ac0
4
- data.tar.gz: 1dfddbf8f907474702693cd7be2a23fa940d0f14
3
+ metadata.gz: f7b9e3d57fd08024e999160fe1678031a293941e
4
+ data.tar.gz: 39454d414e3afddb9e83fcf3e3c574c98911536f
5
5
  SHA512:
6
- metadata.gz: a6212b8724a6c971d5b75437c1eb540f3d72a9afddeefc23ddcea00fcedb6bc1b5a4846f6560412027eea4ae0ab7bdcef4467beab81c9395985dcbf217c96263
7
- data.tar.gz: 65523ca21f55cdf2e852a6993020d61c5a55ea14e8a4b46b6c1280067960558be7b89b5fd3c22d2568ed63cc78e3e3445e49d7318d94330e6bebeef864f5c9e3
6
+ metadata.gz: d4846a9e5e75112f0350cbb4824e5335cb2861fabc927ddcacefe81a9a4894af08da0903920db5d1b4fcfd170b571341e5b41a77293c7a572df92ca4eec487e1
7
+ data.tar.gz: 0909c9942d35536d644db19f3ad0c3bf219d78ad59388ef8cee92af5c7f0d3271c4779503d5268dda0bd65cc2bcbaf231c3d5f02369b43dbcef0af50f77c364b
@@ -16,3 +16,8 @@ script: bundle exec rake spec
16
16
  matrix:
17
17
  allow_failures:
18
18
  - rvm: ruby-head
19
+
20
+ sudo: false
21
+
22
+ notifications:
23
+ webhooks: http://td-beda.herokuapp.com/travisci_callback
data/ChangeLog CHANGED
@@ -1,3 +1,18 @@
1
+ == 2015-10-29 version 0.13.0
2
+
3
+ * fix command error when recive 40X errror from server
4
+ * support show config_diff in connector:[list,show,create,delete]
5
+ * add new command `table:rename`. This command can rename table.
6
+ * fix connector:history crash if history has queueing state.
7
+ * fix connector:run time argument bug.
8
+ * support query, job:show write Hash format JSON. If you use it, you set --column-header and -f json options.
9
+ * change default file name create by connector:guess rename to `config.yml`.
10
+ * remove --exclude option from connector:issue
11
+ * support --g option for connector:config. It can specified guess plugin.
12
+ * improve progressbar
13
+ * support filter section for config file.
14
+ * remove --primary-key option from table:create
15
+
1
16
  == 2015-08-10 version 0.12.0
2
17
 
3
18
  * Improve error message from APIError
@@ -247,6 +247,13 @@ EOS
247
247
  table
248
248
  end
249
249
 
250
+ def table_exist?(database, table_name)
251
+ database.table(table_name)
252
+ true
253
+ rescue
254
+ false
255
+ end
256
+
250
257
  def create_database_and_table_if_not_exist(client, db_name, table_name)
251
258
  # Merge with db_create and table_create after refactoring
252
259
  API.validate_database_name(db_name)
@@ -295,6 +302,22 @@ EOS
295
302
  end
296
303
  end
297
304
 
305
+ def puts_with_indent(message, indent_size = 4, io = $stdout)
306
+ io.puts "#{' ' * indent_size}#{message}"
307
+ end
308
+
309
+ def create_file_backup(out)
310
+ return unless File.exist?(out)
311
+ 0.upto(100) do |idx|
312
+ backup = "#{out}.#{idx}"
313
+ unless File.exist?(backup)
314
+ FileUtils.mv(out, backup)
315
+ return
316
+ end
317
+ end
318
+ raise "backup file creation failed"
319
+ end
320
+
298
321
  def self.validate_api_endpoint(endpoint)
299
322
  require 'uri'
300
323
 
@@ -351,26 +374,28 @@ EOS
351
374
  files
352
375
  end
353
376
 
354
- class DownloadProgressIndicator
355
- def initialize(msg)
356
- @base_msg = msg
357
- $stdout.print @base_msg + " " * 10
358
- end
359
- end
360
-
361
- class TimeBasedDownloadProgressIndicator < DownloadProgressIndicator
377
+ class TimeBasedDownloadProgressIndicator
362
378
  def initialize(msg, start_time, periodicity = 2)
379
+ require 'ruby-progressbar'
380
+
363
381
  @start_time = start_time
364
382
  @last_time = start_time
365
383
  @periodicity = periodicity
366
- super(msg)
384
+
385
+ @progress_bar = ProgressBar.create(
386
+ title: msg,
387
+ total: nil,
388
+ format: formated_with_elasped_time(0),
389
+ output: $stdout,
390
+ unknown_progress_animation_steps: [' '],
391
+ )
392
+
393
+ update_progress_bar(formated_with_elasped_time(Command.humanize_elapsed_time(@start_time, @start_time)))
367
394
  end
368
395
 
369
396
  def update
370
397
  if (time = Time.now.to_i) - @last_time >= @periodicity
371
- msg = "\r#{@base_msg}: #{Command.humanize_elapsed_time(@start_time, time)} elapsed"
372
- $stdout.print "\r" + " " * (msg.length + 10)
373
- $stdout.print msg
398
+ update_progress_bar(formated_with_elasped_time(Command.humanize_elapsed_time(@start_time, time)))
374
399
  @last_time = time
375
400
  true
376
401
  else
@@ -379,13 +404,37 @@ EOS
379
404
  end
380
405
 
381
406
  def finish
382
- $stdout.puts "\r#{@base_msg}...done" + " " * 20
407
+ # NOTE %B is for clear terminal line
408
+ update_progress_bar("%t: done %B")
409
+ end
410
+
411
+ private
412
+
413
+ def formated_with_elasped_time(elasped_time)
414
+ # NOTE %B is for clear terminal line
415
+ "%t: #{elasped_time} elapsed %B"
416
+ end
417
+
418
+ def update_progress_bar(format)
419
+ @progress_bar.format = format
420
+ @progress_bar.refresh
383
421
  end
384
422
  end
385
423
 
386
- class SizeBasedDownloadProgressIndicator < DownloadProgressIndicator
387
- def initialize(msg, size, perc_step = 1, min_periodicity = nil)
388
- @size = size
424
+ class SizeBasedDownloadProgressIndicator
425
+ def initialize(msg, total_size, perc_step = 1, min_periodicity = nil)
426
+ require 'ruby-progressbar'
427
+
428
+ @total_size = total_size
429
+ @total_byte_size = Command.humanize_bytesize(@total_size) unless unknown_progress_mode?
430
+
431
+ @progress_bar = ProgressBar.create(
432
+ title: msg,
433
+ total: unknown_progress_mode? ? nil : @total_size,
434
+ format: formated_title(0),
435
+ output: $stdout,
436
+ )
437
+ @progress_bar.refresh
389
438
 
390
439
  # perc_step is how small a percentage increment can be shown
391
440
  @perc_step = perc_step
@@ -397,22 +446,18 @@ EOS
397
446
  # track progress
398
447
  @last_perc_step = 0
399
448
  @last_time = @start_time
400
-
401
- super(msg)
402
449
  end
403
450
 
404
451
  def update(curr_size)
405
- if @size.nil? || @size == 0
406
- msg = "\r#{@base_msg}: #{Command.humanize_bytesize(curr_size)}"
407
- $stdout.print msg
452
+ if unknown_progress_mode?
453
+ update_progress_bar(curr_size)
408
454
  true
409
455
  else
410
- ratio = (curr_size.to_f * 100 / @size).round(1)
456
+ ratio = (curr_size.to_f * 100 / @total_size).round(1)
411
457
  if ratio >= (@last_perc_step + @perc_step) &&
412
458
  (!@min_periodicity || (time = Time.now.to_i) - @last_time >= @min_periodicity)
413
- msg = "\r#{@base_msg}: #{Command.humanize_bytesize(curr_size)} / #{ratio}%"
414
- $stdout.print "\r" + " " * (msg.length + 10)
415
- $stdout.print msg
459
+ update_progress_bar(curr_size)
460
+
416
461
  @last_perc_step = ratio
417
462
  @last_time = time
418
463
  true
@@ -423,7 +468,32 @@ EOS
423
468
  end
424
469
 
425
470
  def finish
426
- $stdout.print "\r#{@base_msg}...done" + " " * 20
471
+ if unknown_progress_mode?
472
+ @progress_bar.format = "%t : #{Command.humanize_bytesize(@progress_bar.progress).rjust(10)} Done"
473
+ @progress_bar.progress = 0
474
+ else
475
+ update_progress_bar(@progress_bar.total)
476
+ end
477
+ end
478
+
479
+ private
480
+
481
+ def unknown_progress_mode?
482
+ @total_size.nil? || @total_size == 0
483
+ end
484
+
485
+ def update_progress_bar(curr_size)
486
+ @progress_bar.format = formated_title(curr_size)
487
+ @progress_bar.progress = curr_size
488
+ end
489
+
490
+ def formated_title(curr_size)
491
+ if unknown_progress_mode?
492
+ "%t : #{Command.humanize_bytesize(curr_size).rjust(10)}"
493
+ else
494
+ rjust_size = @total_byte_size.size + 1
495
+ "%t #{Command.humanize_bytesize(curr_size).rjust(rjust_size)} / #{@total_byte_size.rjust(rjust_size)} : %w "
496
+ end
427
497
  end
428
498
  end
429
499
 
@@ -1,8 +1,10 @@
1
1
  require 'td/command/common'
2
2
  require 'td/command/job'
3
+ require 'td/connector_config_normalizer'
3
4
  require 'json'
4
5
  require 'uri'
5
6
  require 'yaml'
7
+ require 'time'
6
8
 
7
9
  module TreasureData
8
10
  module Command
@@ -16,13 +18,17 @@ module Command
16
18
  def connector_guess(op)
17
19
  type = 's3'
18
20
  id = secret = source = nil
19
- out = 'td-bulkload.yml'
21
+ out = 'config.yml'
22
+ guess_plugins = {}
20
23
 
21
24
  op.on('--type[=TYPE]', "(obsoleted)") { |s| type = s }
22
25
  op.on('--access-id ID', "(obsoleted)") { |s| id = s }
23
26
  op.on('--access-secret SECRET', "(obsoleted)") { |s| secret = s }
24
27
  op.on('--source SOURCE', "(obsoleted)") { |s| source = s }
25
28
  op.on('-o', '--out FILE_NAME', "output file name for connector:preview") { |s| out = s }
29
+ op.on('-g', '--guess NAME,NAME,...', 'specify list of guess plugins that users want to use') {|s|
30
+ guess_plugins['guess_plugins'] = s.split(',')
31
+ }
26
32
 
27
33
  config_file = op.cmd_parse
28
34
  if config_file
@@ -30,6 +36,7 @@ module Command
30
36
  out ||= config_file
31
37
  else
32
38
  begin
39
+ $stdout.puts 'Command line option is obsoleted. You should use configuration file.'
33
40
  required('--access-id', id)
34
41
  required('--access-secret', secret)
35
42
  required('--source', source)
@@ -60,14 +67,17 @@ module Command
60
67
  }
61
68
  end
62
69
 
70
+ config = TreasureData::ConnectorConfigNormalizer.new(config).normalized_config
71
+ config['exec'].merge!(guess_plugins)
72
+
63
73
  client = get_client
64
74
  job = client.bulk_load_guess(config: config)
65
75
 
66
- create_bulkload_job_file_backup(out)
76
+ create_file_backup(out)
67
77
  if /\.json\z/ =~ out
68
78
  config_str = JSON.pretty_generate(job['config'])
69
79
  else
70
- config_str = YAML.dump(job['config'])
80
+ config_str = config_to_yaml(job['config'])
71
81
  end
72
82
  File.open(out, 'w') do |f|
73
83
  f << config_str
@@ -110,14 +120,13 @@ module Command
110
120
  def connector_issue(op)
111
121
  database = table = nil
112
122
  time_column = nil
113
- wait = exclude = false
123
+ wait = false
114
124
  auto_create = false
115
125
 
116
126
  op.on('--database DB_NAME', "destination database") { |s| database = s }
117
127
  op.on('--table TABLE_NAME', "destination table") { |s| table = s }
118
128
  op.on('--time-column COLUMN_NAME', "data partitioning key") { |s| time_column = s } # unnecessary but for backward compatibility
119
129
  op.on('-w', '--wait', 'wait for finishing the job', TrueClass) { |b| wait = b }
120
- op.on('-x', '--exclude', 'do not automatically retrieve the job result', TrueClass) { |b| exclude = b }
121
130
  op.on('--auto-create-table', "Create table and database if doesn't exist", TrueClass) { |b|
122
131
  auto_create = b
123
132
  }
@@ -142,7 +151,7 @@ module Command
142
151
  $stdout.puts "Use '#{$prog} " + Config.cl_options_string + "job:show #{job_id}' to show the status."
143
152
 
144
153
  if wait
145
- wait_connector_job(client, job_id, exclude)
154
+ wait_connector_job(client, job_id)
146
155
  end
147
156
  end
148
157
 
@@ -152,7 +161,7 @@ module Command
152
161
 
153
162
  client = get_client()
154
163
  # TODO database and table is empty at present. Fix API or Client.
155
- keys = ['name', 'cron', 'timezone', 'delay', 'database', 'table', 'config']
164
+ keys = ['name', 'cron', 'timezone', 'delay', 'database', 'table', 'config', 'config_diff']
156
165
  fields = keys.map { |e| e.capitalize.to_sym }
157
166
  rows = client.bulk_load_list().sort_by { |e|
158
167
  e['name']
@@ -229,35 +238,47 @@ module Command
229
238
  fields = [:JobID, :Status, :Records, :Database, :Table, :Priority, :Started, :Duration]
230
239
  client = get_client()
231
240
  rows = client.bulk_load_history(name).map { |e|
241
+ time_property = if e['start_at']
242
+ {
243
+ :Started => Time.at(e['start_at']),
244
+ :Duration => (e['end_at'].nil? ? Time.now.to_i : e['end_at']) - e['start_at'],
245
+ }
246
+ else
247
+ {:Started => '', :Duration => ''}
248
+ end
249
+
232
250
  {
233
251
  :JobID => e['job_id'],
234
252
  :Status => e['status'],
235
253
  :Records => e['records'],
236
254
  # TODO: td-client-ruby should retuan only name
237
- :Database => e['database']['name'],
238
- :Table => e['table']['name'],
255
+ :Database => e['database'] ? e['database']['name'] : '',
256
+ :Table => e['table'] ? e['table']['name'] : '',
239
257
  :Priority => e['priority'],
240
- :Started => Time.at(e['start_at']),
241
- :Duration => (e['end_at'].nil? ? Time.now.to_i : e['end_at']) - e['start_at'],
242
- }
258
+ }.merge(time_property)
243
259
  }
244
260
  $stdout.puts cmd_render_table(rows, :fields => fields, :render_format => op.render_format)
245
261
  end
246
262
 
247
263
  def connector_run(op)
248
- wait = exclude = false
264
+ wait = false
249
265
  op.on('-w', '--wait', 'wait for finishing the job', TrueClass) { |b| wait = b }
250
- op.on('-x', '--exclude', 'do not automatically retrieve the job result', TrueClass) { |b| exclude = b }
251
266
 
252
267
  name, scheduled_time = op.cmd_parse
268
+ time = if scheduled_time
269
+ Time.parse(scheduled_time).to_i
270
+ else
271
+ current_time.to_i
272
+ end
253
273
 
254
274
  client = get_client()
255
- job_id = client.bulk_load_run(name)
275
+ job_id = client.bulk_load_run(name, time)
276
+
256
277
  $stdout.puts "Job #{job_id} is queued."
257
278
  $stdout.puts "Use '#{$prog} " + Config.cl_options_string + "job:show #{job_id}' to show the status."
258
279
 
259
280
  if wait
260
- wait_connector_job(client, job_id, exclude)
281
+ wait_connector_job(client, job_id)
261
282
  end
262
283
  end
263
284
 
@@ -277,6 +298,19 @@ private
277
298
  nil
278
299
  end
279
300
 
301
+ def config_to_yaml(config)
302
+ config_str = ''
303
+ begin
304
+ require 'td/compact_format_yamler'
305
+ config_str = TreasureData::CompactFormatYamler.dump(config)
306
+ rescue
307
+ # NOTE fail back
308
+ config_str = YAML.dump(config)
309
+ end
310
+ config_str
311
+ end
312
+
313
+
280
314
  def prepare_bulkload_job_config(config_file)
281
315
  unless File.exist?(config_file)
282
316
  raise ParameterConfigurationError, "configuration file: #{config_file} not found"
@@ -293,25 +327,7 @@ private
293
327
  raise ParameterConfigurationError, "configuration file: #{config_file} #{e.message}"
294
328
  end
295
329
 
296
- if config['config']
297
- if config.size != 1
298
- raise "Setting #{(config.keys - ['config']).inspect} keys in a configuration file is not supported. Please set options to the command line argument."
299
- end
300
- config = config['config']
301
- end
302
- config
303
- end
304
-
305
- def create_bulkload_job_file_backup(out)
306
- return unless File.exist?(out)
307
- 0.upto(100) do |idx|
308
- backup = "#{out}.#{idx}"
309
- unless File.exist?(backup)
310
- FileUtils.mv(out, backup)
311
- return
312
- end
313
- end
314
- raise "backup file creation failed"
330
+ TreasureData::ConnectorConfigNormalizer.new(config).normalized_config
315
331
  end
316
332
 
317
333
  def dump_connector_session(session)
@@ -323,13 +339,19 @@ private
323
339
  $stdout.puts "Table : #{session["table"]}"
324
340
  $stdout.puts "Config"
325
341
  $stdout.puts YAML.dump(session["config"])
342
+ $stdout.puts
343
+ $stdout.puts "Config Diff"
344
+ $stdout.puts YAML.dump(session["config_diff"])
326
345
  end
327
346
 
328
- def wait_connector_job(client, job_id, exclude)
347
+ def wait_connector_job(client, job_id)
329
348
  job = client.job(job_id)
330
349
  wait_job(job, true)
331
350
  $stdout.puts "Status : #{job.status}"
332
351
  end
333
352
 
353
+ def current_time
354
+ Time.now
355
+ end
334
356
  end
335
357
  end
@@ -1,5 +1,6 @@
1
1
  require 'td/updater'
2
2
  require 'time'
3
+ require 'yaml'
3
4
 
4
5
  module TreasureData
5
6
  module Command
@@ -77,6 +78,50 @@ module Command
77
78
  bulk_import_unfreeze(op)
78
79
  end
79
80
 
81
+ def import_config(op)
82
+ out = 'td-bulkload.yml'
83
+ options = {
84
+ 'format' => 'csv'
85
+ }
86
+ not_migrate_options = []
87
+ op.on('-o', '--out FILE_NAME', "output file name for connector:guess") { |s| out = s }
88
+ op.on('-f', '--format FORMAT', "source file format [csv, tsv, mysql]; default=csv") { |s| options['format'] = s }
89
+
90
+ op.on('--db-url URL', "Database Connection URL") { |s| options['db_url'] = s }
91
+ op.on('--db-user NAME', "user name for database") { |s| options['db_user'] = s }
92
+ op.on('--db-password PASSWORD', "password for database") { |s| options['db_password'] = s }
93
+ %w(--columns --column-header --time-column --time-format).each do |not_migrate_option|
94
+ opt_arg_name = not_migrate_option.gsub('--', '').upcase
95
+ op.on("#{not_migrate_option} #{opt_arg_name}", 'not supported') { |s| not_migrate_options << not_migrate_option }
96
+ end
97
+
98
+ arg = op.cmd_parse
99
+
100
+ unless %w(mysql csv tsv).include?(options['format'])
101
+ raise ParameterConfigurationError, "#{options['format']} is unknown format. Support format is csv, tsv and mysql."
102
+ end
103
+
104
+ unless not_migrate_options.empty?
105
+ be = not_migrate_options.size == 1 ? 'is' : 'are'
106
+ $stderr.puts "`#{not_migrate_options.join(', ')}` #{be} not migrate. Please, edit config file after execute guess commands."
107
+ end
108
+
109
+ $stdout.puts "Generating #{out}..."
110
+
111
+ config = generate_seed_confing(options['format'], arg, options)
112
+ config_str = YAML.dump(config)
113
+
114
+
115
+ create_file_backup(out)
116
+ File.open(out, 'w') {|f| f << config_str }
117
+
118
+ if config['out']['type'] == 'td'
119
+ show_message_for_td_output_plugin(out)
120
+ else
121
+ show_message_for_td_data_connector(out)
122
+ end
123
+ end
124
+
80
125
  #
81
126
  # Module private methods - don't map to import:* commands
82
127
  #
@@ -270,7 +315,7 @@ module Command
270
315
  port = 80 if port == 0
271
316
  ssl = false
272
317
  end
273
- end
318
+ end
274
319
 
275
320
  sysprops << "-Dtd.api.server.scheme=#{ssl ? 'https' : 'http'}://"
276
321
  sysprops << "-Dtd.api.server.host=#{host}"
@@ -344,5 +389,121 @@ module Command
344
389
  version.first
345
390
  end
346
391
 
392
+ def generate_seed_confing(format, arg, options)
393
+ case format
394
+ when 'csv', 'tsv'
395
+ if arg =~ /^s3:/
396
+ generate_s3_config(format, arg)
397
+ else
398
+ generate_csv_config(format, arg)
399
+ end
400
+ when 'mysql'
401
+ arg = arg[1] unless arg.class == String
402
+ generate_mysql_config(arg, options)
403
+ else
404
+ # NOOP
405
+ end
406
+ end
407
+
408
+ def generate_s3_config(format, arg)
409
+ puts_with_indent('Using S3 input')
410
+ puts_with_indent('Using CSV parser plugin')
411
+ puts_with_indent('Using Treasure Data data connector')
412
+
413
+ match = Regexp.new("^s3://(.*):(.*)@/([^/]*)/(.*)").match(arg)
414
+
415
+ {
416
+ 'in' => {
417
+ 'type' => 's3',
418
+ 'access_key_id' => match[1],
419
+ 'secret_access_key' => match[2],
420
+ 'bucket' => match[3],
421
+ 'path_prefix' => normalize_path_prefix(match[4])
422
+ },
423
+ 'out' => {'mode' => 'append'}
424
+ }
425
+ end
426
+
427
+ def generate_csv_config(format, arg)
428
+ puts_with_indent('Using local file input')
429
+ puts_with_indent('Using CSV parser plugin')
430
+ puts_with_indent('Using Treasure Data output')
431
+
432
+ {
433
+ 'in' => {
434
+ 'type' => 'file',
435
+ 'path_prefix' => normalize_path_prefix(arg),
436
+ 'decorders' => [{'type' => 'gzip'}],
437
+ },
438
+ 'out' => td_output_config,
439
+ }
440
+ end
441
+
442
+ def td_output_config
443
+ {
444
+ 'type' => 'td',
445
+ 'endpoint' => Config.cl_endpoint || Config.endpoint,
446
+ 'apikey' => Config.cl_apikey || Config.apikey,
447
+ 'database' => '',
448
+ 'table' => '',
449
+ }
450
+ end
451
+
452
+ def normalize_path_prefix(path)
453
+ path.gsub(/\*.*/, '')
454
+ end
455
+
456
+ def generate_mysql_config(arg, options)
457
+ puts_with_indent('Using MySQL input')
458
+ puts_with_indent('Using MySQL parser plugin')
459
+ puts_with_indent('Using Treasure Data output')
460
+
461
+ mysql_url_regexp = Regexp.new("[jdbc:]*mysql://(?<host>[^:/]*)[:]*(?<port>[^/]*)/(?<db_name>.*)")
462
+
463
+ config = if (match = mysql_url_regexp.match(options['db_url']))
464
+ {
465
+ 'host' => match['host'],
466
+ 'port' => match['port'] == '' ? 3306 : match['port'].to_i,
467
+ 'database' => match['db_name'],
468
+ }
469
+ else
470
+ {
471
+ 'host' => '',
472
+ 'port' => 3306,
473
+ 'database' => '',
474
+ }
475
+ end
476
+
477
+ {
478
+ 'in' => config.merge(
479
+ 'type' => 'mysql',
480
+ 'user' => options['db_user'],
481
+ 'password' => options['db_password'],
482
+ 'table' => arg,
483
+ 'select' => '*',
484
+ ),
485
+ 'out' => td_output_config,
486
+ }
487
+ end
488
+
489
+ def show_message_for_td_output_plugin(out)
490
+ $stdout.puts 'Done. Please use embulk to load the files.'
491
+ $stdout.puts 'Next steps:'
492
+ $stdout.puts
493
+ puts_with_indent '# install embulk'
494
+ puts_with_indent "$ embulk gem install embulk-output-td"
495
+ puts_with_indent '$ embulk guess seed.yml -o config.yml'
496
+ puts_with_indent '$ embulk preview config.yml'
497
+ puts_with_indent '$ embulk run config.yml'
498
+ end
499
+
500
+ def show_message_for_td_data_connector(out)
501
+ $stdout.puts 'Done. Please use connector:guess and connector:run to load the files.'
502
+ $stdout.puts 'Next steps:'
503
+ puts_with_indent "$ td connector:guess #{out} -o config.yml"
504
+ puts_with_indent '$ td connector:preview config.yml'
505
+ puts_with_indent '$ td connector:run config.yml'
506
+ end
507
+
347
508
  end
348
509
  end