teckel 0.5.0 → 0.6.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
  SHA256:
3
- metadata.gz: bf2c470202a0d90fbc79ed7d6c7e065634f34f6237d01d8d9772489f7ba43e94
4
- data.tar.gz: 0b2937e820b2f61bf37d835521c6b981e95e50c3f1ef4d5e968ee38dfea7c5fa
3
+ metadata.gz: d55dd7fb782cfdcb3cc960423e46ab2ce8b49c9396273f10749105883d55ed6f
4
+ data.tar.gz: e3b7428d98daeb42cc086b4a5cd5081b5c3b4ca90c14b0cf3f4cd9576243d0e3
5
5
  SHA512:
6
- metadata.gz: 3cf2096a7827c2547e6c4cc135657d68dcc597d057e202d0c8cda91753eee09648133e3c6728a3ae1ec14f140fe562b2648ae77439cccfa015a5130326557fba
7
- data.tar.gz: 5f13605ac5dc726ce964a85225d1e860a22d00b8eabae78616fa998e77c94ec7251f1367f07175868e8724477bc95c27945d93e23bee0da68303a452a1180e09
6
+ metadata.gz: '0148d426e8e7124a08a3a7dd3c42487cac5944d17ba9cabb98a8c378a86a5d9813b7b62c483d2203809644528d2e6b6cc4de51476aa67261b1405f3f41576613'
7
+ data.tar.gz: 6792ad4806b977d21d0ea621e311306cb13c239d8604e35e61937e79ce4f5488cbb410aa6ae22e9e12e48b61529f97e924aab22e100717808885c0b0c76980fd
@@ -1,5 +1,11 @@
1
1
  # Changes
2
2
 
3
+ ## 0.6.0
4
+
5
+ - Breaking: Operations return values will be ignored. [GH-21]
6
+ * You'll need to use `success!` or `failure!`
7
+ * `success!` and `failure!` are now implemented on the `Runner`, which makes it easier to change their behavior (including the one above).
8
+
3
9
  ## 0.5.0
4
10
 
5
11
  - Fix: calling chain with settings and no input [GH-14]
@@ -41,7 +41,7 @@ module Teckel
41
41
  # output input
42
42
  #
43
43
  # def call(hsh)
44
- # hsh
44
+ # success!(hsh)
45
45
  # end
46
46
  # end
47
47
  #
@@ -139,7 +139,7 @@ module Teckel
139
139
  # error none
140
140
  #
141
141
  # def call(_)
142
- # settings.to_h
142
+ # success!(settings.to_h)
143
143
  # end
144
144
  # end
145
145
  #
@@ -216,7 +216,7 @@ module Teckel
216
216
 
217
217
  # @!visibility private
218
218
  def inherited(subclass)
219
- dup_config(subclass)
219
+ super dup_config(subclass)
220
220
  end
221
221
 
222
222
  # @!visibility private
@@ -140,9 +140,6 @@ module Teckel
140
140
  #
141
141
  # # when using `output none`:
142
142
  # # `success!` works, but `success!("data")` raises an error
143
- # # same thing when using simple return values as success:
144
- # # take care to not return anything
145
- # nil
146
143
  # end
147
144
  # end
148
145
  #
@@ -153,26 +150,39 @@ module Teckel
153
150
  end
154
151
 
155
152
  module InstanceMethods
153
+ # @!method call(input)
154
+ # @abstract
155
+ # @see Operation
156
+ # @see ClassMethods#call
157
+ #
158
+ # The entry point for your operation. It needs to always accept an input value, even when
159
+ # using +input none+.
160
+ # If your Operation expects to generate success or failure outputs, you need to use either
161
+ # {.success!} or {.fail!} respectively. Simple return values will get ignored by default. See
162
+ # {Teckel::Operation::Config#runner} and {Teckel::Operation::Runner} on how to overwrite.
163
+
156
164
  # @!attribute [r] settings()
157
165
  # @return [Class,nil] When executed with settings, an instance of the
158
166
  # configured {.settings} class. Otherwise +nil+
159
167
  # @see ClassMethods#settings
160
168
  # @!visibility public
161
169
 
162
- # Halt any further execution with a output value
170
+ # Delegates to the configured Runner.
171
+ # The default behavior is to halt any further execution with a output value.
163
172
  #
164
- # @return a thing matching your {Teckel::Operation::Config#output output} definition
173
+ # @see Teckel::Operation::Runner#success!
165
174
  # @!visibility protected
166
175
  def success!(*args)
167
- throw :success, args
176
+ runner.success!(*args)
168
177
  end
169
178
 
170
- # Halt any further execution with an error value
179
+ # Delegates to the configured Runner.
180
+ # The default behavior is to halt any further execution with an error value.
171
181
  #
172
- # @return a thing matching your {Teckel::Operation::Config#error error} definition
182
+ # @see Teckel::Operation::Runner#fail!
173
183
  # @!visibility protected
174
184
  def fail!(*args)
175
- throw :failure, args
185
+ runner.fail!(*args)
176
186
  end
177
187
  end
178
188
 
@@ -362,12 +362,14 @@ module Teckel
362
362
  # @!visibility private
363
363
  def inherited(subclass)
364
364
  subclass.instance_variable_set(:@config, @config.dup)
365
+ super subclass
365
366
  end
366
367
 
367
368
  # @!visibility private
368
369
  def self.extended(base)
369
370
  base.instance_exec do
370
371
  @config = Teckel::Config.new
372
+ attr_accessor :runner
371
373
  attr_accessor :settings
372
374
  end
373
375
  end
@@ -16,14 +16,14 @@ module Teckel
16
16
  attr_reader :operation, :settings
17
17
 
18
18
  def call(input = nil)
19
- err = catch(:failure) do
20
- simple_return = UNDEFINED
19
+ catch(:failure) do
21
20
  out = catch(:success) do
22
- simple_return = run(build_input(input))
21
+ run operation.input_constructor.call(input)
22
+ return nil # :sic!: return values need to go through +success!+
23
23
  end
24
- return simple_return == UNDEFINED ? build_output(*out) : build_output(simple_return)
24
+
25
+ return out
25
26
  end
26
- build_error(*err)
27
27
  end
28
28
 
29
29
  # This is just here to raise a meaningful error.
@@ -32,19 +32,11 @@ module Teckel
32
32
  raise Teckel::Error, "Operation already has settings assigned."
33
33
  end
34
34
 
35
- private
36
-
37
- def run(input)
38
- op = @operation.new
39
- op.settings = settings if settings != UNDEFINED
40
- op.call(input)
41
- end
42
-
43
- def build_input(input)
44
- operation.input_constructor.call(input)
45
- end
46
-
47
- def build_output(*args)
35
+ # Halt any further execution with a output value
36
+ #
37
+ # @return a thing matching your {Teckel::Operation::Config#output output} definition
38
+ # @!visibility protected
39
+ def success!(*args)
48
40
  value =
49
41
  if args.size == 1 && operation.output === args.first # rubocop:disable Style/CaseEquality
50
42
  args.first
@@ -52,10 +44,14 @@ module Teckel
52
44
  operation.output_constructor.call(*args)
53
45
  end
54
46
 
55
- operation.result_constructor.call(value, true)
47
+ throw :success, operation.result_constructor.call(value, true)
56
48
  end
57
49
 
58
- def build_error(*args)
50
+ # Halt any further execution with an error value
51
+ #
52
+ # @return a thing matching your {Teckel::Operation::Config#error error} definition
53
+ # @!visibility protected
54
+ def fail!(*args)
59
55
  value =
60
56
  if args.size == 1 && operation.error === args.first # rubocop:disable Style/CaseEquality
61
57
  args.first
@@ -63,7 +59,16 @@ module Teckel
63
59
  operation.error_constructor.call(*args)
64
60
  end
65
61
 
66
- operation.result_constructor.call(value, false)
62
+ throw :failure, operation.result_constructor.call(value, false)
63
+ end
64
+
65
+ private
66
+
67
+ def run(input)
68
+ op = @operation.new
69
+ op.runner = self
70
+ op.settings = settings if settings != UNDEFINED
71
+ op.call(input)
67
72
  end
68
73
  end
69
74
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Teckel
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -1,32 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe Teckel::Chain do
4
- module TeckelChainDefaultSettingsTest
5
- class MyOperation
6
- include Teckel::Operation
7
- result!
3
+ module TeckelChainDefaultSettingsTest
4
+ class MyOperation
5
+ include Teckel::Operation
6
+ result!
8
7
 
9
- settings Struct.new(:say, :other)
10
- settings_constructor ->(data) { settings.new(*data.values_at(*settings.members)) } # ruby 2.4 way for `keyword_init: true`
8
+ settings Struct.new(:say, :other)
9
+ settings_constructor ->(data) { settings.new(*data.values_at(*settings.members)) } # ruby 2.4 way for `keyword_init: true`
11
10
 
12
- input none
13
- output Hash
14
- error none
11
+ input none
12
+ output Hash
13
+ error none
15
14
 
16
- def call(_)
17
- settings.to_h
18
- end
15
+ def call(_)
16
+ success! settings.to_h
19
17
  end
18
+ end
20
19
 
21
- class Chain
22
- include Teckel::Chain
20
+ class Chain
21
+ include Teckel::Chain
23
22
 
24
- default_settings!(a: { say: "Chain Default" })
23
+ default_settings!(a: { say: "Chain Default" })
25
24
 
26
- step :a, MyOperation
27
- end
25
+ step :a, MyOperation
28
26
  end
27
+ end
29
28
 
29
+ RSpec.describe Teckel::Chain do
30
30
  specify "call chain without settings, uses default settings" do
31
31
  result = TeckelChainDefaultSettingsTest::Chain.call
32
32
  expect(result.success).to eq(say: "Chain Default", other: nil)
@@ -3,72 +3,72 @@
3
3
  require 'support/dry_base'
4
4
  require 'support/fake_models'
5
5
 
6
- RSpec.describe Teckel::Chain do
7
- module TeckelChainDefaultsViaBaseClass
8
- LOG = [] # rubocop:disable Style/MutableConstant
9
-
10
- class LoggingChain
11
- include Teckel::Chain
6
+ module TeckelChainDefaultsViaBaseClass
7
+ LOG = [] # rubocop:disable Style/MutableConstant
12
8
 
13
- around do |chain, input|
14
- require 'benchmark'
15
- result = nil
16
- LOG << Benchmark.measure { result = chain.call(input) }
17
- result
18
- end
9
+ class LoggingChain
10
+ include Teckel::Chain
19
11
 
20
- freeze
12
+ around do |chain, input|
13
+ require 'benchmark'
14
+ result = nil
15
+ LOG << Benchmark.measure { result = chain.call(input) }
16
+ result
21
17
  end
22
18
 
23
- class OperationA
24
- include Teckel::Operation
19
+ freeze
20
+ end
25
21
 
26
- result!
22
+ class OperationA
23
+ include Teckel::Operation
27
24
 
28
- input none
29
- output Types::Integer
30
- error none
25
+ result!
31
26
 
32
- def call(_)
33
- rand(1000)
34
- end
27
+ input none
28
+ output Types::Integer
29
+ error none
35
30
 
36
- finalize!
31
+ def call(_)
32
+ success! rand(1000)
37
33
  end
38
34
 
39
- class OperationB
40
- include Teckel::Operation
35
+ finalize!
36
+ end
41
37
 
42
- result!
38
+ class OperationB
39
+ include Teckel::Operation
43
40
 
44
- input none
45
- output Types::String
46
- error none
41
+ result!
47
42
 
48
- def call(_)
49
- ("a".."z").to_a.sample
50
- end
43
+ input none
44
+ output Types::String
45
+ error none
51
46
 
52
- finalize!
47
+ def call(_)
48
+ success! ("a".."z").to_a.sample
53
49
  end
54
50
 
55
- class ChainA < LoggingChain
56
- step :roll, OperationA
51
+ finalize!
52
+ end
57
53
 
58
- finalize!
59
- end
54
+ class ChainA < LoggingChain
55
+ step :roll, OperationA
60
56
 
61
- class ChainB < LoggingChain
62
- step :say, OperationB
57
+ finalize!
58
+ end
63
59
 
64
- finalize!
65
- end
60
+ class ChainB < LoggingChain
61
+ step :say, OperationB
66
62
 
67
- class ChainC < ChainB
68
- finalize!
69
- end
63
+ finalize!
64
+ end
65
+
66
+ class ChainC < ChainB
67
+ finalize!
70
68
  end
69
+ end
71
70
 
71
+ RSpec.describe Teckel::Chain do
72
72
  before do
73
73
  TeckelChainDefaultsViaBaseClass::LOG.clear
74
74
  end
@@ -1,29 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe Teckel::Chain do
4
- module TeckelChainNoneInputTest
5
- class MyOperation
6
- include Teckel::Operation
7
- result!
3
+ module TeckelChainNoneInputTest
4
+ class MyOperation
5
+ include Teckel::Operation
6
+ result!
8
7
 
9
- settings Struct.new(:say)
8
+ settings Struct.new(:say)
10
9
 
11
- input none
12
- output String
13
- error none
10
+ input none
11
+ output String
12
+ error none
14
13
 
15
- def call(_)
16
- settings&.say || "Called"
17
- end
14
+ def call(_)
15
+ success!(settings&.say || "Called")
18
16
  end
17
+ end
19
18
 
20
- class Chain
21
- include Teckel::Chain
19
+ class Chain
20
+ include Teckel::Chain
22
21
 
23
- step :a, MyOperation
24
- end
22
+ step :a, MyOperation
25
23
  end
24
+ end
26
25
 
26
+ RSpec.describe Teckel::Chain do
27
27
  specify "call chain without input value" do
28
28
  result = TeckelChainNoneInputTest::Chain.call
29
29
  expect(result.success).to eq("Called")
@@ -3,47 +3,47 @@
3
3
  require 'support/dry_base'
4
4
  require 'support/fake_models'
5
5
 
6
- RSpec.describe Teckel::Chain do
7
- module TeckelChainResultTest
8
- class Message
9
- include ::Teckel::Operation
6
+ module TeckelChainResultTest
7
+ class Message
8
+ include ::Teckel::Operation
10
9
 
11
- result!
10
+ result!
12
11
 
13
- input Types::Hash.schema(message: Types::String)
14
- error none
15
- output Types::String
12
+ input Types::Hash.schema(message: Types::String)
13
+ error none
14
+ output Types::String
16
15
 
17
- def call(input)
18
- input[:message].upcase
19
- end
16
+ def call(input)
17
+ success! input[:message].upcase
20
18
  end
19
+ end
21
20
 
22
- class Chain
23
- include Teckel::Chain
24
-
25
- step :message, Message
21
+ class Chain
22
+ include Teckel::Chain
26
23
 
27
- class Result < Teckel::Operation::Result
28
- def initialize(value, success, step, opts = {})
29
- super(value, success)
30
- @step = step
31
- @opts = opts
32
- end
24
+ step :message, Message
33
25
 
34
- class << self
35
- alias :[] :new # Alias the default constructor to :new
36
- end
26
+ class Result < Teckel::Operation::Result
27
+ def initialize(value, success, step, opts = {})
28
+ super(value, success)
29
+ @step = step
30
+ @opts = opts
31
+ end
37
32
 
38
- attr_reader :opts, :step
33
+ class << self
34
+ alias :[] :new # Alias the default constructor to :new
39
35
  end
40
36
 
41
- result_constructor ->(value, success, step) {
42
- result.new(value, success, step, time: Time.now.to_i)
43
- }
37
+ attr_reader :opts, :step
44
38
  end
39
+
40
+ result_constructor ->(value, success, step) {
41
+ result.new(value, success, step, time: Time.now.to_i)
42
+ }
45
43
  end
44
+ end
46
45
 
46
+ RSpec.describe Teckel::Chain do
47
47
  specify do
48
48
  result = TeckelChainResultTest::Chain.call(message: "Hello World!")
49
49
  expect(result).to be_successful