amazing-activist 0.8.0 → 0.9.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: c059cc4ff198a6fb4d721683061c51063050fa209f4bdb3d0e27b0232abd2e7f
4
- data.tar.gz: 1cfd1f85b06587f6c3ab903e36ae15fdd25c5f7c9f366690e041a89a71fcb952
3
+ metadata.gz: 326a7279423476cd39fd4f6179e8654f588202eb49b4845c996416cf326d3b76
4
+ data.tar.gz: 5da3845b7810f1798522aa1797d522cc383443295d736fc7def2599c71bf4aef
5
5
  SHA512:
6
- metadata.gz: 1f21b35f615f84a428d6b53c859b4bcf0c1a60bbb5f41c3bea265135c1acd32e7cf99d734b05bdae3913199095517d632ee042da175581c9ac7fc335b30a97ae
7
- data.tar.gz: f8123ee0debd3ae42218f1651b8b9df163aef428f778b26f1e12296861b354c7710eb8f5e97ab042746a6e881cb467e36b2dc676b230731e9fdf4621affe73a2
6
+ metadata.gz: e23671879a7fe1e85a818c6f665ea03db223190f07b64c74c14cfe854d284748db530c740363f2b65fc9ecf6eb3ece96f1d94d76713104dc0ae0a2c0440e7c0d
7
+ data.tar.gz: 6da7bc114e8be08bf65706b5e0067680283ff1c6754e9aaec714fafd0b7ce269a651b69ba910225bf42eebb5652090e44c6be287df4712d98a057889211d08df
data/CHANGES.md CHANGED
@@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [0.9.0] - 2025-05-28
12
+
13
+ ### Changed
14
+
15
+ - (BREAKING) Switch to Zeitwerk autoloader.
16
+ - (BREAKING) Deprecate `AmazingActivist::Outcome::{Success,Failure}`
17
+ in favour of `AmazingActivist::Success` and `AmazingActivist::Failure`
18
+ respectively.
19
+ - (BREAKING) Harden outcomes initializer API
20
+
21
+
11
22
  ## [0.8.0] - 2025-05-01
12
23
 
13
24
  ### Changed
@@ -109,7 +120,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
109
120
 
110
121
  - Initial release.
111
122
 
112
- [Unreleased]: https://github.com/ixti/amazing-activist/compare/v0.8.0...main
123
+ [Unreleased]: https://github.com/ixti/amazing-activist/compare/v0.9.0...main
124
+ [0.9.0]: https://github.com/ixti/amazing-activist/compare/v0.8.0...v0.9.0
113
125
  [0.8.0]: https://github.com/ixti/amazing-activist/compare/v0.7.0...v0.8.0
114
126
  [0.7.0]: https://github.com/ixti/amazing-activist/compare/v0.6.0...v0.7.0
115
127
  [0.6.0]: https://github.com/ixti/amazing-activist/compare/v0.5.2...v0.6.0
data/README.adoc CHANGED
@@ -28,8 +28,11 @@ class ApplicationActivity < AmazingActivist::Base
28
28
  end
29
29
 
30
30
  class OnboardingActivity < ApplicationActivity
31
+ prop :email, String
32
+ prop :password, String
33
+
31
34
  def call
32
- user = User.new(params)
35
+ user = User.new(email:, password:)
33
36
 
34
37
  return failure(:invalid_params, user: user) unless user.save
35
38
 
@@ -39,10 +42,10 @@ end
39
42
 
40
43
  class UsersController < ApplicationController
41
44
  def create
42
- case OnboardingActivity.call(**params.require(:user).permit(:password))
45
+ case OnboardingActivity.call(**params.require(:user).permit(:email, :password))
43
46
  in success: user
44
47
  redirect_to user_bashboard_url(user)
45
- in failure: :invalid_params, context: { user: user }
48
+ in failure: :invalid_params, context: { user: }
46
49
  @user = user
47
50
  render :new
48
51
  else
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./amazing_activist"
3
+ require "amazing_activist"
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./irresistible"
4
- require_relative "./outcome"
5
-
6
3
  module AmazingActivist
7
4
  # Abstract activity class.
8
5
  #
@@ -38,24 +35,24 @@ module AmazingActivist
38
35
  private :new
39
36
  end
40
37
 
41
- # @return [Outcome::Success, Outcome::Failure]
38
+ # @return [Success, Failure]
42
39
  def call
43
40
  failure(:not_implemented)
44
41
  end
45
42
 
46
43
  private
47
44
 
48
- # @param value (see Outcome::Success#initialize)
49
- # @return [Outcome::Success]
45
+ # @param value (see Success#initialize)
46
+ # @return [Success]
50
47
  def success(value = nil)
51
- Outcome::Success.new(value, activity: self)
48
+ Success.new(value, activity: self)
52
49
  end
53
50
 
54
- # @param code (see Outcome::Failure#initialize)
55
- # @param context (see Outcome::Failure#initialize)
56
- # @return [Outcome::Failure]
51
+ # @param code (see Failure#initialize)
52
+ # @param context (see Failure#initialize)
53
+ # @return [Failure]
57
54
  def failure(code, message: nil, exception: nil, context: {})
58
- Outcome::Failure.new(code, activity: self, message: message, exception: exception, context: context)
55
+ Failure.new(code, activity: self, message: message, exception: exception, context: context)
59
56
  end
60
57
  end
61
58
  end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "literal"
4
-
5
- require_relative "./broken_contract_error"
6
-
7
3
  module AmazingActivist
8
4
  module Contractable
9
5
  include Literal::Properties
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./error"
4
-
5
3
  module AmazingActivist
6
4
  class BrokenContractError < Error; end
7
5
  end
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./error"
4
-
5
3
  module AmazingActivist
6
4
  class UnwrapError < Error
7
- # @return [Outcome::Failure]
5
+ # @return [Failure]
8
6
  attr_reader :failure
9
7
 
10
- # @param failure [Outcome::Failure]
8
+ # @param failure [Failure]
11
9
  def initialize(failure)
12
10
  @failure = failure
13
11
 
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AmazingActivist
4
+ class Failure < Literal::Data
5
+ include Outcome
6
+
7
+ # @!attribute [r] failure
8
+ # @return failure [Symbol]
9
+ prop :failure, _Interface(:to_sym), :positional, &:to_sym
10
+
11
+ # @!attribute [r] activity
12
+ # @return [AmazingActivist::Base]
13
+ prop :activity, AmazingActivist::Base
14
+
15
+ # @!attribute [r] exception
16
+ # @return [Exception, nil]
17
+ prop :exception, _Nilable(Exception)
18
+
19
+ # @!attribute [r] context
20
+ # @return [Hash]
21
+ prop :context, _Hash(_Void, _Void), &:to_h
22
+
23
+ prop :message, _String?, reader: false
24
+
25
+ def code = failure
26
+ def success? = false
27
+ def failure? = true
28
+
29
+ # @api internal
30
+ # @return [Array]
31
+ def deconstruct
32
+ [:failure, failure, activity]
33
+ end
34
+
35
+ # @api internal
36
+ # @return [Hash]
37
+ def deconstruct_keys(keys)
38
+ if keys.nil? || keys.include?(:message)
39
+ { failure:, activity:, exception:, context:, message: }
40
+ else
41
+ { failure:, activity:, exception:, context: }
42
+ end
43
+ end
44
+
45
+ # Failure message.
46
+ #
47
+ # @return [String]
48
+ def message
49
+ @message || Polyglot.new(activity).message(failure, **context)
50
+ end
51
+ end
52
+ end
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./broken_contract_error"
4
- require_relative "./contractable"
5
- require_relative "./rescuable"
6
- require_relative "./unwrap_error"
7
-
8
3
  module AmazingActivist
9
4
  module Irresistible
10
5
  include Contractable
@@ -17,9 +12,7 @@ module AmazingActivist
17
12
  activity = new(...)
18
13
  outcome = irresistible_call(activity)
19
14
 
20
- unless outcome.is_a?(Outcome::Failure) || outcome.is_a?(Outcome::Success)
21
- return activity.instance_exec(outcome, &broken_contract_handler)
22
- end
15
+ return activity.instance_exec(outcome, &broken_contract_handler) unless outcome.is_a?(Outcome)
23
16
 
24
17
  outcome
25
18
  end
@@ -1,98 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../polyglot"
4
- require_relative "../unwrap_error"
5
- require_relative "../undefined"
6
-
7
3
  module AmazingActivist
8
4
  module Outcome
9
- class Failure
10
- # @return [Symbol]
11
- attr_reader :code
12
-
13
- # @return [AmazingActivist::Base]
14
- attr_reader :activity
15
-
16
- # @return [Exception, nil]
17
- attr_reader :exception
18
-
19
- # @return [Hash]
20
- attr_reader :context
21
-
22
- # @param code [#to_sym]
23
- # @param activity [AmazingActivist::Activity]
24
- # @param message [#to_s, nil]
25
- # @param exception [Exception, nil]
26
- # @param context [#to_h]
27
- def initialize(code, activity:, message:, exception:, context:)
28
- @code = code.to_sym
29
- @activity = activity
30
- @message = message&.to_s
31
- @exception = exception
32
- @context = context.to_h
33
- end
34
-
35
- def inspect
36
- "#<#{self.class} (#{@activity.class}) #{@code.inspect}>"
37
- end
38
-
39
- alias to_s inspect
40
-
41
- # @return [true]
42
- def success?
43
- false
44
- end
45
-
46
- # @return [false]
47
- def failure?
48
- true
49
- end
50
-
51
- # @api internal
52
- # @return [Array]
53
- def deconstruct
54
- [:failure, @code, @activity]
55
- end
56
-
57
- # @api internal
58
- # @return [Hash]
59
- def deconstruct_keys(keys)
60
- deconstructed = { failure: @code, activity: @activity, exception: exception, context: context }
61
- deconstructed[:message] = message if keys.nil? || keys.include?(:message)
62
-
63
- deconstructed
64
- end
65
-
66
- # @overload value_or(default)
67
- # @param default [Object]
68
- # @return [Object] default value
69
- # @overload value_or(&block)
70
- # @yieldparam self [self]
71
- # @yieldreturn [Object] result of the block
72
- # @raise [ArgumentError] if neither default value, nor block given
73
- def value_or(default = UNDEFINED)
74
- raise ArgumentError, "either default value, or block must be given" if default == UNDEFINED && !block_given?
75
-
76
- unless default == UNDEFINED
77
- return default unless block_given?
78
-
79
- warn "block supersedes default value argument"
80
- end
81
-
82
- yield self
83
- end
84
-
85
- # @raise [UnwrapError]
86
- def unwrap!
87
- raise UnwrapError, self
88
- end
89
-
90
- # Failure message.
91
- #
92
- # @return [String]
93
- def message
94
- @message || Polyglot.new(@activity).message(@code, **context)
95
- end
96
- end
5
+ # @deprecated Use AmazingActivist::Failure
6
+ Failure = AmazingActivist::Failure
7
+ deprecate_constant :Failure
97
8
  end
98
9
  end
@@ -1,70 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../undefined"
4
-
5
3
  module AmazingActivist
6
4
  module Outcome
7
- class Success
8
- # @return [AmazingActivist::Base]
9
- attr_reader :activity
10
-
11
- # @param value [Object]
12
- # @param activity [AmazingActivist::Base]
13
- def initialize(value, activity:)
14
- @value = value
15
- @activity = activity
16
- end
17
-
18
- def inspect
19
- "#<#{self.class} (#{@activity.class}) #{@value.inspect}>"
20
- end
21
-
22
- alias to_s inspect
23
-
24
- # @return [true]
25
- def success?
26
- true
27
- end
28
-
29
- # @return [false]
30
- def failure?
31
- false
32
- end
33
-
34
- # @api internal
35
- # @return [Array]
36
- def deconstruct
37
- [:success, @value, @activity]
38
- end
39
-
40
- # @api internal
41
- # @return [Hash]
42
- def deconstruct_keys(_)
43
- { success: @value, activity: @activity }
44
- end
45
-
46
- # @note Method requires default value or block (even though neither will
47
- # be used) to keep it consistent with {Failure#value_or}, and avoid
48
- # unpleasant surprises when code is not testing all possible outcomes.
49
- #
50
- # @raise [ArgumentError] if neither default value, nor block given
51
- # @return [Object] unwrapped value
52
- def value_or(default = UNDEFINED)
53
- raise ArgumentError, "either default value, or block must be given" if default == UNDEFINED && !block_given?
54
-
55
- unless default == UNDEFINED
56
- return @value unless block_given?
57
-
58
- warn "block supersedes default value argument"
59
- end
60
-
61
- @value
62
- end
63
-
64
- # @return [Object]
65
- def unwrap!
66
- @value
67
- end
68
- end
5
+ # @deprecated Use AmazingActivist::Success
6
+ Success = AmazingActivist::Success
7
+ deprecate_constant :Success
69
8
  end
70
9
  end
@@ -1,4 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./outcome/success"
4
- require_relative "./outcome/failure"
3
+ module AmazingActivist
4
+ module Outcome
5
+ UNDEFINED = Object.new.freeze
6
+ private_constant :UNDEFINED
7
+
8
+ # @return [String]
9
+ def inspect = "#<#{self.class} (#{activity.class})>"
10
+
11
+ alias to_s inspect
12
+
13
+ # @overload value_or(default)
14
+ # @param default [Object]
15
+ # @return [Object] `default` if outcome is {Failure}
16
+ # @return [Object] {#success} otherwise
17
+ #
18
+ # @overload value_or(&block)
19
+ # Yelds outcome, when outcome is {Failure}, or returns {#success} without
20
+ # yielding the control when {Success}.
21
+ # @yield [self]
22
+ # @yieldparam self [Failure]
23
+ # @yieldreturn [Object] result of the block
24
+ #
25
+ # @raise [ArgumentError] if neither default value, nor block given
26
+ def value_or(default = UNDEFINED)
27
+ if block_given?
28
+ warn "block supersedes default value argument" unless default == UNDEFINED
29
+
30
+ return success if success?
31
+
32
+ yield self
33
+ elsif default != UNDEFINED
34
+ return success if success?
35
+
36
+ default
37
+ else
38
+ raise ArgumentError, "either default value, or block must be given"
39
+ end
40
+ end
41
+
42
+ # @return [Object] {#success} if outcome is {Success}
43
+ # @raise [UnwrapError] otherwise
44
+ def unwrap!
45
+ return success if success?
46
+
47
+ raise UnwrapError, self
48
+ end
49
+ end
50
+ end
@@ -3,10 +3,6 @@
3
3
  require "active_support/core_ext/object/blank"
4
4
  require "active_support/core_ext/string/inflections"
5
5
 
6
- require "i18n"
7
-
8
- I18n.load_path += Dir[File.expand_path("#{__dir__}/locale/*.yml")]
9
-
10
6
  module AmazingActivist
11
7
  # @api internal
12
8
  class Polyglot
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AmazingActivist
4
+ class Success < Literal::Data
5
+ include Outcome
6
+
7
+ # @!attribute [r] success
8
+ # @return [AmazingActivist::Base]
9
+ prop :success, _Any?, :positional
10
+
11
+ # @!attribute [r] activity
12
+ # @return [AmazingActivist::Base]
13
+ prop :activity, AmazingActivist::Base
14
+
15
+ def value = success
16
+ def success? = true
17
+ def failure? = false
18
+
19
+ # @api internal
20
+ # @return [Array]
21
+ def deconstruct
22
+ [:success, success, activity]
23
+ end
24
+
25
+ # @api internal
26
+ # @return [Hash]
27
+ def deconstruct_keys(_)
28
+ { success:, activity: }
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AmazingActivist
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end
@@ -1,6 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./amazing_activist/base"
4
- require_relative "./amazing_activist/error"
5
- require_relative "./amazing_activist/outcome"
3
+ require "i18n"
4
+ require "literal"
5
+ require "zeitwerk"
6
+
6
7
  require_relative "./amazing_activist/version"
8
+
9
+ I18n.load_path += Dir[File.expand_path("#{__dir__}/amazing_activist/locale/*.yml")]
10
+
11
+ module AmazingActivist
12
+ Loader = Zeitwerk::Loader.for_gem
13
+
14
+ Loader.ignore("#{__dir__}/amazing-activist.rb")
15
+
16
+ Loader.collapse("#{__dir__}/amazing_activist/errors")
17
+
18
+ Loader.setup
19
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amazing-activist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Zapparov
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-05-01 00:00:00.000000000 Z
10
+ date: 2025-05-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -43,14 +43,28 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: 1.8.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: 1.8.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: zeitwerk
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.7.3
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 2.7.3
54
68
  description: Another take on Command Pattern.
55
69
  email:
56
70
  - alexey@zapparov.com
@@ -64,9 +78,11 @@ files:
64
78
  - lib/amazing-activist.rb
65
79
  - lib/amazing_activist.rb
66
80
  - lib/amazing_activist/base.rb
67
- - lib/amazing_activist/broken_contract_error.rb
68
81
  - lib/amazing_activist/contractable.rb
69
- - lib/amazing_activist/error.rb
82
+ - lib/amazing_activist/errors/broken_contract_error.rb
83
+ - lib/amazing_activist/errors/error.rb
84
+ - lib/amazing_activist/errors/unwrap_error.rb
85
+ - lib/amazing_activist/failure.rb
70
86
  - lib/amazing_activist/irresistible.rb
71
87
  - lib/amazing_activist/locale/en.yml
72
88
  - lib/amazing_activist/locale/gl.yml
@@ -75,17 +91,16 @@ files:
75
91
  - lib/amazing_activist/outcome/success.rb
76
92
  - lib/amazing_activist/polyglot.rb
77
93
  - lib/amazing_activist/rescuable.rb
78
- - lib/amazing_activist/undefined.rb
79
- - lib/amazing_activist/unwrap_error.rb
94
+ - lib/amazing_activist/success.rb
80
95
  - lib/amazing_activist/version.rb
81
96
  homepage: https://github.com/ixti/amazing-activist
82
97
  licenses:
83
98
  - MIT
84
99
  metadata:
85
100
  homepage_uri: https://github.com/ixti/amazing-activist
86
- source_code_uri: https://github.com/ixti/amazing-activist/tree/v0.8.0
101
+ source_code_uri: https://github.com/ixti/amazing-activist/tree/v0.9.0
87
102
  bug_tracker_uri: https://github.com/ixti/amazing-activist/issues
88
- changelog_uri: https://github.com/ixti/amazing-activist/blob/v0.8.0/CHANGES.md
103
+ changelog_uri: https://github.com/ixti/amazing-activist/blob/v0.9.0/CHANGES.md
89
104
  rubygems_mfa_required: 'true'
90
105
  rdoc_options: []
91
106
  require_paths:
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AmazingActivist
4
- UNDEFINED = Object.new.freeze
5
- end
File without changes