dry-effects 0.1.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|