teckel 0.5.0 → 0.6.0

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