Class: K8::Util::ShellCommand
- Inherits:
-
Object
- Object
- K8::Util::ShellCommand
- Defined in:
- lib/keight.rb
Overview
Invoke shell command and responses it’s output to client.
Example:
def do_download_csv()
## for example, run SQL and generate CSV file (for postgresql)
sql = "select * from table1"
cmd = "psql -AF',' -U dbuser dbname | iconv -f UTF-8 -t CP932 -c | gzip"
shell_command = K8::Util::ShellCommand.new(cmd, input: sql) do
## callback after sending response body
File.unlink(tempfile) if File.exist?(templfile) # for example
end
begin
return shell_command.start() do
## callback before sending response body
@resp.headers['Content-Type'] = "text/csv;charset=Shift_JIS"
@resp.headers['Content-Disposition'] = 'attachment;filename="file.csv"'
@resp.headers['Content-Encoding'] = "gzip"
end
rescue K8::Util::ShellCommandError => ex
logger = @req.env['rack.logger']
logger.error(ex.) if logger
@resp.status = 500
@resp.headers['Content-Type'] = "text/plain;charset=UTF-8"
return ex.
end
end
Constant Summary collapse
- CHUNK_SIZE =
8 * 1024
Instance Attribute Summary collapse
-
#command ⇒ Object
readonly
Returns the value of attribute command.
-
#input ⇒ Object
readonly
Returns the value of attribute input.
-
#process_id ⇒ Object
readonly
Returns the value of attribute process_id.
Instance Method Summary collapse
- #each {|chunk| ... } ⇒ Object
-
#initialize(command, input: nil, chunk_size: nil, &teardown) ⇒ ShellCommand
constructor
A new instance of ShellCommand.
- #log_error(message) ⇒ Object
- #start ⇒ Object
Constructor Details
#initialize(command, input: nil, chunk_size: nil, &teardown) ⇒ ShellCommand
Returns a new instance of ShellCommand.
506 507 508 509 510 511 512 513 514 |
# File 'lib/keight.rb', line 506 def initialize(command, input: nil, chunk_size: nil, &teardown) #; [!j95pi] takes shell command and input string. @command = command # ex: "psql -AF',' dbname | gzip" @input = input # ex: "select * from table1" @chunk_size = chunk_size || CHUNK_SIZE @teardown = teardown @process_id = nil @tuple = nil end |
Instance Attribute Details
#command ⇒ Object (readonly)
Returns the value of attribute command.
516 517 518 |
# File 'lib/keight.rb', line 516 def command @command end |
#input ⇒ Object (readonly)
Returns the value of attribute input.
516 517 518 |
# File 'lib/keight.rb', line 516 def input @input end |
#process_id ⇒ Object (readonly)
Returns the value of attribute process_id.
516 517 518 |
# File 'lib/keight.rb', line 516 def process_id @process_id end |
Instance Method Details
#each {|chunk| ... } ⇒ Object
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/keight.rb', line 554 def each #; [!ssgmm] '#start()' should be called before '#each()'. @process_id or raise ShellCommandError.new("Not started yet (command: #{@command.inspect}).") #; [!vpmbw] yields each chunk data. sout, serr, waiter, chunk = @tuple @tuple = nil yield chunk size = @chunk_size ex = nil begin while (chunk = sout.read(size)) yield chunk end #; [!70xdy] logs stderr output. error = serr.read() log_error(error) if error && ! error.empty? rescue => ex raise ensure #; [!2wll8] closes stdout and stderr, even if error raised. sout.close() serr.close() #; [!0ebq5] calls callback specified at initializer with error object. @teardown.yield(ex) if @teardown end #; [!ln8we] returns self. self end |
#log_error(message) ⇒ Object
584 585 586 587 588 |
# File 'lib/keight.rb', line 584 def log_error() $stderr.write("[ERROR] ShellCommand: #{@command.inspect} #-------\n") $stderr.write(); $stderr.write("\n") unless .end_with?("\n") $stderr.write("--------------------\n") end |
#start ⇒ Object
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/keight.rb', line 518 def start #; [!66uck] not allowed to start more than once. @process_id.nil? or # TODO: close sout and serr raise ShellCommandError.new("Already started (comand: #{@command.inspect})") #; [!9seos] invokes shell command. require 'open3' unless defined?(::Open3) sin, sout, serr, waiter = ::Open3.popen3(@command) @process_id = waiter.pid size = @chunk_size begin #; [!d766y] writes input string if provided to initializer. sin.write(input) if input sin.close() #; [!f651x] reads first chunk data. #; [!cjstj] raises ShellCommandError when command prints something to stderr. chunk = sout.read(size) if chunk.nil? error = serr.read() log_error(error.to_s) error = "Command failed: #{@command}" if ! error || error.empty? raise ShellCommandError.new(error) end #; [!bt12n] saves stdout, stderr, command process, and first chunk data. @tuple = [sout, serr, waiter, chunk] #; [!kgnel] yields callback (if given) when command invoked successfully. yield if block_given? #; [!2989u] closes both stdout and stderr when error raised. rescue => ex sout.close() serr.close() raise end #; [!fp98i] returns self. self end |