servactory 2.4.2 → 2.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbf66d2c89ef6a0b50a80343230e56d940e41ce3754528cc902dbf17663800a7
4
- data.tar.gz: dc302908e1305c9b9cbecacf1506fe9222db5e9b835fd0e8febb6e54e1211b92
3
+ metadata.gz: 46aa75596d1b4afaef52eb55ad99884c13f7c2b62d5d264a54e88c4d840f8588
4
+ data.tar.gz: a40bdcc6ab7922f6358c94502683854b3897930633f90656a498798e74804606
5
5
  SHA512:
6
- metadata.gz: b9eb5b104b1ff966b92b2f656070379daf252d00921937383e094549bc3ae3a7782b6e43cbdec08051b38168a1d2d7f8e38d4b90e9c6a367828fd40c66218399
7
- data.tar.gz: 2b51810de55de6b8a13ba4f7cc22e2ebd913839cd86fa68f05cec9c83b5558dbcba9bf13cf194f163287c5c7a0d7bab5bdf5a715b159106338611cc2d082c706
6
+ metadata.gz: b14c88eed72b89ab6fa6a9e37e7403466c14def2265df8a75b496700b3cb57ab7e19407b1564bd48bb01a180ec05ca07c473ae32e904a048c7bbb6b052f06507
7
+ data.tar.gz: 3112d0f9f1f03e00f64c5e2e36c0c503b3c4854fe12b7f6e961eec679135263e05d804384a7e0e269dee0681817174d0917a4b0022e93bdb6f6a2cb215bd3ae7
data/README.md CHANGED
@@ -38,13 +38,15 @@ class UserService::Authenticate < Servactory::Base
38
38
 
39
39
  output :user, type: User
40
40
 
41
+ make :authenticate!
42
+
41
43
  private
42
44
 
43
- def call
45
+ def authenticate!
44
46
  if (user = User.authenticate_by(email: inputs.email, password: inputs.password)).present?
45
47
  outputs.user = user
46
48
  else
47
- fail!(message: "Authentication failed")
49
+ fail!(message: "Authentication failed", meta: { email: inputs.email })
48
50
  end
49
51
  end
50
52
  end
@@ -55,14 +57,14 @@ end
55
57
  ```ruby
56
58
  class SessionsController < ApplicationController
57
59
  def create
58
- service_result = UserService::Authenticate.call(**session_params)
60
+ service = UserService::Authenticate.call(**session_params)
59
61
 
60
- if service_result.success?
61
- session[:current_user_id] = service_result.user.id
62
- redirect_to service_result.user
62
+ if service.success?
63
+ session[:current_user_id] = service.user.id
64
+ redirect_to service.user
63
65
  else
64
- flash.now[:message] = service_result.error.message
65
- render :new
66
+ flash.now[:alert] = service.error.message
67
+ render :new, status: :unprocessable_entity
66
68
  end
67
69
  end
68
70
 
@@ -17,6 +17,25 @@ module Servactory
17
17
 
18
18
  private
19
19
 
20
+ # NOTE: Based on https://github.com/rails/rails/blob/main/activesupport/lib/active_support/rescuable.rb
21
+ def fail_on!(*class_names, with: nil, &block) # rubocop:disable Metrics/MethodLength
22
+ with ||= block || ->(exception:) { exception.message }
23
+
24
+ class_names.each do |class_name|
25
+ key = if class_name.is_a?(Module) && class_name.respond_to?(:===)
26
+ class_name.name
27
+ elsif class_name.is_a?(String)
28
+ class_name
29
+ else
30
+ raise ArgumentError,
31
+ "#{class_name.inspect} must be an Exception class or a String referencing an Exception class"
32
+ end
33
+
34
+ # Put the new handler at the end because the list is read in reverse.
35
+ config.action_rescue_handlers += [[key, with]]
36
+ end
37
+ end
38
+
20
39
  def stage(&block)
21
40
  @current_stage = Stages::Stage.new(position: next_position)
22
41
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Actions
5
+ module RescueHandlers
6
+ class Collection
7
+ extend Forwardable
8
+ def_delegators :@collection, :+, :detect, :reverse_each
9
+
10
+ def initialize(*)
11
+ @collection = Set.new
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -57,6 +57,8 @@ module Servactory
57
57
 
58
58
  def call_method(method)
59
59
  @context.send(method.name)
60
+ rescue StandardError => e
61
+ rescue_with_handler(e) || raise
60
62
  end
61
63
 
62
64
  def unnecessary_for_stage?(stage)
@@ -83,6 +85,23 @@ module Servactory
83
85
 
84
86
  !condition.call(context: @context)
85
87
  end
88
+
89
+ def rescue_with_handler(exception) # rubocop:disable Metrics/MethodLength
90
+ _, handler = @context.class.config.action_rescue_handlers.reverse_each.detect do |class_or_name, _|
91
+ if (detected_exception = Servactory::Utils.constantize_class(class_or_name))
92
+ detected_exception === exception # rubocop:disable Style/CaseEquality
93
+ end
94
+ end
95
+
96
+ return if handler.nil?
97
+
98
+ @context.fail!(
99
+ message: handler.call(exception: exception),
100
+ meta: {
101
+ original_exception: exception
102
+ }
103
+ )
104
+ end
86
105
  end
87
106
  end
88
107
  end
@@ -18,6 +18,8 @@ module Servactory
18
18
  child.config.success_class = config.success_class
19
19
  child.config.failure_class = config.failure_class
20
20
 
21
+ child.config.result_class = config.result_class
22
+
21
23
  child.config.collection_mode_class_names = config.collection_mode_class_names
22
24
 
23
25
  child.config.input_option_helpers = config.input_option_helpers
@@ -26,6 +28,7 @@ module Servactory
26
28
 
27
29
  child.config.action_aliases = config.action_aliases
28
30
  child.config.action_shortcuts = config.action_shortcuts
31
+ child.config.action_rescue_handlers = config.action_rescue_handlers
29
32
  end
30
33
 
31
34
  def config
@@ -63,6 +63,12 @@ module Servactory
63
63
  raise_error_about_wrong_exception_class_with(:failure_class, failure_class)
64
64
  end
65
65
 
66
+ def result_class(result_class)
67
+ return @config.result_class = result_class if subclass_of_result?(result_class)
68
+
69
+ raise_error_about_wrong_result_class_with(:result_class, result_class)
70
+ end
71
+
66
72
  def collection_mode_class_names(collection_mode_class_names)
67
73
  @config.collection_mode_class_names.merge(collection_mode_class_names)
68
74
  end
@@ -91,19 +97,34 @@ module Servactory
91
97
  @config.action_shortcuts.merge(action_shortcuts)
92
98
  end
93
99
 
94
- ##########################################################################
100
+ private
101
+
102
+ # def action_rescue_handlers(action_rescue_handlers)
103
+ # @config.action_rescue_handlers.merge(action_rescue_handlers)
104
+ # end
95
105
 
96
106
  def subclass_of_exception?(value)
97
107
  value.is_a?(Class) && value <= Exception
98
108
  end
99
109
 
110
+ def subclass_of_result?(value)
111
+ value.is_a?(Class) && value <= Servactory::Result
112
+ end
113
+
100
114
  ##########################################################################
101
115
 
102
116
  def raise_error_about_wrong_exception_class_with(config_name, value)
103
117
  raise ArgumentError,
104
118
  "Error in `#{config_name}` configuration. " \
105
119
  "The `#{value}` value must be a subclass of `Exception`. " \
106
- "See example configuration here: https://servactory.com/guide/configuration"
120
+ "See configuration example here: https://servactory.com/guide/configuration"
121
+ end
122
+
123
+ def raise_error_about_wrong_result_class_with(config_name, value)
124
+ raise ArgumentError,
125
+ "Error in `#{config_name}` configuration. " \
126
+ "The `#{value}` value must be a subclass of `Servactory::Result`. " \
127
+ "See configuration example here: https://servactory.com/guide/configuration"
107
128
  end
108
129
  end
109
130
  end
@@ -8,15 +8,17 @@ module Servactory
8
8
  :output_exception_class,
9
9
  :success_class,
10
10
  :failure_class,
11
+ :result_class,
11
12
  :collection_mode_class_names,
12
13
  :hash_mode_class_names,
13
14
  :input_option_helpers,
14
15
  :internal_option_helpers,
15
16
  :output_option_helpers,
16
17
  :action_aliases,
17
- :action_shortcuts
18
+ :action_shortcuts,
19
+ :action_rescue_handlers
18
20
 
19
- def initialize # rubocop:disable Metrics/MethodLength
21
+ def initialize # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
20
22
  @input_exception_class = Servactory::Exceptions::Input
21
23
  @internal_exception_class = Servactory::Exceptions::Internal
22
24
  @output_exception_class = Servactory::Exceptions::Output
@@ -24,6 +26,8 @@ module Servactory
24
26
  @success_class = Servactory::Exceptions::Success
25
27
  @failure_class = Servactory::Exceptions::Failure
26
28
 
29
+ @result_class = Servactory::Result
30
+
27
31
  @collection_mode_class_names =
28
32
  Servactory::Maintenance::CollectionMode::ClassNamesCollection.new(default_collection_mode_class_names)
29
33
 
@@ -41,6 +45,7 @@ module Servactory
41
45
 
42
46
  @action_aliases = Servactory::Actions::Aliases::Collection.new
43
47
  @action_shortcuts = Servactory::Actions::Shortcuts::Collection.new
48
+ @action_rescue_handlers = Servactory::Actions::RescueHandlers::Collection.new
44
49
  end
45
50
 
46
51
  private
@@ -8,9 +8,9 @@ module Servactory
8
8
 
9
9
  _call!(context, **arguments)
10
10
 
11
- Servactory::Result.success_for(context: context)
11
+ config.result_class.success_for(context: context)
12
12
  rescue config.success_class => e
13
- Servactory::Result.success_for(context: e.context)
13
+ config.result_class.success_for(context: e.context)
14
14
  end
15
15
 
16
16
  def call(arguments = {})
@@ -18,11 +18,11 @@ module Servactory
18
18
 
19
19
  _call!(context, **arguments)
20
20
 
21
- Servactory::Result.success_for(context: context)
21
+ config.result_class.success_for(context: context)
22
22
  rescue config.success_class => e
23
- Servactory::Result.success_for(context: e.context)
23
+ config.result_class.success_for(context: e.context)
24
24
  rescue config.failure_class => e
25
- Servactory::Result.failure_for(context: context, exception: e)
25
+ config.result_class.failure_for(context: context, exception: e)
26
26
  end
27
27
 
28
28
  private
@@ -5,7 +5,7 @@ module Servactory
5
5
  module CollectionMode
6
6
  class ClassNamesCollection
7
7
  extend Forwardable
8
- def_delegators :@collection, :include?
8
+ def_delegators :@collection, :merge, :include?
9
9
 
10
10
  def initialize(collection)
11
11
  @collection = collection
@@ -68,11 +68,7 @@ module Servactory
68
68
 
69
69
  def prepared_types_from(types)
70
70
  types.map do |type|
71
- if type.is_a?(String)
72
- Object.const_get(type)
73
- else
74
- type
75
- end
71
+ Servactory::Utils.constantize_class(type)
76
72
  end
77
73
  end
78
74
 
@@ -89,11 +89,7 @@ module Servactory
89
89
 
90
90
  def prepared_types_from(types)
91
91
  types.map do |type|
92
- if type.is_a?(String)
93
- Object.const_get(type)
94
- else
95
- type
96
- end
92
+ Servactory::Utils.constantize_class(type)
97
93
  end
98
94
  end
99
95
  end
@@ -9,6 +9,18 @@ module Servactory
9
9
  define_singleton_method(key) { value }
10
10
  end
11
11
  end
12
+
13
+ def inspect
14
+ "#<#{self.class.name} #{draw_result}>"
15
+ end
16
+
17
+ private
18
+
19
+ def draw_result
20
+ methods(false).sort.map do |method_name|
21
+ "@#{method_name}=#{send(method_name)}"
22
+ end.join(", ")
23
+ end
12
24
  end
13
25
 
14
26
  private_constant :Outputs
@@ -39,7 +51,7 @@ module Servactory
39
51
  end
40
52
 
41
53
  def on_failure(type = :all)
42
- yield(exception: @exception) if failure? && [:all, @exception&.type].include?(type)
54
+ yield(outputs: outputs, exception: @exception) if failure? && [:all, @exception&.type].include?(type)
43
55
 
44
56
  self
45
57
  end
@@ -4,11 +4,15 @@ module Servactory
4
4
  module TestKit
5
5
  class Result
6
6
  def self.as_success(attributes = {})
7
- Servactory::Result.success_for(context: new(attributes))
7
+ context = new(attributes)
8
+
9
+ Servactory::Result.success_for(context: context)
8
10
  end
9
11
 
10
12
  def self.as_failure(attributes = {}, exception: nil)
11
- Servactory::Result.failure_for(context: new(attributes), exception: exception)
13
+ context = new(attributes)
14
+
15
+ Servactory::Result.failure_for(context: context, exception: exception)
12
16
  end
13
17
 
14
18
  def initialize(attributes = {})
@@ -84,5 +84,18 @@ module Servactory
84
84
  end
85
85
  end
86
86
  end
87
+
88
+ def constantize_class(class_or_name)
89
+ case class_or_name
90
+ when String, Symbol
91
+ begin
92
+ Object.const_get(class_or_name)
93
+ rescue NameError
94
+ class_or_name.safe_constantize
95
+ end
96
+ else
97
+ class_or_name
98
+ end
99
+ end
87
100
  end
88
101
  end
@@ -3,9 +3,9 @@
3
3
  module Servactory
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 4
7
- PATCH = 2
8
- PRE = nil
6
+ MINOR = 5
7
+ PATCH = 0
8
+ PRE = "rc1"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servactory
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.2
4
+ version: 2.5.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-29 00:00:00.000000000 Z
11
+ date: 2024-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -242,6 +242,7 @@ files:
242
242
  - lib/servactory/actions/aliases/collection.rb
243
243
  - lib/servactory/actions/collection.rb
244
244
  - lib/servactory/actions/dsl.rb
245
+ - lib/servactory/actions/rescue_handlers/collection.rb
245
246
  - lib/servactory/actions/shortcuts/collection.rb
246
247
  - lib/servactory/actions/stages/collection.rb
247
248
  - lib/servactory/actions/stages/stage.rb
@@ -339,7 +340,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
339
340
  requirements:
340
341
  - - ">="
341
342
  - !ruby/object:Gem::Version
342
- version: 2.7.0
343
+ version: 3.0.0
343
344
  required_rubygems_version: !ruby/object:Gem::Requirement
344
345
  requirements:
345
346
  - - ">="