dry-effects 0.1.0.alpha

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