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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +73 -0
  6. data/.travis.yml +31 -0
  7. data/CHANGELOG.md +3 -0
  8. data/CONTRIBUTING.md +29 -0
  9. data/Gemfile +22 -0
  10. data/LICENSE +21 -0
  11. data/README.md +20 -0
  12. data/Rakefile +8 -0
  13. data/dry-effects.gemspec +48 -0
  14. data/examples/amb.rb +51 -0
  15. data/examples/state.rb +29 -0
  16. data/lib/dry/effects.rb +42 -0
  17. data/lib/dry/effects/all.rb +47 -0
  18. data/lib/dry/effects/constructors.rb +8 -0
  19. data/lib/dry/effects/container.rb +11 -0
  20. data/lib/dry/effects/effect.rb +29 -0
  21. data/lib/dry/effects/effects/amb.rb +23 -0
  22. data/lib/dry/effects/effects/async.rb +22 -0
  23. data/lib/dry/effects/effects/cache.rb +67 -0
  24. data/lib/dry/effects/effects/current_time.rb +27 -0
  25. data/lib/dry/effects/effects/defer.rb +31 -0
  26. data/lib/dry/effects/effects/env.rb +31 -0
  27. data/lib/dry/effects/effects/fork.rb +21 -0
  28. data/lib/dry/effects/effects/implicit.rb +25 -0
  29. data/lib/dry/effects/effects/interrupt.rb +29 -0
  30. data/lib/dry/effects/effects/lock.rb +45 -0
  31. data/lib/dry/effects/effects/parallel.rb +19 -0
  32. data/lib/dry/effects/effects/random.rb +19 -0
  33. data/lib/dry/effects/effects/reader.rb +15 -0
  34. data/lib/dry/effects/effects/resolve.rb +26 -0
  35. data/lib/dry/effects/effects/retry.rb +26 -0
  36. data/lib/dry/effects/effects/state.rb +50 -0
  37. data/lib/dry/effects/errors.rb +68 -0
  38. data/lib/dry/effects/extensions.rb +13 -0
  39. data/lib/dry/effects/extensions/auto_inject.rb +67 -0
  40. data/lib/dry/effects/extensions/system.rb +43 -0
  41. data/lib/dry/effects/halt.rb +29 -0
  42. data/lib/dry/effects/handler.rb +58 -0
  43. data/lib/dry/effects/inflector.rb +9 -0
  44. data/lib/dry/effects/initializer.rb +99 -0
  45. data/lib/dry/effects/instruction.rb +8 -0
  46. data/lib/dry/effects/instructions/execute.rb +25 -0
  47. data/lib/dry/effects/instructions/raise.rb +25 -0
  48. data/lib/dry/effects/provider.rb +29 -0
  49. data/lib/dry/effects/provider/class_interface.rb +61 -0
  50. data/lib/dry/effects/providers/amb.rb +36 -0
  51. data/lib/dry/effects/providers/async.rb +31 -0
  52. data/lib/dry/effects/providers/cache.rb +43 -0
  53. data/lib/dry/effects/providers/current_time.rb +49 -0
  54. data/lib/dry/effects/providers/defer.rb +84 -0
  55. data/lib/dry/effects/providers/env.rb +65 -0
  56. data/lib/dry/effects/providers/fork.rb +23 -0
  57. data/lib/dry/effects/providers/implicit.rb +39 -0
  58. data/lib/dry/effects/providers/interrupt.rb +37 -0
  59. data/lib/dry/effects/providers/lock.rb +125 -0
  60. data/lib/dry/effects/providers/parallel.rb +34 -0
  61. data/lib/dry/effects/providers/random.rb +13 -0
  62. data/lib/dry/effects/providers/reader.rb +61 -0
  63. data/lib/dry/effects/providers/resolve.rb +88 -0
  64. data/lib/dry/effects/providers/retry.rb +59 -0
  65. data/lib/dry/effects/providers/state.rb +30 -0
  66. data/lib/dry/effects/stack.rb +67 -0
  67. data/lib/dry/effects/version.rb +7 -0
  68. metadata +263 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Effects
5
+ module Constructors
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/container'
4
+
5
+ module Dry
6
+ module Effects
7
+ class Container
8
+ include Dry::Container::Mixin
9
+ end
10
+ end
11
+ end
@@ -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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/effects/effects/state'
4
+
5
+ module Dry
6
+ module Effects
7
+ module Effects
8
+ class Reader < State
9
+ def initialize(scope, writer: false, **opts)
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end