servactory 1.1.0 → 1.3.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: 6ba22a3302a9dde10fcadfc2cc5777e83589e86862b8e96484ddd3ad62e78782
4
- data.tar.gz: 4fa891477fb78494b3df34bb50f3b5bea018d331471662e235d91c49d82b9e6c
3
+ metadata.gz: e9766f87f647b0873a6671d88811141a77b571fb15ac5ee6b10b78e2e5696ca6
4
+ data.tar.gz: c36ee73abad2d9241f6b48190cbf3351fe8c9c40bc7cb9bdfdfb09c04e5d05f8
5
5
  SHA512:
6
- metadata.gz: c4c5e7cbf5c7708cc468f25152086d974c1ecfee76222bade55215c4f14ee9b0b20194755338152ac21e72845e8c1358f4e185b08b6db08319c48990b5cf43ef
7
- data.tar.gz: 70c1325c6b7de415565d41f4471798d1349c0627eeca7a329b876d703500b338acb725a5bd5adc41578656488541a13690358d377799c493f8e8ba719bb1c710
6
+ metadata.gz: 11b27258c9a302cb989eed03fe4f1325c03855f58fd3a79b6cad7a3846fa8e9395f780fe0d9a2dd30c9b044d4e98df4677857c93c275f9bfe8780d6ebefe5d5e
7
+ data.tar.gz: 57800e68b8c8bece089513dcf0b0ff8da6ab2efb33a4d4a694b11995dae51b7dd0496b1b0f4b6c0ba403db5c6037432e43371d68bea5b56bb05844d98fb36e8e
data/README.md CHANGED
@@ -81,36 +81,105 @@ class SendService < ApplicationService::Base
81
81
  end
82
82
  ```
83
83
 
84
- ### Inputs
84
+ ### Input attributes
85
+
86
+ #### Isolated usage
87
+
88
+ With this approach, all input attributes are available only from `inputs`.
85
89
 
86
90
  ```ruby
87
- class SendService < ApplicationService::Base
91
+ class UserService::Accept < ApplicationService::Base
88
92
  input :user, type: User
89
93
 
90
- stage { make :something }
94
+ stage { make :accept! }
91
95
 
92
96
  private
93
97
 
94
- def something
95
- # ...
98
+ def accept!
99
+ inputs.user.accept!
96
100
  end
97
101
  end
98
102
  ```
99
103
 
100
- ### Outputs
104
+ #### As an internal argument
105
+
106
+ With this approach, all input attributes are available from `inputs` as well as directly from the context.
101
107
 
102
108
  ```ruby
103
- class SendService < ApplicationService::Base
109
+ class UserService::Accept < ApplicationService::Base
110
+ input :user, type: User, internal: true
111
+
112
+ stage { make :accept! }
113
+
114
+ private
115
+
116
+ def accept!
117
+ user.accept!
118
+ end
119
+ end
120
+ ```
121
+
122
+ ### Output attributes
123
+
124
+ ```ruby
125
+ class NotificationService::Create < ApplicationService::Base
104
126
  input :user, type: User
105
127
 
106
128
  output :notification, type: Notification
107
129
 
108
- stage { make :something }
130
+ stage { make :create_notification! }
109
131
 
110
132
  private
111
133
 
112
- def something
113
- self.notification = Notification.create!
134
+ def create_notification!
135
+ self.notification = Notification.create!(user: inputs.user)
136
+ end
137
+ end
138
+ ```
139
+
140
+ ### Internal attributes
141
+
142
+ ```ruby
143
+ class NotificationService::Create < ApplicationService::Base
144
+ input :user, type: User
145
+
146
+ internal :inviter, type: User
147
+
148
+ output :notification, type: Notification
149
+
150
+ stage do
151
+ make :assign_inviter
152
+ make :create_notification!
153
+ end
154
+
155
+ private
156
+
157
+ def assign_inviter
158
+ self.inviter = user.inviter
159
+ end
160
+
161
+ def create_notification!
162
+ self.notification = Notification.create!(user: inputs.user, inviter:)
114
163
  end
115
164
  end
116
165
  ```
166
+
167
+ ### Result
168
+
169
+ All services have the result of their work. For example, in case of success this call:
170
+
171
+ ```ruby
172
+ service_result = NotificationService::Create.call!(user: User.first)
173
+ ```
174
+
175
+ Will return this:
176
+
177
+ ```ruby
178
+ #<Servactory::Result:0x0000000112c00748 @notification=...>
179
+ ```
180
+
181
+ And then you can work with this result, for example, in this way:
182
+
183
+ ```ruby
184
+ Notification::SendJob.perform_later(service_result.notification.id)
185
+ ```
@@ -15,7 +15,9 @@ module Servactory
15
15
 
16
16
  input_arguments_workbench.find_unnecessary!
17
17
  input_arguments_workbench.check_rules!
18
- output_arguments_workbench.find_conflicts_in!(collection_of_internal_arguments:)
18
+ output_arguments_workbench.find_conflicts_in!(
19
+ collection_of_internal_arguments: collection_of_internal_arguments
20
+ )
19
21
 
20
22
  prepare_data
21
23
 
@@ -25,7 +27,7 @@ module Servactory
25
27
 
26
28
  Servactory::Result.prepare_for(
27
29
  context: context_store.context,
28
- collection_of_output_arguments:
30
+ collection_of_output_arguments: collection_of_output_arguments
29
31
  )
30
32
  end
31
33
 
@@ -34,10 +36,10 @@ module Servactory
34
36
  attr_reader :context_store
35
37
 
36
38
  def assign_data_with(arguments)
37
- input_arguments_workbench.assign(context: context_store.context, arguments:) # 1
38
- internal_arguments_workbench.assign(context: context_store.context) # 2
39
- output_arguments_workbench.assign(context: context_store.context) # 3
40
- stage_handyman&.assign(context: context_store.context) # 4
39
+ input_arguments_workbench.assign(context: context_store.context, arguments: arguments) # 1
40
+ internal_arguments_workbench.assign(context: context_store.context) # 2
41
+ output_arguments_workbench.assign(context: context_store.context) # 3
42
+ stage_handyman&.assign(context: context_store.context) # 4
41
43
  end
42
44
 
43
45
  def prepare_data
@@ -47,10 +49,10 @@ module Servactory
47
49
  internal_arguments_workbench.prepare # 3
48
50
  end
49
51
 
50
- def configuration(&)
52
+ def configuration(&block)
51
53
  context_configuration = Servactory::Context::Configuration.new
52
54
 
53
- context_configuration.instance_eval(&)
55
+ context_configuration.instance_eval(&block)
54
56
  end
55
57
  end
56
58
  end
@@ -13,7 +13,7 @@ module Servactory
13
13
  def self.check(context:, input:, value:, check_key:, **)
14
14
  return unless should_be_checked_for?(input, check_key)
15
15
 
16
- new(context:, input:, value:).check
16
+ new(context: context, input: input, value: value).check
17
17
  end
18
18
 
19
19
  def self.should_be_checked_for?(input, check_key)
@@ -14,7 +14,7 @@ module Servactory
14
14
  def self.check(context:, input:, value:, check_key:, check_options:)
15
15
  return unless should_be_checked_for?(input, check_key)
16
16
 
17
- new(context:, input:, value:, check_options:).check
17
+ new(context: context, input: input, value: value, check_options: check_options).check
18
18
  end
19
19
 
20
20
  def self.should_be_checked_for?(input, check_key)
@@ -32,7 +32,7 @@ module Servactory
32
32
  @check_options = check_options
33
33
  end
34
34
 
35
- def check
35
+ def check # rubocop:disable Metrics/MethodLength
36
36
  @check_options.each do |code, options|
37
37
  message = call_or_fetch_message_from(code, options)
38
38
 
@@ -42,7 +42,7 @@ module Servactory
42
42
  DEFAULT_MESSAGE,
43
43
  service_class_name: @context.class.name,
44
44
  input: @input,
45
- code:
45
+ code: code
46
46
  )
47
47
  end
48
48
 
@@ -57,16 +57,15 @@ module Servactory
57
57
  return if check.call(value: @value)
58
58
 
59
59
  message.presence || DEFAULT_MESSAGE
60
- rescue StandardError => e
60
+ rescue StandardError => _e
61
61
  message_text =
62
- "[#{@context.class.name}] Syntax error inside `#{code}` of " \
63
- "`#{@input.name}` input: [#{e.class}] #{e.message}"
62
+ "[#{@context.class.name}] Syntax error inside `#{code}` of `#{@input.name}` input"
64
63
 
65
64
  add_error(
66
65
  message_text,
67
66
  service_class_name: @context.class.name,
68
67
  input: @input,
69
- code:
68
+ code: code
70
69
  )
71
70
  end
72
71
  end
@@ -17,7 +17,7 @@ module Servactory
17
17
  def self.check(context:, input:, value:, **)
18
18
  return unless should_be_checked_for?(input)
19
19
 
20
- new(context:, input:, value:).check
20
+ new(context: context, input: input, value: value).check
21
21
  end
22
22
 
23
23
  def self.should_be_checked_for?(input)
@@ -6,7 +6,15 @@ module Servactory
6
6
  class Type < Base
7
7
  DEFAULT_MESSAGE = lambda do |service_class_name:, input:, expected_type:, given_type:|
8
8
  if input.array?
9
- "[#{service_class_name}] Wrong type in input array `#{input.name}`, expected `#{expected_type}`"
9
+ array_message = input.array[:message]
10
+
11
+ if array_message.is_a?(Proc)
12
+ array_message.call(input: input, expected_type: expected_type)
13
+ elsif array_message.is_a?(String) && array_message.present?
14
+ array_message
15
+ else
16
+ "[#{service_class_name}] Wrong type in input array `#{input.name}`, expected `#{expected_type}`"
17
+ end
10
18
  else
11
19
  "[#{service_class_name}] Wrong type of input `#{input.name}`, " \
12
20
  "expected `#{expected_type}`, " \
@@ -19,7 +27,7 @@ module Servactory
19
27
  def self.check(context:, input:, value:, check_key:, check_options:)
20
28
  return unless should_be_checked_for?(input, value, check_key)
21
29
 
22
- new(context:, input:, value:, types: check_options).check
30
+ new(context: context, input: input, value: value, types: check_options).check
23
31
  end
24
32
 
25
33
  def self.should_be_checked_for?(input, value, check_key)
@@ -3,6 +3,8 @@
3
3
  module Servactory
4
4
  module InputArguments
5
5
  class InputArgument
6
+ ARRAY_DEFAULT_VALUE = ->(is: false, message: nil) { { is: is, message: message } }
7
+
6
8
  attr_reader :name,
7
9
  :types,
8
10
  :inclusion,
@@ -18,7 +20,7 @@ module Servactory
18
20
 
19
21
  @inclusion = options.fetch(:inclusion, nil)
20
22
  @must = options.fetch(:must, nil)
21
- @array = options.fetch(:array, false)
23
+ @array = prepare_advanced_for(options.fetch(:array, ARRAY_DEFAULT_VALUE.call))
22
24
  @required = options.fetch(:required, true)
23
25
  @internal = options.fetch(:internal, false)
24
26
  @default = options.fetch(:default, nil)
@@ -26,15 +28,26 @@ module Servactory
26
28
 
27
29
  def options_for_checks
28
30
  {
29
- types:,
30
- inclusion:,
31
- must:,
32
- required:,
33
- # internal:,
34
- default:
31
+ types: types,
32
+ inclusion: inclusion,
33
+ must: must,
34
+ required: required,
35
+ # internal: internal,
36
+ default: default
35
37
  }
36
38
  end
37
39
 
40
+ def prepare_advanced_for(value)
41
+ if value.is_a?(Hash)
42
+ ARRAY_DEFAULT_VALUE.call(
43
+ is: value.fetch(:is, false),
44
+ message: value.fetch(:message, nil)
45
+ )
46
+ else
47
+ ARRAY_DEFAULT_VALUE.call(is: value)
48
+ end
49
+ end
50
+
38
51
  def conflict_code
39
52
  return :required_vs_default if required? && default_value_present?
40
53
  return :array_vs_array if array? && types.include?(Array)
@@ -52,7 +65,7 @@ module Servactory
52
65
  end
53
66
 
54
67
  def array?
55
- Servactory::Utils.boolean?(array)
68
+ Servactory::Utils.boolean?(array[:is])
56
69
  end
57
70
 
58
71
  def required?
@@ -28,13 +28,18 @@ module Servactory
28
28
 
29
29
  def process_input(input)
30
30
  input.options_for_checks.each do |check_key, check_options|
31
- process_option(check_key, check_options, input:)
31
+ process_option(check_key, check_options, input: input)
32
32
  end
33
33
  end
34
34
 
35
35
  def process_option(check_key, check_options, input:)
36
36
  check_classes.each do |check_class|
37
- errors_from_checks = process_check_class(check_class, input:, check_key:, check_options:)
37
+ errors_from_checks = process_check_class(
38
+ check_class,
39
+ input: input,
40
+ check_key: check_key,
41
+ check_options: check_options
42
+ )
38
43
 
39
44
  @errors.push(*errors_from_checks)
40
45
  end
@@ -43,10 +48,10 @@ module Servactory
43
48
  def process_check_class(check_class, input:, check_key:, check_options:)
44
49
  check_class.check(
45
50
  context: @context,
46
- input:,
51
+ input: input,
47
52
  value: @incoming_arguments.fetch(input.name, nil),
48
- check_key:,
49
- check_options:
53
+ check_key: check_key,
54
+ check_options: check_options
50
55
  )
51
56
  end
52
57
 
@@ -43,7 +43,7 @@ module Servactory
43
43
 
44
44
  @context.assign_inputs(Servactory::Inputs.new(**@inputs_variables))
45
45
 
46
- @context.class.class_eval(context_internal_variables_template)
46
+ @context.class.class_eval(context_internal_variables_template) if context_internal_variables_template.present?
47
47
 
48
48
  @internal_variables.each do |input_name, input_value|
49
49
  @context.instance_variable_set(:"@#{input_name}", input_value)
@@ -79,8 +79,12 @@ module Servactory
79
79
  # private attr_reader(*[:attr_1]);
80
80
  #
81
81
  def context_internal_variables_template
82
- <<-RUBY.squish
83
- private attr_reader(*#{@internal_variables.keys});
82
+ return if @internal_variables.blank?
83
+
84
+ @context_internal_variables_template ||= <<-RUBY.squish
85
+ private;
86
+
87
+ attr_reader(*#{@internal_variables.keys});
84
88
  RUBY
85
89
  end
86
90
  end
@@ -16,8 +16,8 @@ module Servactory
16
16
 
17
17
  def options_for_checks
18
18
  {
19
- types:,
20
- required:
19
+ types: types,
20
+ required: required
21
21
  }
22
22
  end
23
23
 
@@ -42,14 +42,16 @@ module Servactory
42
42
  define_method(:#{internal_argument.name}=) do |value|;
43
43
  Servactory::InternalArguments::Checks::Type.check!(
44
44
  context: self,
45
- internal_argument:,
46
- value:
45
+ internal_argument: internal_argument,
46
+ value: value
47
47
  );
48
48
 
49
49
  instance_variable_set(:@#{internal_argument.name}, value);
50
50
  end;
51
51
 
52
- private attr_reader :#{internal_argument.name};
52
+ private;
53
+
54
+ attr_reader :#{internal_argument.name};
53
55
  RUBY
54
56
  end
55
57
  end
@@ -18,9 +18,9 @@ module Servactory
18
18
 
19
19
  def options_for_checks
20
20
  {
21
- types:,
22
- required:,
23
- default:
21
+ types: types,
22
+ required: required,
23
+ default: default
24
24
  }
25
25
  end
26
26
 
@@ -35,21 +35,25 @@ module Servactory
35
35
  # instance_variable_set(:@user, value);
36
36
  # end;
37
37
  #
38
- # private attr_reader :user;
38
+ # private;
39
+ #
40
+ # attr_reader :user;
39
41
  #
40
42
  def context_output_argument_template_for(output_argument)
41
43
  <<-RUBY.squish
42
44
  define_method(:#{output_argument.name}=) do |value|;
43
45
  Servactory::OutputArguments::Checks::Type.check!(
44
46
  context: self,
45
- output_argument:,
46
- value:
47
+ output_argument: output_argument,
48
+ value: value
47
49
  );
48
50
 
49
51
  instance_variable_set(:@#{output_argument.name}, value);
50
52
  end;
51
53
 
52
- private attr_reader :#{output_argument.name};
54
+ private;
55
+
56
+ attr_reader :#{output_argument.name};
53
57
  RUBY
54
58
  end
55
59
  end
@@ -12,10 +12,10 @@ module Servactory
12
12
 
13
13
  attr_reader :stage_handyman
14
14
 
15
- def stage(&)
15
+ def stage(&block)
16
16
  @stage_factory ||= Factory.new
17
17
 
18
- @stage_factory.instance_eval(&)
18
+ @stage_factory.instance_eval(&block)
19
19
 
20
20
  @stage_handyman = Handyman.work_in(@stage_factory)
21
21
 
@@ -3,7 +3,7 @@
3
3
  module Servactory
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 1
6
+ MINOR = 3
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
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.1.0
4
+ version: 1.3.0
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-05 00:00:00.000000000 Z
11
+ date: 2023-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -223,7 +223,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
223
223
  requirements:
224
224
  - - ">="
225
225
  - !ruby/object:Gem::Version
226
- version: 3.2.0
226
+ version: 2.7.0
227
227
  required_rubygems_version: !ruby/object:Gem::Requirement
228
228
  requirements:
229
229
  - - ">="