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 +4 -4
- data/CHANGES.md +59 -0
- data/lib/amazing_activist/base.rb +2 -8
- data/lib/amazing_activist/broken_contract_error.rb +7 -0
- data/lib/amazing_activist/contractable.rb +35 -0
- data/lib/amazing_activist/irresistible.rb +42 -0
- data/lib/amazing_activist/locale/en.yml +1 -1
- data/lib/amazing_activist/locale/gl.yml +1 -1
- data/lib/amazing_activist/polyglot.rb +5 -8
- data/lib/amazing_activist/rescuable.rb +57 -0
- data/lib/amazing_activist/version.rb +1 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 290c18d206342a875bfadb61a6d1cda66e7df96a44ecda1379a18e3aefc187fd
|
4
|
+
data.tar.gz: d21e98ee9f606a7264cdf743f935f7c845832c884f86feef55076f8a3eae535f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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,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
|
@@ -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/
|
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/
|
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}>
|
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
|
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
|
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.
|
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-
|
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.
|
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.
|
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: []
|