dry-effects 0.1.0.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 +7 -0
- data/.codeclimate.yml +15 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +73 -0
- data/.travis.yml +31 -0
- data/CHANGELOG.md +3 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +22 -0
- data/LICENSE +21 -0
- data/README.md +20 -0
- data/Rakefile +8 -0
- data/dry-effects.gemspec +48 -0
- data/examples/amb.rb +51 -0
- data/examples/state.rb +29 -0
- data/lib/dry/effects.rb +42 -0
- data/lib/dry/effects/all.rb +47 -0
- data/lib/dry/effects/constructors.rb +8 -0
- data/lib/dry/effects/container.rb +11 -0
- data/lib/dry/effects/effect.rb +29 -0
- data/lib/dry/effects/effects/amb.rb +23 -0
- data/lib/dry/effects/effects/async.rb +22 -0
- data/lib/dry/effects/effects/cache.rb +67 -0
- data/lib/dry/effects/effects/current_time.rb +27 -0
- data/lib/dry/effects/effects/defer.rb +31 -0
- data/lib/dry/effects/effects/env.rb +31 -0
- data/lib/dry/effects/effects/fork.rb +21 -0
- data/lib/dry/effects/effects/implicit.rb +25 -0
- data/lib/dry/effects/effects/interrupt.rb +29 -0
- data/lib/dry/effects/effects/lock.rb +45 -0
- data/lib/dry/effects/effects/parallel.rb +19 -0
- data/lib/dry/effects/effects/random.rb +19 -0
- data/lib/dry/effects/effects/reader.rb +15 -0
- data/lib/dry/effects/effects/resolve.rb +26 -0
- data/lib/dry/effects/effects/retry.rb +26 -0
- data/lib/dry/effects/effects/state.rb +50 -0
- data/lib/dry/effects/errors.rb +68 -0
- data/lib/dry/effects/extensions.rb +13 -0
- data/lib/dry/effects/extensions/auto_inject.rb +67 -0
- data/lib/dry/effects/extensions/system.rb +43 -0
- data/lib/dry/effects/halt.rb +29 -0
- data/lib/dry/effects/handler.rb +58 -0
- data/lib/dry/effects/inflector.rb +9 -0
- data/lib/dry/effects/initializer.rb +99 -0
- data/lib/dry/effects/instruction.rb +8 -0
- data/lib/dry/effects/instructions/execute.rb +25 -0
- data/lib/dry/effects/instructions/raise.rb +25 -0
- data/lib/dry/effects/provider.rb +29 -0
- data/lib/dry/effects/provider/class_interface.rb +61 -0
- data/lib/dry/effects/providers/amb.rb +36 -0
- data/lib/dry/effects/providers/async.rb +31 -0
- data/lib/dry/effects/providers/cache.rb +43 -0
- data/lib/dry/effects/providers/current_time.rb +49 -0
- data/lib/dry/effects/providers/defer.rb +84 -0
- data/lib/dry/effects/providers/env.rb +65 -0
- data/lib/dry/effects/providers/fork.rb +23 -0
- data/lib/dry/effects/providers/implicit.rb +39 -0
- data/lib/dry/effects/providers/interrupt.rb +37 -0
- data/lib/dry/effects/providers/lock.rb +125 -0
- data/lib/dry/effects/providers/parallel.rb +34 -0
- data/lib/dry/effects/providers/random.rb +13 -0
- data/lib/dry/effects/providers/reader.rb +61 -0
- data/lib/dry/effects/providers/resolve.rb +88 -0
- data/lib/dry/effects/providers/retry.rb +59 -0
- data/lib/dry/effects/providers/state.rb +30 -0
- data/lib/dry/effects/stack.rb +67 -0
- data/lib/dry/effects/version.rb +7 -0
- metadata +263 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/equalizer'
|
4
|
+
require 'dry/effects/initializer'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Effects
|
8
|
+
class Effect
|
9
|
+
extend Initializer
|
10
|
+
|
11
|
+
include ::Dry::Equalizer(:type, :name, :payload)
|
12
|
+
|
13
|
+
option :type
|
14
|
+
|
15
|
+
option :name, default: -> { type }
|
16
|
+
|
17
|
+
option :payload, default: -> { EMPTY_ARRAY }
|
18
|
+
|
19
|
+
def payload(*payload)
|
20
|
+
if payload.empty?
|
21
|
+
@payload
|
22
|
+
else
|
23
|
+
with(payload: payload)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method :call, :payload
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Amb < ::Module
|
9
|
+
class AmbEffect < Effect
|
10
|
+
option :id
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(id)
|
14
|
+
get = AmbEffect.new(type: :amb, name: :get, id: id)
|
15
|
+
|
16
|
+
module_eval do
|
17
|
+
define_method(:"#{id}?") { ::Dry::Effects.yield(get) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Async < ::Module
|
9
|
+
Async = Effect.new(type: :async, name: :async)
|
10
|
+
|
11
|
+
Await = Effect.new(type: :async, name: :await)
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
module_eval do
|
15
|
+
define_method(:async) { |&block| ::Dry::Effects.yield(Async.payload(block)) }
|
16
|
+
define_method(:await) { |task| ::Dry::Effects.yield(Await.payload(task)) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Cache < ::Module
|
9
|
+
class CacheEffect < Effect
|
10
|
+
option :scope
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(scope, shared: false)
|
14
|
+
if scope.is_a?(::Hash)
|
15
|
+
scope, as = scope.to_a[0]
|
16
|
+
else
|
17
|
+
as = :cache
|
18
|
+
end
|
19
|
+
|
20
|
+
fetch_or_store = CacheEffect.new(
|
21
|
+
type: :cache,
|
22
|
+
name: :fetch_or_store,
|
23
|
+
scope: scope
|
24
|
+
)
|
25
|
+
|
26
|
+
if shared
|
27
|
+
key = method(:shared_cache_key)
|
28
|
+
else
|
29
|
+
key = method(:cache_key)
|
30
|
+
end
|
31
|
+
|
32
|
+
methods = Array(as)
|
33
|
+
|
34
|
+
module_eval do
|
35
|
+
methods.each do |meth|
|
36
|
+
define_method(meth) do |*args, &block|
|
37
|
+
if block
|
38
|
+
eff = fetch_or_store.(key.(self, args), block)
|
39
|
+
else
|
40
|
+
eff = fetch_or_store.(key.(self, args, method: meth), -> { super(*args) })
|
41
|
+
end
|
42
|
+
|
43
|
+
::Dry::Effects.yield(eff)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def shared_cache_key(_, args, method: Undefined)
|
50
|
+
if Undefined.equal?(method)
|
51
|
+
args
|
52
|
+
else
|
53
|
+
[method, args]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def cache_key(instance, args, method: Undefined)
|
58
|
+
if Undefined.equal?(method)
|
59
|
+
[instance, args]
|
60
|
+
else
|
61
|
+
[instance, method, args]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class CurrentTime < ::Module
|
9
|
+
CurrentTime = Effect.new(type: :current_time)
|
10
|
+
|
11
|
+
def initialize(round: Undefined)
|
12
|
+
get = CurrentTime.payload(round_to: round)
|
13
|
+
|
14
|
+
module_eval do
|
15
|
+
define_method(:current_time) do |round: Undefined|
|
16
|
+
if Undefined.equal?(round)
|
17
|
+
::Dry::Effects.yield(get)
|
18
|
+
else
|
19
|
+
::Dry::Effects.yield(get.payload(round_to: round))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Defer < ::Module
|
9
|
+
Defer = Effect.new(type: :defer, name: :defer)
|
10
|
+
Later = Effect.new(type: :defer, name: :later)
|
11
|
+
Wait = Effect.new(type: :defer, name: :wait)
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
module_eval do
|
15
|
+
define_method(:defer) do |executor: Undefined, &block|
|
16
|
+
::Dry::Effects.yield(Defer.(block, executor))
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method(:wait) do |promises|
|
20
|
+
::Dry::Effects.yield(Wait.(promises))
|
21
|
+
end
|
22
|
+
|
23
|
+
define_method(:later) do |executor: Undefined, &block|
|
24
|
+
::Dry::Effects.yield(Later.(block, executor))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Env < ::Module
|
9
|
+
Read = Effect.new(type: :env, name: :read)
|
10
|
+
|
11
|
+
def initialize(*args, **kwargs)
|
12
|
+
readers = args.zip(args) + kwargs.to_a
|
13
|
+
|
14
|
+
module_eval do
|
15
|
+
if readers.empty?
|
16
|
+
define_method(:env) do |key|
|
17
|
+
::Dry::Effects.yield(Read.(key))
|
18
|
+
end
|
19
|
+
else
|
20
|
+
readers.each do |reader, key|
|
21
|
+
define_method(reader) do
|
22
|
+
::Dry::Effects.yield(Read.(key))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Fork < ::Module
|
9
|
+
Fork = Effect.new(type: :fork)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
13
|
+
def fork
|
14
|
+
yield(::Dry::Effects.yield(Fork))
|
15
|
+
end
|
16
|
+
RUBY
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Implicit < ::Module
|
9
|
+
class ImplicitEffect < Effect
|
10
|
+
option :dependency
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(dependency)
|
14
|
+
lookup = ImplicitEffect.new(type: :implicit, dependency: dependency)
|
15
|
+
|
16
|
+
module_eval do
|
17
|
+
define_method(dependency) do |*args|
|
18
|
+
::Dry::Effects.yield(lookup.(args[0])).(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Interrupt < ::Module
|
9
|
+
class InterruptEffect < Effect
|
10
|
+
option :scope
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(scope = :default)
|
14
|
+
interrupt = InterruptEffect.new(type: :interrupt, scope: scope)
|
15
|
+
|
16
|
+
module_eval do
|
17
|
+
define_method(scope) do |payload = Undefined|
|
18
|
+
if Undefined.equal?(payload)
|
19
|
+
::Dry::Effects.yield(interrupt)
|
20
|
+
else
|
21
|
+
::Dry::Effects.yield(interrupt.(payload))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Lock < ::Module
|
9
|
+
Lock = Effect.new(type: :lock, name: :lock)
|
10
|
+
Meta = Effect.new(type: :lock, name: :meta)
|
11
|
+
Unlock = Effect.new(type: :lock, name: :unlock)
|
12
|
+
Locked = Effect.new(type: :lock, name: :locked?)
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
module_eval do
|
16
|
+
define_method(:lock) do |key, meta: Undefined, &block|
|
17
|
+
if block
|
18
|
+
begin
|
19
|
+
handle = ::Dry::Effects.yield(Lock.(key, meta))
|
20
|
+
block.(!handle.nil?)
|
21
|
+
ensure
|
22
|
+
::Dry::Effects.yield(Unlock.(handle)) if handle
|
23
|
+
end
|
24
|
+
else
|
25
|
+
::Dry::Effects.yield(Lock.(key, meta))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method(:unlock) do |key|
|
30
|
+
::Dry::Effects.yield(Unlock.(key))
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method(:locked?) do |key|
|
34
|
+
::Dry::Effects.yield(Locked.(key))
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method(:lock_meta) do |key|
|
38
|
+
::Dry::Effects.yield(Meta.(key))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Parallel < ::Module
|
9
|
+
Par = Effect.new(type: :parallel, name: :par)
|
10
|
+
Join = Effect.new(type: :parallel, name: :join)
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
define_method(:par) { |&block| ::Dry::Effects.yield(Par).(&block) }
|
14
|
+
define_method(:join) { |xs| ::Dry::Effects.yield(Join.(xs)) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/effects/effect'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Effects
|
7
|
+
module Effects
|
8
|
+
class Random < ::Module
|
9
|
+
Read = Effect.new(type: :random, name: :rand)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
module_eval do
|
13
|
+
define_method(:rand) { |n| ::Dry::Effects.yield(Read.(n)) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|