servactory 1.4.2 → 1.4.3

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: 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