dry-effects 0.1.0.alpha2 → 0.1.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 +4 -4
- data/.travis.yml +4 -4
- data/examples/{amb.rb → cmp.rb} +8 -8
- data/lib/dry/effects.rb +33 -2
- data/lib/dry/effects/all.rb +3 -2
- data/lib/dry/effects/constructors.rb +4 -0
- data/lib/dry/effects/effects/{amb.rb → cmp.rb} +3 -3
- data/lib/dry/effects/effects/current_time.rb +9 -0
- data/lib/dry/effects/effects/resolve.rb +1 -3
- data/lib/dry/effects/effects/retry.rb +7 -2
- data/lib/dry/effects/effects/state.rb +12 -3
- data/lib/dry/effects/effects/timeout.rb +31 -0
- data/lib/dry/effects/errors.rb +19 -0
- data/lib/dry/effects/frame.rb +80 -0
- data/lib/dry/effects/handler.rb +11 -44
- data/lib/dry/effects/provider.rb +19 -0
- data/lib/dry/effects/provider/class_interface.rb +4 -3
- data/lib/dry/effects/providers/async.rb +4 -1
- data/lib/dry/effects/providers/cache.rb +8 -0
- data/lib/dry/effects/providers/cmp.rb +50 -0
- data/lib/dry/effects/providers/current_time.rb +38 -21
- data/lib/dry/effects/providers/defer.rb +8 -3
- data/lib/dry/effects/providers/env.rb +10 -0
- data/lib/dry/effects/providers/fork.rb +1 -1
- data/lib/dry/effects/providers/implicit.rb +6 -0
- data/lib/dry/effects/providers/interrupt.rb +10 -2
- data/lib/dry/effects/providers/lock.rb +7 -0
- data/lib/dry/effects/providers/parallel.rb +4 -1
- data/lib/dry/effects/providers/reader.rb +8 -1
- data/lib/dry/effects/providers/resolve.rb +17 -0
- data/lib/dry/effects/providers/retry.rb +6 -1
- data/lib/dry/effects/providers/state.rb +6 -0
- data/lib/dry/effects/providers/timeout.rb +47 -0
- data/lib/dry/effects/providers/timestamp.rb +27 -16
- data/lib/dry/effects/version.rb +1 -1
- metadata +11 -8
- data/lib/dry/effects/providers/amb.rb +0 -36
data/lib/dry/effects/handler.rb
CHANGED
@@ -1,58 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'fiber'
|
4
|
-
require 'dry/effects/initializer'
|
5
|
-
require 'dry/effects/effect'
|
6
|
-
require 'dry/effects/errors'
|
7
|
-
require 'dry/effects/stack'
|
8
|
-
require 'dry/effects/instructions/raise'
|
9
|
-
|
10
3
|
module Dry
|
11
4
|
module Effects
|
12
5
|
class Handler
|
13
|
-
|
14
|
-
def stack
|
15
|
-
::Thread.current[:dry_effects_stack] ||= Stack.new
|
16
|
-
end
|
17
|
-
|
18
|
-
def stack=(stack)
|
19
|
-
::Thread.current[:dry_effects_stack] = stack
|
20
|
-
end
|
21
|
-
|
22
|
-
def spawn_fiber(stack)
|
23
|
-
fiber = ::Fiber.new do
|
24
|
-
self.stack = stack
|
25
|
-
yield
|
26
|
-
end
|
27
|
-
result = fiber.resume
|
28
|
-
|
29
|
-
loop do
|
30
|
-
break result unless fiber.alive?
|
6
|
+
attr_reader :provider
|
31
7
|
|
32
|
-
|
33
|
-
::Dry::Effects.yield(result) do |_, error|
|
34
|
-
Instructions.Raise(error)
|
35
|
-
end
|
36
|
-
end
|
8
|
+
attr_reader :frame
|
37
9
|
|
38
|
-
|
39
|
-
|
40
|
-
|
10
|
+
def initialize(type, *args)
|
11
|
+
@provider = ::Dry::Effects.providers[type].new(*args)
|
12
|
+
@frame = Frame.new(provider)
|
41
13
|
end
|
42
14
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def call(args = EMPTY_ARRAY, &block)
|
48
|
-
stack = Handler.stack
|
15
|
+
def call(*args, &block)
|
16
|
+
frame.(args, &block)
|
17
|
+
end
|
49
18
|
|
50
|
-
|
51
|
-
|
52
|
-
else
|
53
|
-
stack.push(provider.dup, args, &block)
|
54
|
-
end
|
19
|
+
def to_s
|
20
|
+
"#<Dry::Effects::Handler #{provider.represent}>"
|
55
21
|
end
|
22
|
+
alias_method :inspect, :to_s
|
56
23
|
end
|
57
24
|
end
|
58
25
|
end
|
data/lib/dry/effects/provider.rb
CHANGED
@@ -5,22 +5,41 @@ require 'dry/effects/provider/class_interface'
|
|
5
5
|
|
6
6
|
module Dry
|
7
7
|
module Effects
|
8
|
+
# Base class for effect providers
|
9
|
+
#
|
10
|
+
# @api private
|
8
11
|
class Provider
|
9
12
|
extend Initializer
|
10
13
|
extend ClassInterface
|
11
14
|
|
15
|
+
# yield the block with the handler installed
|
16
|
+
#
|
17
|
+
# @api private
|
12
18
|
def call(_stack)
|
13
19
|
yield
|
14
20
|
end
|
15
21
|
|
22
|
+
# Effect-specific representation of the provider
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
# @api public
|
16
26
|
def represent
|
17
27
|
type.to_s
|
18
28
|
end
|
19
29
|
|
30
|
+
# Effect type
|
31
|
+
#
|
32
|
+
# @return [Symbol]
|
33
|
+
# @api public
|
20
34
|
def type
|
21
35
|
self.class.type
|
22
36
|
end
|
23
37
|
|
38
|
+
# Whether the effect can be handled?
|
39
|
+
#
|
40
|
+
# @param [Effect] effect
|
41
|
+
# @return [Boolean]
|
42
|
+
# @api public
|
24
43
|
def provide?(effect)
|
25
44
|
type.equal?(effect.type)
|
26
45
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'dry/core/class_attributes'
|
4
|
+
require 'dry/effects/frame'
|
4
5
|
|
5
6
|
module Dry
|
6
7
|
module Effects
|
@@ -30,7 +31,7 @@ module Dry
|
|
30
31
|
def [](type)
|
31
32
|
if self < Provider
|
32
33
|
Provider.effects.fetch(type) do
|
33
|
-
Provider.effects[type] = Class.new(self).tap do |subclass|
|
34
|
+
Provider.effects[type] = ::Class.new(self).tap do |subclass|
|
34
35
|
subclass.type type
|
35
36
|
end
|
36
37
|
end
|
@@ -43,11 +44,11 @@ module Dry
|
|
43
44
|
handle_method = handle_method(*args, **kwargs)
|
44
45
|
|
45
46
|
provider = new(*args, **kwargs).freeze
|
46
|
-
|
47
|
+
frame = Frame.new(provider)
|
47
48
|
|
48
49
|
::Module.new do
|
49
50
|
define_method(handle_method) do |*xs, &block|
|
50
|
-
|
51
|
+
frame.(xs, &block)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
@@ -17,9 +17,12 @@ module Dry
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def await(task)
|
20
|
-
|
20
|
+
Frame.spawn_fiber(stack, &@tasks.delete(task))
|
21
21
|
end
|
22
22
|
|
23
|
+
# Yield the block with the handler installed
|
24
|
+
#
|
25
|
+
# @api private
|
23
26
|
def call(stack)
|
24
27
|
@stack = stack
|
25
28
|
super
|
@@ -21,15 +21,23 @@ module Dry
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
# Yield the block with the handler installed
|
25
|
+
#
|
26
|
+
# @api private
|
24
27
|
def call(stack, cache = EMPTY_HASH.dup)
|
25
28
|
@cache = cache
|
26
29
|
super(stack)
|
27
30
|
end
|
28
31
|
|
32
|
+
# @param [Effect] effect
|
33
|
+
# @return [Boolean]
|
34
|
+
# @api public
|
29
35
|
def provide?(effect)
|
30
36
|
super && scope.eql?(effect.scope)
|
31
37
|
end
|
32
38
|
|
39
|
+
# @return [String]
|
40
|
+
# @api public
|
33
41
|
def represent
|
34
42
|
if cache.empty?
|
35
43
|
"cache[#{scope} empty]"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/provider'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Providers
|
8
|
+
class Cmp < Provider[:cmp]
|
9
|
+
include Dry::Equalizer(:id, :value)
|
10
|
+
|
11
|
+
attr_reader :value
|
12
|
+
|
13
|
+
param :id
|
14
|
+
|
15
|
+
def get
|
16
|
+
value
|
17
|
+
end
|
18
|
+
|
19
|
+
# Yield the block with the handler installed
|
20
|
+
#
|
21
|
+
# @return [Array(Any, Any)]
|
22
|
+
# @api private
|
23
|
+
def call(stack, value = Undefined)
|
24
|
+
if Undefined.equal?(value)
|
25
|
+
@value = false
|
26
|
+
first = super(stack)
|
27
|
+
@value = true
|
28
|
+
[first, super(stack)]
|
29
|
+
else
|
30
|
+
@value = value
|
31
|
+
super(stack)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [Effect] effect
|
36
|
+
# @return [Boolean]
|
37
|
+
# @api public
|
38
|
+
def provide?(effect)
|
39
|
+
super && id.equal?(effect.id)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String]
|
43
|
+
# @api public
|
44
|
+
def represent
|
45
|
+
"cmp[#{id}=#{@value}]"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -20,31 +20,14 @@ module Dry
|
|
20
20
|
|
21
21
|
attr_reader :generator
|
22
22
|
|
23
|
+
# Yield the block with the handler installed
|
24
|
+
#
|
25
|
+
# @api private
|
23
26
|
def call(stack, generator = Undefined, **options)
|
24
27
|
@generator = build_generator(generator, **options)
|
25
28
|
super(stack)
|
26
29
|
end
|
27
30
|
|
28
|
-
def build_generator(generator, step: Undefined, initial: Undefined, overridable: false)
|
29
|
-
if overridable
|
30
|
-
parent = ::Dry::Effects.yield(Locate) { nil }
|
31
|
-
else
|
32
|
-
parent = nil
|
33
|
-
end
|
34
|
-
|
35
|
-
if !parent.nil?
|
36
|
-
-> options { parent.current_time(options) }
|
37
|
-
elsif !Undefined.equal?(generator)
|
38
|
-
generator
|
39
|
-
elsif !Undefined.equal?(step)
|
40
|
-
IncrementingTimeGenerator.(initial, step)
|
41
|
-
elsif fixed?
|
42
|
-
FixedTimeGenerator.()
|
43
|
-
else
|
44
|
-
RunningTimeGenerator.()
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
31
|
def current_time(round_to: Undefined, **options)
|
49
32
|
time = generator.(**options)
|
50
33
|
|
@@ -57,17 +40,51 @@ module Dry
|
|
57
40
|
end
|
58
41
|
end
|
59
42
|
|
43
|
+
# Locate handler in the stack
|
44
|
+
#
|
45
|
+
# @return [Provider]
|
46
|
+
# @api private
|
60
47
|
def locate
|
61
48
|
self
|
62
49
|
end
|
63
50
|
|
51
|
+
# @return [String]
|
52
|
+
# @api public
|
64
53
|
def represent
|
65
54
|
if fixed?
|
66
|
-
|
55
|
+
if generator.nil?
|
56
|
+
'current_time[fixed=true]'
|
57
|
+
else
|
58
|
+
"current_time[fixed=#{generator.().iso8601(6)}]"
|
59
|
+
end
|
67
60
|
else
|
68
61
|
'current_time[fixed=false]'
|
69
62
|
end
|
70
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# @return [Proc] time generator
|
68
|
+
# @api private
|
69
|
+
def build_generator(generator, step: Undefined, initial: Undefined, overridable: false)
|
70
|
+
if overridable
|
71
|
+
parent = ::Dry::Effects.yield(Locate) { nil }
|
72
|
+
else
|
73
|
+
parent = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
if !parent.nil?
|
77
|
+
-> **options { parent.current_time(**options) }
|
78
|
+
elsif !Undefined.equal?(generator)
|
79
|
+
generator
|
80
|
+
elsif !Undefined.equal?(step)
|
81
|
+
IncrementingTimeGenerator.(initial, step)
|
82
|
+
elsif fixed?
|
83
|
+
FixedTimeGenerator.()
|
84
|
+
else
|
85
|
+
RunningTimeGenerator.()
|
86
|
+
end
|
87
|
+
end
|
71
88
|
end
|
72
89
|
end
|
73
90
|
end
|
@@ -19,21 +19,21 @@ module Dry
|
|
19
19
|
stack = self.stack.dup
|
20
20
|
at = Undefined.default(executor, self.executor)
|
21
21
|
::Concurrent::Promise.execute(executor: at) do
|
22
|
-
|
22
|
+
Frame.spawn_fiber(stack, &block)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
def later(block, executor)
|
27
27
|
if @later_calls.frozen?
|
28
28
|
Instructions.Raise(Errors::EffectRejectedError.new(<<~MSG))
|
29
|
-
.later calls are not allowed, they would processed
|
29
|
+
.later calls are not allowed, they would be processed
|
30
30
|
by another stack. Add another defer handler to the current stack
|
31
31
|
MSG
|
32
32
|
else
|
33
33
|
at = Undefined.default(executor, self.executor)
|
34
34
|
stack = self.stack.dup
|
35
35
|
@later_calls << ::Concurrent::Promise.new(executor: at) do
|
36
|
-
|
36
|
+
Frame.spawn_fiber(stack, &block)
|
37
37
|
end
|
38
38
|
nil
|
39
39
|
end
|
@@ -47,6 +47,9 @@ module Dry
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
# Yield the block with the handler installed
|
51
|
+
#
|
52
|
+
# @api private
|
50
53
|
def call(stack, executor: Undefined)
|
51
54
|
unless Undefined.equal?(executor)
|
52
55
|
@executor = executor
|
@@ -67,6 +70,8 @@ module Dry
|
|
67
70
|
end
|
68
71
|
end
|
69
72
|
|
73
|
+
# @return [String]
|
74
|
+
# @api public
|
70
75
|
def represent
|
71
76
|
info = []
|
72
77
|
info << executor.to_s if executor.is_a?(::Symbol)
|
@@ -30,10 +30,17 @@ module Dry
|
|
30
30
|
end
|
31
31
|
protected :fetch
|
32
32
|
|
33
|
+
# Locate handler in the stack
|
34
|
+
#
|
35
|
+
# @return [Provider]
|
36
|
+
# @api private
|
33
37
|
def locate
|
34
38
|
self
|
35
39
|
end
|
36
40
|
|
41
|
+
# Yield the block with the handler installed
|
42
|
+
#
|
43
|
+
# @api private
|
37
44
|
def call(stack, values = EMPTY_HASH, options = EMPTY_HASH)
|
38
45
|
unless values.empty?
|
39
46
|
@values = @values.merge(values)
|
@@ -48,6 +55,9 @@ module Dry
|
|
48
55
|
super(stack)
|
49
56
|
end
|
50
57
|
|
58
|
+
# @param [Effect] effect
|
59
|
+
# @return [Boolean]
|
60
|
+
# @api public
|
51
61
|
def provide?(effect)
|
52
62
|
if super
|
53
63
|
!effect.name.equal?(:read) || key?(effect.payload[0])
|
@@ -18,6 +18,9 @@ module Dry
|
|
18
18
|
dictionary.fetch(arg.class)
|
19
19
|
end
|
20
20
|
|
21
|
+
# Yield the block with the handler installed
|
22
|
+
#
|
23
|
+
# @api private
|
21
24
|
def call(stack, dynamic = EMPTY_HASH)
|
22
25
|
if dynamic.empty?
|
23
26
|
@dictionary = static
|
@@ -28,6 +31,9 @@ module Dry
|
|
28
31
|
super(stack)
|
29
32
|
end
|
30
33
|
|
34
|
+
# @param [Effect] effect
|
35
|
+
# @return [Boolean]
|
36
|
+
# @api public
|
31
37
|
def provide?(effect)
|
32
38
|
super &&
|
33
39
|
dependency.equal?(effect.dependency) &&
|
@@ -14,20 +14,28 @@ module Dry
|
|
14
14
|
Instructions.Raise(halt.new(payload))
|
15
15
|
end
|
16
16
|
|
17
|
+
# Yield the block with the handler installed
|
18
|
+
#
|
19
|
+
# @api private
|
17
20
|
def call(_stack)
|
18
|
-
yield
|
21
|
+
[false, yield]
|
19
22
|
rescue halt => e
|
20
|
-
e.payload[0]
|
23
|
+
[true, e.payload[0]]
|
21
24
|
end
|
22
25
|
|
23
26
|
def halt
|
24
27
|
Halt[scope]
|
25
28
|
end
|
26
29
|
|
30
|
+
# @return [String]
|
31
|
+
# @api public
|
27
32
|
def represent
|
28
33
|
"interrupt[#{scope}]"
|
29
34
|
end
|
30
35
|
|
36
|
+
# @param [Effect] effect
|
37
|
+
# @return [Boolean]
|
38
|
+
# @api public
|
31
39
|
def provide?(effect)
|
32
40
|
super && scope.equal?(effect.scope)
|
33
41
|
end
|