servactory 1.4.2 → 1.4.3

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: 781548f2f3cda01476bfa6570d3b1fac94890c3b86271f6bc58a32542f2d2ce9
4
- data.tar.gz: fdddb44644b45ce539193db90e86bfefc63b3fd8140b2a54bd8654d22027191a
3
+ metadata.gz: 3877871b09a517026f3b66849d43348aae44feca18f2f97e101d1948cbc4f9d0
4
+ data.tar.gz: c6f0feca2515ebfd9e6123351c78ac29aee0ded0da66c4f689b259460ee7e403
5
5
  SHA512:
6
- metadata.gz: 60bc160221bfad63233635071ddb345f814c9ca1bdab3867cb5d3ae58835b43bca0c6a7928200a2553995bf836cfe1592d42f3dbfaa48ea97e44af8ae5eb75e2
7
- data.tar.gz: 8a272ec82df4539946462099c9dd45857d1583b1526f74fa78d05e1a8896762e61a580bac4016ea0b78c62bb1ebf16480e9992dfadd4c99950b0938557a68f54
6
+ metadata.gz: 3d9d7237afe83f2b813d3c3555a322c5357dcc56aff1f45c0289c8efc47a139f5dc0f1bfec5f81005be1083aa0372b40a99b345df212aeded2edb425a03337c8
7
+ data.tar.gz: e1c7a318ce6a853269c6c3240e4028921fbad2f2d50c0e0c8d1ef52820bff91fe96638e363bbf63f278419cf477c8c2211a929d0c8f19a7198dd4e76c2c122df
data/README.md CHANGED
@@ -23,6 +23,8 @@ A set of tools for building reliable services of any complexity.
23
23
  - [Must](#must)
24
24
  - [Output attributes](#output-attributes)
25
25
  - [Internal attributes](#internal-attributes)
26
+ - [Stage](#stage)
27
+ - [Failures](#failures)
26
28
  - [Result](#result)
27
29
 
28
30
  ## Requirements
@@ -52,7 +54,7 @@ bundle install
52
54
 
53
55
  ### Preparation
54
56
 
55
- We recommend that you first prepare the following files in your project.
57
+ As a first step, it is recommended to prepare the base class for further inheritance.
56
58
 
57
59
  #### ApplicationService::Errors
58
60
 
@@ -150,9 +152,9 @@ By default, all inputs are required. To make an input optional, specify `false`
150
152
 
151
153
  ```ruby
152
154
  class UsersService::Create < ApplicationService::Base
153
- input :first_name, type: String, internal: true
155
+ input :first_name, type: String
154
156
  input :middle_name, type: String, required: false
155
- input :last_name, type: String, internal: true
157
+ input :last_name, type: String
156
158
 
157
159
  # ...
158
160
  end
@@ -242,6 +244,85 @@ class NotificationService::Create < ApplicationService::Base
242
244
  end
243
245
  ```
244
246
 
247
+ ### Stage
248
+
249
+ A "stage" is a single action or group of actions that needs to be "make".
250
+
251
+ #### Minimal example
252
+
253
+ ```ruby
254
+ stage { make :something }
255
+
256
+ def something
257
+ # ...
258
+ end
259
+ ```
260
+
261
+ #### Condition
262
+
263
+ ```ruby
264
+ stage { make :something, if: -> { Settings.something.enabled } }
265
+
266
+ def something
267
+ # ...
268
+ end
269
+ ```
270
+
271
+ #### Groups
272
+
273
+ The functionality of stage groups will be expanded in future releases.
274
+
275
+ ```ruby
276
+ stage do
277
+ make :assign_api_model
278
+ make :perform_api_request
279
+ end
280
+
281
+ stage do
282
+ make :process_result
283
+ end
284
+
285
+ def assign_api_model
286
+ self.api_model = APIModel.new
287
+ end
288
+
289
+ def perform_api_request
290
+ self.response = APIClient.resource.create(api_model)
291
+ end
292
+
293
+ def process_result
294
+ ARModel.create!(response)
295
+ end
296
+ ```
297
+
298
+ ### Failures
299
+
300
+ The methods that are used in the stages may fail. In order to more informatively provide information about this outside the service, the following methods were prepared.
301
+
302
+ #### Fail
303
+
304
+ ```ruby
305
+ stage { make :check! }
306
+
307
+ def check!
308
+ return if inputs.invoice_number.start_with?("AA")
309
+
310
+ fail!("Invalid invoice number")
311
+ end
312
+ ```
313
+
314
+ #### Fail for input
315
+
316
+ ```ruby
317
+ stage { make :check! }
318
+
319
+ def check!
320
+ return if inputs.invoice_number.start_with?("AA")
321
+
322
+ fail_input!(:invoice_number, "Invalid invoice number")
323
+ end
324
+ ```
325
+
245
326
  ### Result
246
327
 
247
328
  All services have the result of their work. For example, in case of success this call:
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Servactory
4
4
  class Base
5
+ include Configuration::DSL
5
6
  include Context::DSL
6
7
  include InputArguments::DSL
7
8
  include InternalArguments::DSL
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Configuration
5
+ module DSL
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ private
12
+
13
+ def configuration(&block)
14
+ @configuration_factory ||= Factory.new
15
+
16
+ @configuration_factory.instance_eval(&block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Configuration
5
+ class Factory
6
+ def input_argument_error_class(input_argument_error_class)
7
+ Servactory.configuration.input_argument_error_class = input_argument_error_class
8
+ end
9
+
10
+ def output_argument_error_class(output_argument_error_class)
11
+ Servactory.configuration.output_argument_error_class = output_argument_error_class
12
+ end
13
+
14
+ def internal_argument_error_class(internal_argument_error_class)
15
+ Servactory.configuration.internal_argument_error_class = internal_argument_error_class
16
+ end
17
+
18
+ def failure_class(failure_class)
19
+ Servactory.configuration.failure_class = failure_class
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Configuration
5
+ class Setup
6
+ attr_accessor :input_argument_error_class,
7
+ :internal_argument_error_class,
8
+ :output_argument_error_class,
9
+ :failure_class
10
+
11
+ def initialize
12
+ @input_argument_error_class = Servactory::Errors::InputArgumentError
13
+ @internal_argument_error_class = Servactory::Errors::InternalArgumentError
14
+ @output_argument_error_class = Servactory::Errors::OutputArgumentError
15
+
16
+ @failure_class = Servactory::Errors::Failure
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Context
5
+ module Callable
6
+ def call!(arguments = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
7
+ @context_store = Store.new(self)
8
+
9
+ assign_data_with(arguments)
10
+
11
+ input_arguments_workbench.find_unnecessary!
12
+ input_arguments_workbench.check_rules!
13
+ output_arguments_workbench.find_conflicts_in!(
14
+ collection_of_internal_arguments: collection_of_internal_arguments
15
+ )
16
+
17
+ prepare_data
18
+
19
+ input_arguments_workbench.check!
20
+
21
+ stage_handyman.run_methods!
22
+
23
+ context_store.context.raise_first_fail
24
+
25
+ Servactory::Result.prepare_for(
26
+ context: context_store.context,
27
+ collection_of_output_arguments: collection_of_output_arguments
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :context_store
34
+
35
+ def assign_data_with(arguments) # rubocop:disable Metrics/AbcSize
36
+ input_arguments_workbench.assign(
37
+ context: context_store.context,
38
+ arguments: arguments,
39
+ collection_of_input_options: collection_of_input_options
40
+ )
41
+
42
+ internal_arguments_workbench.assign(context: context_store.context)
43
+ output_arguments_workbench.assign(context: context_store.context)
44
+ stage_handyman&.assign(context: context_store.context)
45
+ end
46
+
47
+ def prepare_data
48
+ input_arguments_workbench.prepare # 1
49
+
50
+ output_arguments_workbench.prepare # 2
51
+ internal_arguments_workbench.prepare # 3
52
+ end
53
+ end
54
+ end
55
+ end
@@ -4,61 +4,8 @@ module Servactory
4
4
  module Context
5
5
  module DSL
6
6
  def self.included(base)
7
- base.extend(ClassMethods)
8
- end
9
-
10
- module ClassMethods
11
- def call!(arguments = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
12
- @context_store ||= Store.new(self)
13
-
14
- assign_data_with(arguments)
15
-
16
- input_arguments_workbench.find_unnecessary!
17
- input_arguments_workbench.check_rules!
18
- output_arguments_workbench.find_conflicts_in!(
19
- collection_of_internal_arguments: collection_of_internal_arguments
20
- )
21
-
22
- prepare_data
23
-
24
- input_arguments_workbench.check!
25
-
26
- stage_handyman.run_methods!
27
-
28
- Servactory::Result.prepare_for(
29
- context: context_store.context,
30
- collection_of_output_arguments: collection_of_output_arguments
31
- )
32
- end
33
-
34
- private
35
-
36
- attr_reader :context_store
37
-
38
- def assign_data_with(arguments) # rubocop:disable Metrics/AbcSize
39
- input_arguments_workbench.assign(
40
- context: context_store.context,
41
- arguments: arguments,
42
- collection_of_input_options: collection_of_input_options
43
- )
44
-
45
- internal_arguments_workbench.assign(context: context_store.context)
46
- output_arguments_workbench.assign(context: context_store.context)
47
- stage_handyman&.assign(context: context_store.context)
48
- end
49
-
50
- def prepare_data
51
- input_arguments_workbench.prepare # 1
52
-
53
- output_arguments_workbench.prepare # 2
54
- internal_arguments_workbench.prepare # 3
55
- end
56
-
57
- def configuration(&block)
58
- context_configuration = Servactory::Context::Configuration.new
59
-
60
- context_configuration.instance_eval(&block)
61
- end
7
+ base.extend(Callable)
8
+ base.prepend(Workspace)
62
9
  end
63
10
  end
64
11
  end
@@ -7,44 +7,6 @@ module Servactory
7
7
 
8
8
  def initialize(service_class)
9
9
  @context = service_class.new
10
-
11
- service_class.class_eval(service_class_template_with(context))
12
- end
13
-
14
- private
15
-
16
- # EXAMPLE:
17
- #
18
- # attr_reader(:inputs)
19
- #
20
- # def assign_inputs(inputs)
21
- # @inputs = inputs
22
- # end
23
- #
24
- # def fail_input!(input_attribute_name, message, prefix: true)
25
- # message_text = prefix ? "[#{context.class.name}] Custom `\#{input_attribute_name}` input error: " : ""
26
- #
27
- # message_text += message
28
- #
29
- # raise Servactory.configuration.input_argument_error_class, message_text
30
- # end
31
- #
32
- def service_class_template_with(context)
33
- <<-RUBY
34
- attr_reader(:inputs)
35
-
36
- def assign_inputs(inputs)
37
- @inputs = inputs
38
- end
39
-
40
- def fail_input!(input_attribute_name, message, prefix: true)
41
- message_text = prefix ? "[#{context.class.name}] Custom `\#{input_attribute_name}` input error: " : ""
42
-
43
- message_text += message
44
-
45
- raise Servactory.configuration.input_argument_error_class, message_text
46
- end
47
- RUBY
48
10
  end
49
11
  end
50
12
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Context
5
+ module Workspace
6
+ class Error
7
+ attr_reader :type,
8
+ :message,
9
+ :attribute_name
10
+
11
+ def initialize(type:, message:, attribute_name: nil)
12
+ @type = type
13
+ @message = message
14
+ @attribute_name = attribute_name
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Context
5
+ module Workspace
6
+ class Errors
7
+ # NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
8
+ extend Forwardable
9
+ def_delegators :@collection, :<<, :filter, :reject, :empty?, :first
10
+
11
+ def initialize(collection = [])
12
+ @collection = collection
13
+ end
14
+
15
+ def not_blank_and_uniq
16
+ Errors.new(reject(&:blank?).uniq)
17
+ end
18
+
19
+ def for_fails
20
+ filtered = filter { |error| error.type == :fail }
21
+
22
+ Errors.new(filtered)
23
+ end
24
+
25
+ def for_inputs
26
+ filtered = filter { |error| error.type == :inputs }
27
+
28
+ Errors.new(filtered)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Context
5
+ module Workspace
6
+ attr_reader :inputs
7
+
8
+ def errors
9
+ @errors ||= Errors.new
10
+ end
11
+
12
+ def assign_inputs(inputs)
13
+ @inputs = inputs
14
+ end
15
+
16
+ def fail_input!(input_attribute_name, message)
17
+ raise Servactory.configuration.input_argument_error_class,
18
+ Error.new(type: :input, attribute_name: input_attribute_name, message: message).message
19
+ end
20
+
21
+ def fail!(error)
22
+ errors << Error.new(type: :fail, message: error)
23
+ end
24
+
25
+ def raise_first_fail
26
+ return if (tmp_errors = errors.for_fails.not_blank_and_uniq).empty?
27
+
28
+ raise Servactory.configuration.failure_class, tmp_errors.first.message
29
+ end
30
+ end
31
+ end
32
+ end
@@ -5,18 +5,18 @@ module Servactory
5
5
  class OptionsCollection
6
6
  # NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
7
7
  extend Forwardable
8
- def_delegators :@collection, :<<, :each, :select, :map
8
+ def_delegators :@collection, :<<, :filter, :each, :map
9
9
 
10
10
  def initialize(*)
11
11
  @collection = []
12
12
  end
13
13
 
14
14
  def check_classes
15
- select { |option| option.check_class.present? }.map(&:check_class).uniq
15
+ filter { |option| option.check_class.present? }.map(&:check_class).uniq
16
16
  end
17
17
 
18
18
  def options_for_checks
19
- select(&:need_for_checks?).to_h do |option|
19
+ filter(&:need_for_checks?).to_h do |option|
20
20
  value = if option.value.is_a?(Hash)
21
21
  option.value.key?(:is) ? option.value.fetch(:is) : option.value
22
22
  else
@@ -75,15 +75,17 @@ module Servactory
75
75
 
76
76
  # EXAMPLE:
77
77
  #
78
- # private; attr_reader(*[:attr_1]);
78
+ # private
79
+ #
80
+ # attr_reader(*[:attr_1])
79
81
  #
80
82
  def context_internal_variables_template
81
83
  return if @internal_variables.blank?
82
84
 
83
- @context_internal_variables_template ||= <<-RUBY.squish
84
- private;
85
+ @context_internal_variables_template ||= <<-RUBY
86
+ private
85
87
 
86
- attr_reader(*#{@internal_variables.keys});
88
+ attr_reader(*#{@internal_variables.keys})
87
89
  RUBY
88
90
  end
89
91
  end
@@ -29,29 +29,29 @@ module Servactory
29
29
 
30
30
  # EXAMPLE:
31
31
  #
32
- # define_method(:user=) do |value|;
33
- # Servactory::InternalArguments::Checks::Type.check!( context: self, internal_argument:, value: );
32
+ # define_method(:user=) do |value|
33
+ # Servactory::InternalArguments::Checks::Type.check!( context: self, internal_argument:, value: )
34
34
  #
35
- # instance_variable_set(:@user, value);
36
- # end;
35
+ # instance_variable_set(:@user, value)
36
+ # end
37
37
  #
38
- # private attr_reader :user;
38
+ # private attr_reader :user
39
39
  #
40
40
  def context_internal_argument_template_for(internal_argument)
41
- <<-RUBY.squish
42
- define_method(:#{internal_argument.name}=) do |value|;
41
+ <<-RUBY
42
+ define_method(:#{internal_argument.name}=) do |value|
43
43
  Servactory::InternalArguments::Checks::Type.check!(
44
44
  context: self,
45
45
  internal_argument: internal_argument,
46
46
  value: value
47
- );
47
+ )
48
48
 
49
- instance_variable_set(:@#{internal_argument.name}, value);
50
- end;
49
+ instance_variable_set(:@#{internal_argument.name}, value)
50
+ end
51
51
 
52
- private;
52
+ private
53
53
 
54
- attr_reader :#{internal_argument.name};
54
+ attr_reader :#{internal_argument.name}
55
55
  RUBY
56
56
  end
57
57
  end
@@ -29,31 +29,31 @@ module Servactory
29
29
 
30
30
  # EXAMPLE:
31
31
  #
32
- # define_method(:user=) do |value|;
33
- # Servactory::InternalArguments::Checks::Type.check!( context: self, output_argument:, value: );
32
+ # define_method(:user=) do |value|
33
+ # Servactory::InternalArguments::Checks::Type.check!( context: self, output_argument:, value: )
34
34
  #
35
- # instance_variable_set(:@user, value);
36
- # end;
35
+ # instance_variable_set(:@user, value)
36
+ # end
37
37
  #
38
- # private;
38
+ # private
39
39
  #
40
- # attr_reader :user;
40
+ # attr_reader :user
41
41
  #
42
42
  def context_output_argument_template_for(output_argument)
43
- <<-RUBY.squish
44
- define_method(:#{output_argument.name}=) do |value|;
43
+ <<-RUBY
44
+ define_method(:#{output_argument.name}=) do |value|
45
45
  Servactory::OutputArguments::Checks::Type.check!(
46
46
  context: self,
47
47
  output_argument: output_argument,
48
48
  value: value
49
- );
49
+ )
50
50
 
51
- instance_variable_set(:@#{output_argument.name}, value);
52
- end;
51
+ instance_variable_set(:@#{output_argument.name}, value)
52
+ end
53
53
 
54
- private;
54
+ private
55
55
 
56
- attr_reader :#{output_argument.name};
56
+ attr_reader :#{output_argument.name}
57
57
  RUBY
58
58
  end
59
59
  end
@@ -4,7 +4,7 @@ module Servactory
4
4
  module VERSION
5
5
  MAJOR = 1
6
6
  MINOR = 4
7
- PATCH = 2
7
+ PATCH = 3
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
10
10
  end
data/lib/servactory.rb CHANGED
@@ -17,11 +17,11 @@ module Servactory
17
17
  module_function
18
18
 
19
19
  def configuration
20
- @configuration ||= Servactory::Configuration.new
20
+ @configuration ||= Servactory::Configuration::Setup.new
21
21
  end
22
22
 
23
23
  def reset
24
- @configuration = Servactory::Configuration.new
24
+ @configuration = Servactory::Configuration::Setup.new
25
25
  end
26
26
 
27
27
  def configure
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: 1.4.2
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-08 00:00:00.000000000 Z
11
+ date: 2023-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -161,10 +161,16 @@ files:
161
161
  - Rakefile
162
162
  - lib/servactory.rb
163
163
  - lib/servactory/base.rb
164
- - lib/servactory/configuration.rb
164
+ - lib/servactory/configuration/dsl.rb
165
+ - lib/servactory/configuration/factory.rb
166
+ - lib/servactory/configuration/setup.rb
167
+ - lib/servactory/context/callable.rb
165
168
  - lib/servactory/context/configuration.rb
166
169
  - lib/servactory/context/dsl.rb
167
170
  - lib/servactory/context/store.rb
171
+ - lib/servactory/context/workspace.rb
172
+ - lib/servactory/context/workspace/error.rb
173
+ - lib/servactory/context/workspace/errors.rb
168
174
  - lib/servactory/errors/base.rb
169
175
  - lib/servactory/errors/failure.rb
170
176
  - lib/servactory/errors/input_argument_error.rb
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- class Configuration
5
- attr_accessor :input_argument_error_class,
6
- :internal_argument_error_class,
7
- :output_argument_error_class,
8
- :failure_class
9
-
10
- def initialize
11
- @input_argument_error_class = Servactory::Errors::InputArgumentError
12
- @internal_argument_error_class = Servactory::Errors::InternalArgumentError
13
- @output_argument_error_class = Servactory::Errors::OutputArgumentError
14
-
15
- @failure_class = Servactory::Errors::Failure
16
- end
17
- end
18
- end