amazing-activist 0.3.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5a9d6f3426a86d93d6affd8f54d19f7f071cb5142636c1b3b6fc2a12fbe87b9
4
- data.tar.gz: d53b13cc039dee609e0d2870c560932942d3aa38009b4e7a742f7ce7ad44e9df
3
+ metadata.gz: 290c18d206342a875bfadb61a6d1cda66e7df96a44ecda1379a18e3aefc187fd
4
+ data.tar.gz: d21e98ee9f606a7264cdf743f935f7c845832c884f86feef55076f8a3eae535f
5
5
  SHA512:
6
- metadata.gz: '0549ced0fb0312fd5c4cca4cfc8ccaa0e359584c868497a5eea3caa7ad246958de12566d5f33525d555f6bbd9c3a77eb3536514d0e555b83a8a629048c3d58ff'
7
- data.tar.gz: de8d63d94501dc73c61cc626e899407846af44dc8e9cb1c603e7154d57a57720adaa0897a319782a458ed9d89eff474a510777f5083206b9da902143f89060bd
6
+ metadata.gz: 3056d150ef3732e54396acd8915056cd20f5d11c6efc00eb3d131920deeaac92f42cbe5d92955e2667671fe3a14af93fc6cc495108fb797692b3280cb5449f7d
7
+ data.tar.gz: 68886df50e8d078f52b03804ec09f146d64f6fcc6ad27c1153bc71b078e2b9b49868e5bb496a3caef9708321c18fe088d371c8a9eb4f9b8a1c09d62365d84ced
data/CHANGES.md ADDED
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.5.0] - 2023-03-13
11
+
12
+ ### Changed
13
+
14
+ - (BREAKING) i18n does not remove `_activity` suffix from keys anymore, e.g.
15
+ for `Foo::BarActivity` expected i18n key will be `foo/bar_activity`, not
16
+ `foo/bar` as it was before.
17
+
18
+
19
+ ## [0.4.0] - 2024-02-19
20
+
21
+ ### Added
22
+
23
+ - `Base.on_broken_outcome` allows registering custom handler for the broken
24
+ outcome contract (when `#call` returns neither `Success` nor `Failure`)
25
+ - `Base.rescue_from` allows registering unhandled exception handlers.
26
+
27
+
28
+ ## [0.3.0] - 2024-02-19
29
+
30
+ ### Added
31
+
32
+ - `UnwrapError#cause` respects failure's `#exception`.
33
+
34
+ ### Changed
35
+
36
+ - (BREAKING) `Base#failure` context must be given as keyword argument.
37
+ - (BREAKING) `Failure#message` is no longer part of its `#context`.
38
+ - (BREAKING) `Failure#exception` is no longer part of its `#context`.
39
+
40
+
41
+ ## [0.2.0] - 2024-01-28
42
+
43
+ ### Added
44
+
45
+ - Generate default failure messages with
46
+ [i18n](https://github.com/ruby-i18n/i18n).
47
+
48
+
49
+ ## [0.1.0] - 2024-01-27
50
+
51
+ ### Added
52
+
53
+ - Initial release.
54
+
55
+ [0.5.0]: https://github.com/ixti/amazing-activist/compare/v0.4.0...v0.5.0
56
+ [0.4.0]: https://github.com/ixti/amazing-activist/compare/v0.3.0...v0.4.0
57
+ [0.3.0]: https://github.com/ixti/amazing-activist/compare/v0.2.0...v0.3.0
58
+ [0.2.0]: https://github.com/ixti/amazing-activist/compare/v0.1.0...v0.2.0
59
+ [0.1.0]: https://github.com/ixti/amazing-activist/tree/v0.1.0
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "./irresistible"
3
4
  require_relative "./outcome"
4
5
 
5
6
  module AmazingActivist
@@ -28,14 +29,7 @@ module AmazingActivist
28
29
  # end
29
30
  # ----
30
31
  class Base
31
- class << self
32
- # Convenience method to initialize and immediatelly call the activity.
33
- # @see #initialize
34
- # @see #call
35
- def call(...)
36
- new(...).call
37
- end
38
- end
32
+ extend Irresistible
39
33
 
40
34
  # @param params [Hash{Symbol => Object}]
41
35
  def initialize(**params)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./error"
4
+
5
+ module AmazingActivist
6
+ class BrokenContractError < Error; end
7
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./broken_contract_error"
4
+
5
+ module AmazingActivist
6
+ module Contractable
7
+ DEFAULT_BROKEN_OUTCOME_HANDLER = lambda do |outcome|
8
+ raise BrokenContractError, "#{self.class}#call returned #{outcome.class} instead of Outcome"
9
+ end
10
+ private_constant :DEFAULT_BROKEN_OUTCOME_HANDLER
11
+
12
+ protected
13
+
14
+ # @api internal
15
+ def broken_contract_handler
16
+ return @broken_contract_handler if defined?(@broken_contract_handler)
17
+
18
+ ancestors.each do |klass|
19
+ next unless klass < Base && klass != self
20
+
21
+ return @broken_contract_handler = klass.broken_contract_handler
22
+ end
23
+
24
+ @broken_contract_handler = DEFAULT_BROKEN_OUTCOME_HANDLER
25
+ end
26
+
27
+ private
28
+
29
+ def on_broken_outcome(&block)
30
+ raise ArgumentError, "Handler block required." unless block
31
+
32
+ @broken_contract_handler = block
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./broken_contract_error"
4
+ require_relative "./contractable"
5
+ require_relative "./rescuable"
6
+ require_relative "./unwrap_error"
7
+
8
+ module AmazingActivist
9
+ module Irresistible
10
+ include Contractable
11
+ include Rescuable
12
+
13
+ # Initialize and call activity.
14
+ #
15
+ # @see #initialize
16
+ # @see #call
17
+ def call(...)
18
+ activity = new(...)
19
+ outcome = irresistible_call(activity)
20
+
21
+ unless outcome.is_a?(Outcome::Failure) || outcome.is_a?(Outcome::Success)
22
+ return activity.instance_exec(outcome, &broken_contract_handler)
23
+ end
24
+
25
+ outcome
26
+ end
27
+
28
+ private
29
+
30
+ # @api internal
31
+ def irresistible_call(activity)
32
+ activity.call
33
+ rescue UnwrapError => e
34
+ e.failure
35
+ rescue Exception => e # rubocop:disable Lint/RescueException
36
+ handler = rescue_handler_for(e)
37
+ raise unless handler
38
+
39
+ activity.instance_exec(e, &handler)
40
+ end
41
+ end
42
+ end
@@ -1,4 +1,4 @@
1
1
  en:
2
2
  amazing_activist:
3
3
  failures:
4
- not_implemented: "<%{activity}> activity has no implementation"
4
+ not_implemented: "<%{activity}> has no implementation"
@@ -1,4 +1,4 @@
1
1
  gl:
2
2
  amazing_activist:
3
3
  failures:
4
- not_implemented: "<%{activity}> actividade non ten implementación"
4
+ not_implemented: "<%{activity}> non ten implementación"
@@ -10,9 +10,6 @@ I18n.load_path += Dir[File.expand_path("#{__dir__}/locale/*.yml")]
10
10
  module AmazingActivist
11
11
  # @api internal
12
12
  class Polyglot
13
- ANONYMOUS_ACTIVITY_NAME = "anonymous"
14
- private_constant :ANONYMOUS_ACTIVITY_NAME
15
-
16
13
  def initialize(activity)
17
14
  @activity = activity
18
15
  end
@@ -25,26 +22,26 @@ module AmazingActivist
25
22
  # Thus, if activity `Pretty::DamnGoodActivity` failed with `:bad_choise`
26
23
  # code the lookup will be:
27
24
  #
28
- # * `amazing_activist.activities.pretty/damn_good.failures.bad_choice
25
+ # * `amazing_activist.activities.pretty/damn_good_activity.failures.bad_choice
29
26
  # * `amazing_activist.failures.bad_choice
30
27
  #
31
28
  # If there's no translation with any of the above keys, a generic
32
29
  # non-translated message will be used:
33
30
  #
34
- # <pretty/damn_good> activity failed - bad_choice
31
+ # <pretty/damn_good_activity> failed - bad_choice
35
32
  #
36
33
  # @return [String] Failure message
37
34
  def message(code, **context)
38
35
  default = [
39
36
  :"amazing_activist.failures.#{code}",
40
- "<%{activity}> activity failed - %{code}" # rubocop:disable Style/FormatStringToken
37
+ "<%{activity}> failed - %{code}" # rubocop:disable Style/FormatStringToken
41
38
  ]
42
39
 
43
40
  if @activity.class.name
44
- activity = @activity.class.name.underscore.presence.delete_suffix("_activity")
41
+ activity = @activity.class.name.underscore.presence
45
42
  i18n_key = :"amazing_activist.activities.#{activity}.failures.#{code}"
46
43
  else
47
- activity = "(anonymous)"
44
+ activity = "(anonymous activity)"
48
45
  i18n_key = default.shift
49
46
  end
50
47
 
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AmazingActivist
4
+ module Rescuable
5
+ protected
6
+
7
+ # @api internal
8
+ def rescue_handlers
9
+ return @rescue_handlers if defined?(@rescue_handlers)
10
+
11
+ ancestors.each do |klass|
12
+ next unless klass < Base && klass != self
13
+
14
+ return @rescue_handlers = klass.rescue_handlers
15
+ end
16
+
17
+ @rescue_handlers = [].freeze
18
+ end
19
+
20
+ # @api internal
21
+ def rescue_handlers=(new_handlers)
22
+ @rescue_handlers = new_handlers.freeze
23
+ end
24
+
25
+ private
26
+
27
+ def rescue_from(*klasses, &block)
28
+ raise ArgumentError, "Handler block required." unless block
29
+
30
+ klasses.each do |klass|
31
+ klass_name =
32
+ case klass
33
+ when Module then klass.name
34
+ when String then klass
35
+ else raise ArgumentError, "#{klass.inspect} must be an Exception class or a String referencing a class."
36
+ end
37
+
38
+ self.rescue_handlers += [[klass_name, block].freeze]
39
+ end
40
+ end
41
+
42
+ # @api internal
43
+ def rescue_handler_for(exception)
44
+ while exception
45
+ rescue_handlers.reverse_each do |klass, handler|
46
+ return handler if exception.is_a?(const_get(klass))
47
+ rescue StandardError
48
+ # do nothing
49
+ end
50
+
51
+ exception = exception.cause
52
+ end
53
+
54
+ nil
55
+ end
56
+ end
57
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AmazingActivist
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amazing-activist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Zapparov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-19 00:00:00.000000000 Z
11
+ date: 2024-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -45,18 +45,23 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - CHANGES.md
48
49
  - LICENSE.txt
49
50
  - README.adoc
50
51
  - lib/amazing-activist.rb
51
52
  - lib/amazing_activist.rb
52
53
  - lib/amazing_activist/base.rb
54
+ - lib/amazing_activist/broken_contract_error.rb
55
+ - lib/amazing_activist/contractable.rb
53
56
  - lib/amazing_activist/error.rb
57
+ - lib/amazing_activist/irresistible.rb
54
58
  - lib/amazing_activist/locale/en.yml
55
59
  - lib/amazing_activist/locale/gl.yml
56
60
  - lib/amazing_activist/outcome.rb
57
61
  - lib/amazing_activist/outcome/failure.rb
58
62
  - lib/amazing_activist/outcome/success.rb
59
63
  - lib/amazing_activist/polyglot.rb
64
+ - lib/amazing_activist/rescuable.rb
60
65
  - lib/amazing_activist/unwrap_error.rb
61
66
  - lib/amazing_activist/version.rb
62
67
  homepage: https://github.com/ixti/amazing-activist
@@ -64,9 +69,9 @@ licenses:
64
69
  - MIT
65
70
  metadata:
66
71
  homepage_uri: https://github.com/ixti/amazing-activist
67
- source_code_uri: https://github.com/ixti/amazing-activist/tree/v0.3.0
72
+ source_code_uri: https://github.com/ixti/amazing-activist/tree/v0.5.0
68
73
  bug_tracker_uri: https://github.com/ixti/amazing-activist/issues
69
- changelog_uri: https://github.com/ixti/amazing-activist/blob/v0.3.0/CHANGES.md
74
+ changelog_uri: https://github.com/ixti/amazing-activist/blob/v0.5.0/CHANGES.md
70
75
  rubygems_mfa_required: 'true'
71
76
  post_install_message:
72
77
  rdoc_options: []