dry-effects 0.1.2 → 0.2.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -5
  3. data/LICENSE +1 -1
  4. data/README.md +19 -11
  5. data/dry-effects.gemspec +33 -41
  6. data/lib/dry/effects/all.rb +4 -4
  7. data/lib/dry/effects/container.rb +1 -1
  8. data/lib/dry/effects/effect.rb +2 -2
  9. data/lib/dry/effects/effects/async.rb +3 -1
  10. data/lib/dry/effects/effects/cache.rb +13 -9
  11. data/lib/dry/effects/effects/cmp.rb +3 -1
  12. data/lib/dry/effects/effects/current_time.rb +4 -2
  13. data/lib/dry/effects/effects/defer.rb +3 -1
  14. data/lib/dry/effects/effects/env.rb +4 -2
  15. data/lib/dry/effects/effects/fork.rb +3 -1
  16. data/lib/dry/effects/effects/implicit.rb +4 -2
  17. data/lib/dry/effects/effects/interrupt.rb +4 -2
  18. data/lib/dry/effects/effects/lock.rb +8 -6
  19. data/lib/dry/effects/effects/parallel.rb +4 -2
  20. data/lib/dry/effects/effects/random.rb +5 -3
  21. data/lib/dry/effects/effects/reader.rb +1 -1
  22. data/lib/dry/effects/effects/resolve.rb +23 -3
  23. data/lib/dry/effects/effects/retry.rb +4 -2
  24. data/lib/dry/effects/effects/state.rb +4 -2
  25. data/lib/dry/effects/effects/timeout.rb +3 -1
  26. data/lib/dry/effects/effects/timestamp.rb +3 -1
  27. data/lib/dry/effects/errors.rb +4 -4
  28. data/lib/dry/effects/extensions/active_support/tagged_logging.rb +13 -0
  29. data/lib/dry/effects/extensions/auto_inject.rb +5 -5
  30. data/lib/dry/effects/extensions/system.rb +8 -7
  31. data/lib/dry/effects/extensions.rb +12 -4
  32. data/lib/dry/effects/frame.rb +30 -9
  33. data/lib/dry/effects/halt.rb +3 -3
  34. data/lib/dry/effects/handler.rb +1 -1
  35. data/lib/dry/effects/inflector.rb +1 -1
  36. data/lib/dry/effects/initializer.rb +17 -16
  37. data/lib/dry/effects/instruction.rb +1 -1
  38. data/lib/dry/effects/instructions/execute.rb +2 -1
  39. data/lib/dry/effects/instructions/raise.rb +2 -1
  40. data/lib/dry/effects/provider/class_interface.rb +2 -2
  41. data/lib/dry/effects/provider.rb +2 -2
  42. data/lib/dry/effects/providers/async.rb +2 -2
  43. data/lib/dry/effects/providers/cache.rb +2 -2
  44. data/lib/dry/effects/providers/cmp.rb +1 -1
  45. data/lib/dry/effects/providers/current_time/time_generators.rb +1 -1
  46. data/lib/dry/effects/providers/current_time.rb +5 -5
  47. data/lib/dry/effects/providers/defer.rb +6 -6
  48. data/lib/dry/effects/providers/env.rb +2 -2
  49. data/lib/dry/effects/providers/fork.rb +2 -2
  50. data/lib/dry/effects/providers/implicit.rb +1 -1
  51. data/lib/dry/effects/providers/interrupt.rb +3 -3
  52. data/lib/dry/effects/providers/lock.rb +6 -8
  53. data/lib/dry/effects/providers/parallel.rb +3 -3
  54. data/lib/dry/effects/providers/random.rb +74 -2
  55. data/lib/dry/effects/providers/reader.rb +1 -1
  56. data/lib/dry/effects/providers/resolve.rb +8 -7
  57. data/lib/dry/effects/providers/retry.rb +5 -7
  58. data/lib/dry/effects/providers/state.rb +2 -2
  59. data/lib/dry/effects/providers/timeout.rb +2 -2
  60. data/lib/dry/effects/providers/timestamp.rb +2 -2
  61. data/lib/dry/effects/stack.rb +6 -6
  62. data/lib/dry/effects/version.rb +1 -1
  63. data/lib/dry/effects.rb +7 -7
  64. metadata +22 -69
  65. data/.codeclimate.yml +0 -12
  66. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  67. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -30
  68. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  69. data/.github/workflows/ci.yml +0 -74
  70. data/.github/workflows/docsite.yml +0 -34
  71. data/.github/workflows/sync_configs.yml +0 -34
  72. data/.gitignore +0 -12
  73. data/.rspec +0 -4
  74. data/.rubocop.yml +0 -95
  75. data/CODE_OF_CONDUCT.md +0 -13
  76. data/CONTRIBUTING.md +0 -29
  77. data/Gemfile +0 -23
  78. data/Rakefile +0 -8
  79. data/docsite/source/effects/cache.html.md +0 -84
  80. data/docsite/source/effects/current_time.html.md +0 -186
  81. data/docsite/source/effects/defer.html.md +0 -130
  82. data/docsite/source/effects/env.html.md +0 -144
  83. data/docsite/source/effects/interrupt.html.md +0 -109
  84. data/docsite/source/effects/parallel.html.md +0 -25
  85. data/docsite/source/effects/reader.html.md +0 -126
  86. data/docsite/source/effects/resolve.html.md +0 -188
  87. data/docsite/source/effects/state.html.md +0 -178
  88. data/docsite/source/effects/timeout.html.md +0 -42
  89. data/docsite/source/effects.html.md +0 -29
  90. data/docsite/source/index.html.md +0 -212
  91. data/examples/cmp.rb +0 -51
  92. data/examples/state.rb +0 -29
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
3
+ require "dry/effects/effect"
4
4
 
5
5
  module Dry
6
6
  module Effects
@@ -11,6 +11,8 @@ module Dry
11
11
  end
12
12
 
13
13
  def initialize(scope = :default)
14
+ super()
15
+
14
16
  interrupt = InterruptEffect.new(type: :interrupt, scope: scope)
15
17
 
16
18
  module_eval do
@@ -18,7 +20,7 @@ module Dry
18
20
  if Undefined.equal?(payload)
19
21
  ::Dry::Effects.yield(interrupt)
20
22
  else
21
- ::Dry::Effects.yield(interrupt.(payload))
23
+ ::Dry::Effects.yield(interrupt.payload(payload))
22
24
  end
23
25
  end
24
26
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
3
+ require "dry/effects/effect"
4
4
 
5
5
  module Dry
6
6
  module Effects
@@ -12,17 +12,19 @@ module Dry
12
12
  Locked = Effect.new(type: :lock, name: :locked?)
13
13
 
14
14
  def initialize
15
+ super
16
+
15
17
  module_eval do
16
18
  define_method(:lock) do |key, meta: Undefined, &block|
17
19
  if block
18
20
  begin
19
- handle = ::Dry::Effects.yield(Lock.(key, meta))
21
+ handle = ::Dry::Effects.yield(Lock.payload(key, meta))
20
22
  block.(!handle.nil?)
21
23
  ensure
22
- ::Dry::Effects.yield(Unlock.(handle)) if handle
24
+ ::Dry::Effects.yield(Unlock.payload(handle)) if handle
23
25
  end
24
26
  else
25
- ::Dry::Effects.yield(Lock.(key, meta))
27
+ ::Dry::Effects.yield(Lock.payload(key, meta))
26
28
  end
27
29
  end
28
30
 
@@ -31,11 +33,11 @@ module Dry
31
33
  end
32
34
 
33
35
  define_method(:locked?) do |key|
34
- ::Dry::Effects.yield(Locked.(key))
36
+ ::Dry::Effects.yield(Locked.payload(key))
35
37
  end
36
38
 
37
39
  define_method(:lock_meta) do |key|
38
- ::Dry::Effects.yield(Meta.(key))
40
+ ::Dry::Effects.yield(Meta.payload(key))
39
41
  end
40
42
  end
41
43
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
3
+ require "dry/effects/effect"
4
4
 
5
5
  module Dry
6
6
  module Effects
@@ -10,8 +10,10 @@ module Dry
10
10
  Join = Effect.new(type: :parallel, name: :join)
11
11
 
12
12
  def initialize
13
+ super
14
+
13
15
  define_method(:par) { |&block| ::Dry::Effects.yield(Par).(&block) }
14
- define_method(:join) { |xs| ::Dry::Effects.yield(Join.(xs)) }
16
+ define_method(:join) { |xs| ::Dry::Effects.yield(Join.payload(xs)) }
15
17
  end
16
18
  end
17
19
  end
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
3
+ require "dry/effects/effect"
4
4
 
5
5
  module Dry
6
6
  module Effects
7
7
  module Effects
8
8
  class Random < ::Module
9
- Read = Effect.new(type: :random, name: :rand)
9
+ Rand = Effect.new(type: :random, name: :rand)
10
10
 
11
11
  def initialize
12
+ super
13
+
12
14
  module_eval do
13
- define_method(:rand) { |n| ::Dry::Effects.yield(Read.(n)) }
15
+ define_method(:rand) { |n = nil| ::Dry::Effects.yield(Rand.payload(n)) }
14
16
  end
15
17
  end
16
18
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effects/state'
3
+ require "dry/effects/effects/state"
4
4
 
5
5
  module Dry
6
6
  module Effects
@@ -1,23 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
4
- require 'dry/effects/constructors'
3
+ require "dry/effects/effect"
4
+ require "dry/effects/constructors"
5
5
 
6
6
  module Dry
7
7
  module Effects
8
8
  module Effects
9
9
  class Resolve < ::Module
10
+ DependencyNameInvalid = Class.new(StandardError)
11
+
12
+ VALID_NAME = /([a-z_][a-zA-Z_0-9]*)$/.freeze
13
+
10
14
  Resolve = Effect.new(type: :resolve)
11
15
 
12
16
  Constructors.register(:Resolve) { |key| Resolve.(key) }
13
17
 
14
18
  def initialize(*keys, **aliases)
19
+ super()
20
+
21
+ keys_aliased = keys.map { |k| name_for(k) }.zip(keys)
15
22
  module_eval do
16
- (keys.zip(keys) + aliases.to_a).each do |name, key|
23
+ (keys_aliased + aliases.to_a).each do |name, key|
17
24
  define_method(name) { |&block| ::Dry::Effects.yield(Resolve.(key), &block) }
18
25
  end
19
26
  end
20
27
  end
28
+
29
+ private
30
+
31
+ # similar approach in dry-auto_inject https://github.com/dry-rb/dry-auto_inject/blob/master/lib/dry/auto_inject/dependency_map.rb#L42
32
+ def name_for(identifier)
33
+ matched = VALID_NAME.match(identifier.to_s)
34
+ unless matched
35
+ raise DependencyNameInvalid,
36
+ "name +#{identifier}+ is not a valid Ruby identifier"
37
+ end
38
+
39
+ matched[0]
40
+ end
21
41
  end
22
42
  end
23
43
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
4
- require 'dry/effects/constructors'
3
+ require "dry/effects/effect"
4
+ require "dry/effects/constructors"
5
5
 
6
6
  module Dry
7
7
  module Effects
@@ -18,6 +18,8 @@ module Dry
18
18
  end
19
19
 
20
20
  def initialize
21
+ super
22
+
21
23
  module_eval do
22
24
  define_method(:repeat) do |scope|
23
25
  effect = Retry.new(type: :retry, scope: scope)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
4
- require 'dry/effects/constructors'
3
+ require "dry/effects/effect"
4
+ require "dry/effects/constructors"
5
5
 
6
6
  module Dry
7
7
  module Effects
@@ -22,6 +22,8 @@ module Dry
22
22
  end
23
23
 
24
24
  def initialize(scope, default: Undefined, writer: true, as: scope)
25
+ super()
26
+
25
27
  read = State.new(type: :state, name: :read, scope: scope)
26
28
  write = State.new(type: :state, name: :write, scope: scope)
27
29
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
3
+ require "dry/effects/effect"
4
4
 
5
5
  module Dry
6
6
  module Effects
@@ -13,6 +13,8 @@ module Dry
13
13
  end
14
14
 
15
15
  def initialize(scope)
16
+ super()
17
+
16
18
  timeout = TimeoutEffect.new(type: :timeout, name: :timeout, scope: scope)
17
19
 
18
20
  module_eval do
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/effects/effect'
3
+ require "dry/effects/effect"
4
4
 
5
5
  module Dry
6
6
  module Effects
@@ -9,6 +9,8 @@ module Dry
9
9
  Timestamp = Effect.new(type: :timestamp)
10
10
 
11
11
  def initialize(options = EMPTY_HASH)
12
+ super()
13
+
12
14
  module_eval do
13
15
  define_method(:timestamp) do |round: Undefined|
14
16
  round_to = Undefined.coalesce(round, options.fetch(:round, Undefined))
@@ -20,7 +20,7 @@ module Dry
20
20
  super(
21
21
  Undefined.default(message) {
22
22
  "Effect #{effect.inspect} not handled. "\
23
- 'Effects must be wrapped with corresponding handlers'
23
+ "Effects must be wrapped with corresponding handlers"
24
24
  }
25
25
  )
26
26
  end
@@ -32,7 +32,7 @@ module Dry
32
32
  class MissingStateError < UnhandledEffectError
33
33
  def initialize(effect)
34
34
  message = "Value of +#{effect.scope}+ is not set, "\
35
- 'you need to provide value with an effect handler'
35
+ "you need to provide value with an effect handler"
36
36
 
37
37
  super(effect, message)
38
38
  end
@@ -46,8 +46,8 @@ module Dry
46
46
 
47
47
  def initialize(effect)
48
48
  message = "+#{effect.scope}+ is not defined, you need to assign it first "\
49
- 'by using a writer, passing initial value to the handler, or '\
50
- 'providing a fallback value'
49
+ "by using a writer, passing initial value to the handler, or "\
50
+ "providing a fallback value"
51
51
 
52
52
  super(message)
53
53
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/tagged_logging"
4
+
5
+ ActiveSupport::TaggedLogging::Formatter.prepend(Module.new {
6
+ def current_tags
7
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
8
+ unless Thread.current.thread_variable_get(thread_key)
9
+ Thread.current.thread_variable_set(thread_key, [])
10
+ end
11
+ Thread.current.thread_variable_get(thread_key)
12
+ end
13
+ })
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
4
- require 'dry/auto_inject/strategies/constructor'
5
- require 'dry/effects/effects/resolve'
3
+ require "concurrent/map"
4
+ require "dry/auto_inject/strategies/constructor"
5
+ require "dry/effects/effects/resolve"
6
6
 
7
7
  module Dry
8
8
  module Effects
@@ -24,7 +24,7 @@ module Dry
24
24
  class Static < Base
25
25
  private
26
26
 
27
- def define_readers(dynamic = false)
27
+ def define_readers(dynamic = false) # rubocop:disable Style/OptionalBooleanParameter
28
28
  map = dependency_map.to_h
29
29
  cache = ::Concurrent::Map.new
30
30
  instance_mod.class_exec do
@@ -49,7 +49,7 @@ module Dry
49
49
  class Dynamic < Static
50
50
  private
51
51
 
52
- def define_readers(dynamic = true)
52
+ def define_readers(dynamic = true) # rubocop:disable Style/OptionalBooleanParameter
53
53
  super
54
54
  end
55
55
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/system/container'
3
+ require "dry/system/container"
4
4
 
5
5
  Dry::Effects.load_extensions(:auto_inject)
6
6
 
@@ -8,17 +8,18 @@ module Dry
8
8
  module Effects
9
9
  module System
10
10
  class AutoRegistrar < ::Dry::System::AutoRegistrar
11
- def call(dir)
12
- super do |config|
13
- config.memoize = true
14
- config.instance { |c| c.instance.freeze }
15
- yield(config) if block_given?
11
+ # Always memoize and freeze registered components
12
+ def call(component_dir)
13
+ components(component_dir).each do |component|
14
+ next unless register_component?(component)
15
+
16
+ container.register(component.key, memoize: true) { component.instance.freeze }
16
17
  end
17
18
  end
18
19
  end
19
20
 
20
21
  class Container < ::Dry::System::Container
21
- setting :auto_registrar, AutoRegistrar
22
+ config.auto_registrar = AutoRegistrar
22
23
 
23
24
  def self.injector(effects: true, **kwargs)
24
25
  if effects
@@ -1,17 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/core/extensions'
3
+ require "dry/core/extensions"
4
4
 
5
5
  Dry::Effects.extend(Dry::Core::Extensions)
6
6
 
7
7
  Dry::Effects.register_extension(:auto_inject) do
8
- require 'dry/effects/extensions/auto_inject'
8
+ require "dry/effects/extensions/auto_inject"
9
9
  end
10
10
 
11
11
  Dry::Effects.register_extension(:system) do
12
- require 'dry/effects/extensions/system'
12
+ require "dry/effects/extensions/system"
13
13
  end
14
14
 
15
15
  Dry::Effects.register_extension(:rspec) do
16
- require 'dry/effects/extensions/rspec'
16
+ require "dry/effects/extensions/rspec"
17
+ end
18
+
19
+ Dry::Effects.register_extension(:active_support_tagged_logging) do
20
+ require "dry/effects/extensions/active_support/tagged_logging"
21
+ end
22
+
23
+ Dry::Effects.register_extension(:active_support) do
24
+ Dry::Effects.load_extensions(:active_support_tagged_logging)
17
25
  end
@@ -1,11 +1,11 @@
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'
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
9
 
10
10
  module Dry
11
11
  module Effects
@@ -41,15 +41,36 @@ module Dry
41
41
  result = fiber.resume
42
42
 
43
43
  loop do
44
+ error = false
44
45
  break result unless fiber.alive?
45
46
 
46
47
  provided = stack.(result) do
47
- ::Dry::Effects.yield(result) do |_, error|
48
- Instructions.Raise(error)
48
+ ::Dry::Effects.yield(result) do |_, e|
49
+ error = true
50
+ e
49
51
  end
50
52
  end
51
53
 
52
- result = fiber.resume(provided)
54
+ result =
55
+ if error
56
+ raise_in_fiber(fiber, provided)
57
+ else
58
+ fiber.resume(provided)
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ if RUBY_VERSION >= "2.7"
66
+ # @api private
67
+ def raise_in_fiber(fiber, error)
68
+ fiber.raise(error)
69
+ end
70
+ else
71
+ # @api private
72
+ def raise_in_fiber(fiber, error)
73
+ fiber.resume(Instructions.Raise(error))
53
74
  end
54
75
  end
55
76
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
4
- require 'dry/core/class_attributes'
5
- require 'dry/effects/inflector'
3
+ require "concurrent/map"
4
+ require "dry/core/class_attributes"
5
+ require "dry/effects/inflector"
6
6
 
7
7
  module Dry
8
8
  module Effects