pdi 1.0.1 → 2.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4dc8c8b41b233e1982d4c6164318cf9b4682dfcb335699d4027372edee921f8a
4
- data.tar.gz: 6f61afc314e69019464cb4722063f02513a06f493e820661e2f12128af5d6658
3
+ metadata.gz: 3adae07a9c634b7075a87a47c810c0d4e3c90137ee85a08e01e09038baf990e1
4
+ data.tar.gz: 9f7ecf0e06ace02def29a7e710ea87d5ed5e2af82282a1dba935fa3628116533
5
5
  SHA512:
6
- metadata.gz: a540f0ad04dc38d0155c156041e22269155aa2fcad3958ec9a874302d888af4f70afdb69cf88ecebd624457d2fa8b03bba59c9963e5772d86f679088186a59bb
7
- data.tar.gz: '085f9c0f9a0e2a3d4487d63a0c3a59ab0f5757e7eace1c757abedd96faf7d9152894e9c41a5c37bafe985a64889d03d5f8de3be72960a0ed9247ced3a14adfed'
6
+ metadata.gz: 5104d4d97f12a70e7f2d492dee6f8f870c388fea8091bc26d60fd87efb826331479a5efa851de7cbeb278d48aad9368d12cbad7bbb675b9cd9a20baee50f087e
7
+ data.tar.gz: 3ff6caa7e64806a1f03f918b7a6692e25c7493aa6280c0f9edabc99dd937e8890d76f3e8e0f060584964b993791c990621dc3cd8c908f91b5e81a25ac3e9f5ff
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 2.0.0 (May 7th, 2020)
2
+
3
+ Breaking Changes:
4
+
5
+ * Standard error and output have been combined into one stream (out). Early feedback indicated that reading both at the same time was preferable.
6
+
7
+ Enhancements:
8
+
9
+ * Added optional `timeout_in_seconds` argument to Pdi::Spoon#initialize.
10
+
1
11
  # 1.0.1 (February 19th, 2020)
2
12
 
3
13
  Fixes:
data/README.md CHANGED
@@ -50,6 +50,7 @@ Notes:
50
50
 
51
51
  * You can also override the names of the scripts using the `kitchen` and `pan` constructor keyword arguments. The defaults are `kitchen.sh` and `pan.sh`, respectively.
52
52
  * For other command line arguments that are not supported first-class in the Options objects below you can utilize the `args` argument when instantiating a `Spoon` instance.
53
+ * Another optional argument is `timeout_in_seconds`. If set it will ensure the sub-process runs within a given window. If it times out the sub-process will be terminated and a Timeout::Error will be raised.
53
54
 
54
55
  ### Executing a Job/Transformation
55
56
 
data/lib/pdi.rb CHANGED
@@ -8,8 +8,10 @@
8
8
  #
9
9
 
10
10
  require 'acts_as_hashable'
11
+ require 'English'
11
12
  require 'forwardable'
12
13
  require 'open3'
14
+ require 'timeout'
13
15
 
14
16
  require_relative 'pdi/executor'
15
17
  require_relative 'pdi/spoon'
data/lib/pdi/executor.rb CHANGED
@@ -13,20 +13,42 @@ module Pdi
13
13
  # This class is the library's "metal" layer, the one which actually makes the system call and
14
14
  # interacts with the operating system (through Ruby's standard library.)
15
15
  class Executor
16
+ attr_reader :timeout_in_seconds
17
+
18
+ def initialize(timeout_in_seconds: nil)
19
+ @timeout_in_seconds = timeout_in_seconds
20
+
21
+ freeze
22
+ end
23
+
16
24
  def run(args)
17
25
  args = Array(args).map(&:to_s)
18
26
 
19
- out, err, status = Open3.capture3(*args)
20
-
21
- Result.new(
22
- args: args,
23
- status: {
24
- code: status.exitstatus,
25
- out: out,
26
- err: err,
27
- pid: status.pid
28
- }
29
- )
27
+ IO.popen(args, err: %i[child out]) do |io|
28
+ begin
29
+ io_read =
30
+ if timeout_in_seconds
31
+ Timeout.timeout(timeout_in_seconds) { io.read }
32
+ else
33
+ io.read
34
+ end
35
+
36
+ io.close
37
+ status = $CHILD_STATUS
38
+
39
+ Result.new(
40
+ args: args,
41
+ status: {
42
+ code: status.exitstatus,
43
+ out: io_read,
44
+ pid: status.pid
45
+ }
46
+ )
47
+ rescue Timeout::Error => e
48
+ Process.kill(9, io.pid)
49
+ raise e
50
+ end
51
+ end
30
52
  end
31
53
  end
32
54
  end
@@ -18,7 +18,7 @@ module Pdi
18
18
 
19
19
  attr_reader :args, :status
20
20
 
21
- def_delegators :status, :code, :out, :err, :pid
21
+ def_delegators :status, :code, :out, :pid
22
22
 
23
23
  def initialize(args:, status: {})
24
24
  @args = args
@@ -13,12 +13,11 @@ module Pdi
13
13
  class Status
14
14
  acts_as_hashable
15
15
 
16
- attr_reader :code, :out, :err, :pid
16
+ attr_reader :code, :out, :pid
17
17
 
18
- def initialize(code:, out: '', err: '', pid:)
18
+ def initialize(code:, out: '', pid:)
19
19
  @code = code
20
20
  @out = out
21
- @err = err
22
21
  @pid = pid
23
22
 
24
23
  freeze
data/lib/pdi/spoon.rb CHANGED
@@ -30,7 +30,8 @@ module Pdi
30
30
  args: [],
31
31
  dir:,
32
32
  kitchen: DEFAULT_KITCHEN,
33
- pan: DEFAULT_PAN
33
+ pan: DEFAULT_PAN,
34
+ timeout_in_seconds: nil
34
35
  )
35
36
  assert_required(:dir, dir)
36
37
  assert_required(:kitchen, kitchen)
@@ -40,7 +41,7 @@ module Pdi
40
41
  @dir = File.expand_path(dir.to_s)
41
42
  @kitchen = kitchen.to_s
42
43
  @pan = pan.to_s
43
- @executor = Executor.new
44
+ @executor = Executor.new(timeout_in_seconds: timeout_in_seconds)
44
45
  @parser = Parser.new
45
46
 
46
47
  freeze
data/lib/pdi/version.rb CHANGED
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Pdi
11
- VERSION = '1.0.1'
11
+ VERSION = '2.0.0-alpha'
12
12
  end
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ echo 'std_out'
4
+ echo 'err_out' >&2
5
+
6
+ sleep $1
7
+
8
+ echo 'after_sleep'
9
+
10
+ exit $2
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2018-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ describe Pdi::Executor do
13
+ let(:script) { File.join('spec', 'mocks', 'spoon', 'sleep.sh') }
14
+ let(:one_second) { 1 }
15
+ let(:return_code) { 33 }
16
+
17
+ describe '#run' do
18
+ context 'with a timeout' do
19
+ # do not make these too high, bugs could cause the entire script to still be executed without
20
+ # killing it so these high values could draw out results.
21
+ let(:thirty_seconds) { 30 }
22
+
23
+ subject { described_class.new(timeout_in_seconds: one_second) }
24
+
25
+ # This will run a script that will take 30 seconds to process, but by limiting the
26
+ # timeout using the #run argument, it should raise an error after one second.
27
+ it 'times out and kills process after 5 seconds' do
28
+ args = [script, thirty_seconds]
29
+
30
+ expect { subject.run(args) }.to raise_error(Timeout::Error)
31
+ end
32
+ end
33
+
34
+ context 'without a timeout' do
35
+ it 'returns right exit status as code' do
36
+ args = [script, one_second, return_code]
37
+
38
+ result = subject.run(args)
39
+
40
+ expect(result.code).to eq(return_code)
41
+ end
42
+
43
+ it 'returns right standard output and error as out' do
44
+ args = [script, one_second, return_code]
45
+
46
+ result = subject.run(args)
47
+
48
+ expect(result.out).to eq("std_out\nerr_out\nafter_sleep\n")
49
+ end
50
+ end
51
+ end
52
+ end
@@ -14,6 +14,21 @@ describe Pdi::Spoon do
14
14
  let(:mocks_dir) { %w[spec mocks spoon] }
15
15
  let(:dir) { File.join(*mocks_dir) }
16
16
 
17
+ describe '#initialize' do
18
+ it 'sets executor' do
19
+ timeout_in_seconds = 987
20
+
21
+ subject = described_class.new(dir: dir, timeout_in_seconds: timeout_in_seconds)
22
+
23
+ # Private/internal testing is not recommended, but I really wanted to ensure
24
+ # this class is properly configuring the Executor instance, that way I can rely
25
+ # mainly on the Executor unit tests instead of integration tests at this level.
26
+ executor = subject.send('executor')
27
+
28
+ expect(executor.timeout_in_seconds).to eq(timeout_in_seconds)
29
+ end
30
+ end
31
+
17
32
  describe '#run' do
18
33
  context 'transformations' do
19
34
  let(:options) do
@@ -43,8 +58,7 @@ describe Pdi::Spoon do
43
58
 
44
59
  result = subject.run(options)
45
60
 
46
- expect(result.out).to eq("output to stdout\n")
47
- expect(result.err).to eq("output to sterr\n")
61
+ expect(result.out).to eq("output to stdout\noutput to sterr\n")
48
62
  expect(result.code).to eq(0)
49
63
  end
50
64
  end
@@ -97,8 +111,7 @@ describe Pdi::Spoon do
97
111
 
98
112
  result = subject.run(options)
99
113
 
100
- expect(result.out).to eq("output to stdout\n")
101
- expect(result.err).to eq("output to sterr\n")
114
+ expect(result.out).to eq("output to stdout\noutput to sterr\n")
102
115
  expect(result.code).to eq(0)
103
116
  end
104
117
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-20 00:00:00.000000000 Z
11
+ date: 2020-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_hashable
@@ -160,7 +160,9 @@ files:
160
160
  - lib/pdi/version.rb
161
161
  - pdi.gemspec
162
162
  - spec/mocks/spoon/return_code.sh
163
+ - spec/mocks/spoon/sleep.sh
163
164
  - spec/mocks/spoon/version.sh
165
+ - spec/pdi/executor_spec.rb
164
166
  - spec/pdi/spoon/kitchen_error_spec.rb
165
167
  - spec/pdi/spoon/options/arg_spec.rb
166
168
  - spec/pdi/spoon/options/param_spec.rb
@@ -183,9 +185,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
183
185
  version: 2.3.8
184
186
  required_rubygems_version: !ruby/object:Gem::Requirement
185
187
  requirements:
186
- - - ">="
188
+ - - ">"
187
189
  - !ruby/object:Gem::Version
188
- version: '0'
190
+ version: 1.3.1
189
191
  requirements: []
190
192
  rubygems_version: 3.0.3
191
193
  signing_key:
@@ -193,7 +195,9 @@ specification_version: 4
193
195
  summary: Ruby wrapper for invoking Pentaho Data Integration
194
196
  test_files:
195
197
  - spec/mocks/spoon/return_code.sh
198
+ - spec/mocks/spoon/sleep.sh
196
199
  - spec/mocks/spoon/version.sh
200
+ - spec/pdi/executor_spec.rb
197
201
  - spec/pdi/spoon/kitchen_error_spec.rb
198
202
  - spec/pdi/spoon/options/arg_spec.rb
199
203
  - spec/pdi/spoon/options/param_spec.rb