pdi 1.0.1 → 2.0.0.pre.alpha

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