servactory 1.4.4 → 1.4.5

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: 54a713971d52bebd38a97f473eb942be19f5c4029604aa26f0729cb1dc40f24a
4
- data.tar.gz: 1b368e874cdf3c502328fe3dbf71758a3c0821140c1b0e9a595f6622b41ec1bc
3
+ metadata.gz: 8878b890cc7b43a45e8ea3e982a27e5873ec4d1aeff1ac91f6ce9ea77cc98e38
4
+ data.tar.gz: 4f2947b852d2f0e53965b79b88ce013ae442575dee4f6539b1a2684e242211a5
5
5
  SHA512:
6
- metadata.gz: c2e412f4e06a1a21aa70a43a0f898f6d5fc0adcca1eaf7922edb80aa719313b9c2325d83ef41e34eda9c8cacc988e60b076938e23b1ff1e709d50aee9ce0f0b1
7
- data.tar.gz: d871cef64e8249b89572f6b97e9fdd9410632d20a393ea77d59f36481acc7f0207bfa26e6c79749a2031962927b965955986f257ce298d554d8289f2f85bfaf4
6
+ metadata.gz: 437c627bef5b24d7f5a5e7b3f4c3b75e207af58d37900d6a8c6cf8b73c69e6704feb575f01233362f1ce2c40f7c640d2900d3d58e416ea12ed682c9f23fd058b
7
+ data.tar.gz: fc9a5f1a97cf8db5483df7be46f805a6c8f5598b559237a89c2409ee761d61204b79da375d3d0228133b88f4e60fe124c22519c6a314d39bb33dbf585cd5422a
data/README.md CHANGED
@@ -14,10 +14,13 @@ A set of tools for building reliable services of any complexity.
14
14
  - [Preparation](#preparation)
15
15
  - [Usage](#usage)
16
16
  - [Minimal example](#minimal-example)
17
+ - [Call](#call)
18
+ - [Result](#result)
17
19
  - [Input attributes](#input-attributes)
18
20
  - [Isolated usage](#isolated-usage)
19
21
  - [As an internal argument](#isolated-usage)
20
22
  - [Optional inputs](#optional-inputs)
23
+ - [As (internal name)](#as-internal-name)
21
24
  - [An array of specific values](#an-array-of-specific-values)
22
25
  - [Inclusion](#inclusion)
23
26
  - [Must](#must)
@@ -25,7 +28,6 @@ A set of tools for building reliable services of any complexity.
25
28
  - [Internal attributes](#internal-attributes)
26
29
  - [Stage](#stage)
27
30
  - [Failures](#failures)
28
- - [Result](#result)
29
31
  - [Testing](#testing)
30
32
  - [Thanks](#thanks)
31
33
  - [Contributing](#contributing)
@@ -111,6 +113,47 @@ end
111
113
 
112
114
  [More examples](https://github.com/afuno/servactory/tree/main/examples/usual)
113
115
 
116
+ ### Call
117
+
118
+ Services can only be called via `.call` and `.call!` methods.
119
+
120
+ The `.call` method will only fail if it catches an exception in the input arguments.
121
+ Internal and output attributes, as well as methods for failures - all this will be collected in the result.
122
+
123
+ The `.call!` method will fail if it catches any exception.
124
+
125
+ #### Via .call
126
+
127
+ ```ruby
128
+ UsersService::Accept.call(user: User.first)
129
+ ```
130
+
131
+ #### Via .call!
132
+
133
+ ```ruby
134
+ UsersService::Accept.call!(user: User.first)
135
+ ```
136
+
137
+ ### Result
138
+
139
+ All services have the result of their work. For example, in case of success this call:
140
+
141
+ ```ruby
142
+ service_result = UsersService::Accept.call!(user: User.first)
143
+ ```
144
+
145
+ Will return this:
146
+
147
+ ```ruby
148
+ #<Servactory::Result:0x0000000107ad9e88 @user="...">
149
+ ```
150
+
151
+ And then you can work with this result, for example, in this way:
152
+
153
+ ```ruby
154
+ Notification::SendJob.perform_later(service_result.user.id)
155
+ ```
156
+
114
157
  ### Input attributes
115
158
 
116
159
  #### Isolated usage
@@ -163,7 +206,7 @@ class UsersService::Create < ApplicationService::Base
163
206
  end
164
207
  ```
165
208
 
166
- #### As
209
+ #### As (internal name)
167
210
 
168
211
  This option changes the name of the input within the service.
169
212
 
@@ -346,26 +389,6 @@ def check!
346
389
  end
347
390
  ```
348
391
 
349
- ### Result
350
-
351
- All services have the result of their work. For example, in case of success this call:
352
-
353
- ```ruby
354
- service_result = NotificationService::Create.call!(user: User.first)
355
- ```
356
-
357
- Will return this:
358
-
359
- ```ruby
360
- #<Servactory::Result:0x0000000112c00748 @notification=...>
361
- ```
362
-
363
- And then you can work with this result, for example, in this way:
364
-
365
- ```ruby
366
- Notification::SendJob.perform_later(service_result.notification.id)
367
- ```
368
-
369
392
  ## Testing
370
393
 
371
394
  Testing Servactory services is the same as testing regular Ruby classes.
@@ -28,6 +28,29 @@ module Servactory
28
28
  )
29
29
  end
30
30
 
31
+ def call(arguments = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
32
+ @context_store = Store.new(self)
33
+
34
+ assign_data_with(arguments)
35
+
36
+ input_arguments_workbench.find_unnecessary!
37
+ input_arguments_workbench.check_rules!
38
+ output_arguments_workbench.find_conflicts_in!(
39
+ collection_of_internal_arguments: collection_of_internal_arguments
40
+ )
41
+
42
+ prepare_data
43
+
44
+ input_arguments_workbench.check!
45
+
46
+ stage_handyman.run_methods!
47
+
48
+ Servactory::Result.prepare_for(
49
+ context: context_store.context,
50
+ collection_of_output_arguments: collection_of_output_arguments
51
+ )
52
+ end
53
+
31
54
  private
32
55
 
33
56
  attr_reader :context_store
@@ -6,7 +6,7 @@ module Servactory
6
6
  class Errors
7
7
  # NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
8
8
  extend Forwardable
9
- def_delegators :@collection, :<<, :filter, :reject, :empty?, :first
9
+ def_delegators :@collection, :<<, :to_a, :filter, :reject, :empty?, :first
10
10
 
11
11
  def initialize(collection = Set.new)
12
12
  @collection = collection
@@ -9,7 +9,7 @@ module Servactory
9
9
  def add_error(message, **arguments)
10
10
  message = message.call(**arguments) if message.is_a?(Proc)
11
11
 
12
- errors.add(message)
12
+ errors << message
13
13
  end
14
14
 
15
15
  private
@@ -6,7 +6,7 @@ module Servactory
6
6
  class Errors
7
7
  # NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
8
8
  extend Forwardable
9
- def_delegators :@collection, :add, :to_a
9
+ def_delegators :@collection, :<<, :to_a
10
10
 
11
11
  def initialize(*)
12
12
  @collection = Set.new
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module InputArguments
5
+ class DefineInputConflict
6
+ attr_reader :content
7
+
8
+ def initialize(content:)
9
+ @content = content
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module InputArguments
5
+ class DefineInputMethod
6
+ attr_reader :name,
7
+ :content
8
+
9
+ def initialize(name:, content:)
10
+ @name = name.to_sym
11
+ @content = content
12
+ end
13
+ end
14
+ end
15
+ end
@@ -55,22 +55,19 @@ module Servactory
55
55
  name: :required,
56
56
  input: self,
57
57
  check_class: Servactory::InputArguments::Checks::Required,
58
- define_input_methods: lambda do
59
- <<-RUBY
60
- def required?
61
- Servactory::Utils.boolean?(required[:is])
62
- end
63
-
64
- def optional?
65
- !required?
66
- end
67
- RUBY
68
- end,
69
- define_conflicts: lambda do
70
- <<-RUBY
71
- return :required_vs_default if required? && default_value_present?
72
- RUBY
73
- end,
58
+ define_input_methods: [
59
+ DefineInputMethod.new(
60
+ name: :required?,
61
+ content: ->(value:) { Servactory::Utils.boolean?(value[:is]) }
62
+ ),
63
+ DefineInputMethod.new(
64
+ name: :optional?,
65
+ content: ->(value:) { !Servactory::Utils.boolean?(value[:is]) }
66
+ )
67
+ ],
68
+ define_input_conflicts: [
69
+ DefineInputConflict.new(content: -> { return :required_vs_default if required? && default_value_present? })
70
+ ],
74
71
  need_for_checks: true,
75
72
  value_key: :is,
76
73
  value_fallback: true,
@@ -95,13 +92,12 @@ module Servactory
95
92
  name: :default,
96
93
  input: self,
97
94
  check_class: Servactory::InputArguments::Checks::Type,
98
- define_input_methods: lambda do
99
- <<-RUBY
100
- def default_value_present?
101
- !default.nil?
102
- end
103
- RUBY
104
- end,
95
+ define_input_methods: [
96
+ DefineInputMethod.new(
97
+ name: :default_value_present?,
98
+ content: ->(value:) { !value.nil? }
99
+ )
100
+ ],
105
101
  need_for_checks: true,
106
102
  value_fallback: nil,
107
103
  with_advanced_mode: false,
@@ -114,19 +110,16 @@ module Servactory
114
110
  name: :array,
115
111
  input: self,
116
112
  check_class: Servactory::InputArguments::Checks::Type,
117
- define_input_methods: lambda do
118
- <<-RUBY
119
- def array?
120
- Servactory::Utils.boolean?(array[:is])
121
- end
122
- RUBY
123
- end,
124
- define_conflicts: lambda do
125
- <<-RUBY
126
- return :array_vs_array if array? && types.include?(Array)
127
- return :array_vs_inclusion if array? && inclusion_present?
128
- RUBY
129
- end,
113
+ define_input_methods: [
114
+ DefineInputMethod.new(
115
+ name: :array?,
116
+ content: ->(value:) { Servactory::Utils.boolean?(value[:is]) }
117
+ )
118
+ ],
119
+ define_input_conflicts: [
120
+ DefineInputConflict.new(content: -> { return :array_vs_array if array? && types.include?(Array) }),
121
+ DefineInputConflict.new(content: -> { return :array_vs_inclusion if array? && inclusion_present? })
122
+ ],
130
123
  need_for_checks: false,
131
124
  value_key: :is,
132
125
  value_fallback: false,
@@ -139,13 +132,12 @@ module Servactory
139
132
  name: :inclusion,
140
133
  input: self,
141
134
  check_class: Servactory::InputArguments::Checks::Inclusion,
142
- define_input_methods: lambda do
143
- <<-RUBY
144
- def inclusion_present?
145
- inclusion[:in].is_a?(Array) && inclusion[:in].present?
146
- end
147
- RUBY
148
- end,
135
+ define_input_methods: [
136
+ DefineInputMethod.new(
137
+ name: :inclusion_present?,
138
+ content: ->(value:) { value[:in].is_a?(Array) && value[:in].present? }
139
+ )
140
+ ],
149
141
  need_for_checks: true,
150
142
  value_key: :in,
151
143
  value_fallback: nil,
@@ -158,13 +150,12 @@ module Servactory
158
150
  name: :must,
159
151
  input: self,
160
152
  check_class: Servactory::InputArguments::Checks::Must,
161
- define_input_methods: lambda do
162
- <<-RUBY
163
- def must_present?
164
- must.present?
165
- end
166
- RUBY
167
- end,
153
+ define_input_methods: [
154
+ DefineInputMethod.new(
155
+ name: :must_present?,
156
+ content: ->(value:) { value.present? }
157
+ )
158
+ ],
168
159
  need_for_checks: true,
169
160
  value_key: :is,
170
161
  value_fallback: nil,
@@ -178,13 +169,12 @@ module Servactory
178
169
  name: :internal,
179
170
  input: self,
180
171
  check_class: nil,
181
- define_input_methods: lambda do
182
- <<-RUBY
183
- def internal?
184
- Servactory::Utils.boolean?(internal[:is])
185
- end
186
- RUBY
187
- end,
172
+ define_input_methods: [
173
+ DefineInputMethod.new(
174
+ name: :internal?,
175
+ content: ->(value:) { Servactory::Utils.boolean?(value[:is]) }
176
+ )
177
+ ],
188
178
  need_for_checks: false,
189
179
  value_key: :is,
190
180
  value_fallback: false,
@@ -197,9 +187,7 @@ module Servactory
197
187
  end
198
188
 
199
189
  def conflict_code
200
- instance_eval(collection_of_options.defined_conflicts)
201
-
202
- nil
190
+ collection_of_options.defined_conflict_code
203
191
  end
204
192
 
205
193
  def with_conflicts?
@@ -9,7 +9,8 @@ module Servactory
9
9
 
10
10
  attr_reader :name,
11
11
  :check_class,
12
- :define_conflicts,
12
+ :define_input_methods,
13
+ :define_input_conflicts,
13
14
  :need_for_checks,
14
15
  :value_key,
15
16
  :value
@@ -24,13 +25,14 @@ module Servactory
24
25
  original_value: nil,
25
26
  value_key: nil,
26
27
  define_input_methods: nil,
27
- define_conflicts: nil,
28
+ define_input_conflicts: nil,
28
29
  with_advanced_mode: true,
29
30
  **options
30
31
  ) # do
31
32
  @name = name.to_sym
32
33
  @check_class = check_class
33
- @define_conflicts = define_conflicts
34
+ @define_input_methods = define_input_methods
35
+ @define_input_conflicts = define_input_conflicts
34
36
  @need_for_checks = need_for_checks
35
37
  @value_key = value_key
36
38
 
@@ -41,7 +43,7 @@ module Servactory
41
43
  with_advanced_mode: with_advanced_mode
42
44
  )
43
45
 
44
- input.instance_eval(define_input_methods.call) if define_input_methods.present?
46
+ prepare_input_methods_for(input)
45
47
  end
46
48
  # rubocop:enable Metrics/MethodLength
47
49
 
@@ -73,6 +75,22 @@ module Servactory
73
75
  DEFAULT_VALUE.call(key: value_key, value: value)
74
76
  end
75
77
  end
78
+
79
+ def prepare_input_methods_for(input)
80
+ input.instance_eval(define_input_methods_template) if define_input_methods_template.present?
81
+ end
82
+
83
+ def define_input_methods_template
84
+ return if @define_input_methods.blank?
85
+
86
+ @define_input_methods_template ||= @define_input_methods.map do |define_input_method|
87
+ <<-RUBY
88
+ def #{define_input_method.name}
89
+ #{define_input_method.content.call(value: @value)}
90
+ end
91
+ RUBY
92
+ end.join("\n")
93
+ end
76
94
  end
77
95
  end
78
96
  end
@@ -5,7 +5,7 @@ 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, :<<, :filter, :each, :map
8
+ def_delegators :@collection, :<<, :filter, :each, :map, :flat_map
9
9
 
10
10
  def initialize(*)
11
11
  @collection = Set.new
@@ -27,8 +27,12 @@ module Servactory
27
27
  end
28
28
  end
29
29
 
30
- def defined_conflicts
31
- map { |option| option.define_conflicts&.call }.reject(&:blank?).uniq.join
30
+ def defined_conflict_code
31
+ flat_map do |option|
32
+ option.define_input_conflicts&.map do |define_input_conflict|
33
+ define_input_conflict.content.call
34
+ end
35
+ end.reject(&:blank?).first
32
36
  end
33
37
  end
34
38
  end
@@ -9,13 +9,24 @@ module Servactory
9
9
  private
10
10
 
11
11
  def prepare_for(context:, collection_of_output_arguments:)
12
+ prepare_outputs_with(context: context, collection_of_output_arguments: collection_of_output_arguments)
13
+ prepare_statuses_with(context: context)
14
+
15
+ self
16
+ end
17
+
18
+ def prepare_outputs_with(context:, collection_of_output_arguments:)
12
19
  collection_of_output_arguments.each do |output|
13
20
  self.class.attr_reader(:"#{output.name}")
14
21
 
15
22
  instance_variable_set(:"@#{output.name}", context.instance_variable_get(:"@#{output.name}"))
16
23
  end
24
+ end
17
25
 
18
- self
26
+ def prepare_statuses_with(context:)
27
+ define_singleton_method(:errors) { context.errors }
28
+ define_singleton_method(:success?) { context.errors.empty? }
29
+ define_singleton_method(:failure?) { !context.errors.empty? }
19
30
  end
20
31
  end
21
32
  end
@@ -4,7 +4,7 @@ module Servactory
4
4
  module VERSION
5
5
  MAJOR = 1
6
6
  MINOR = 4
7
- PATCH = 4
7
+ PATCH = 5
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servactory
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.4
4
+ version: 1.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
@@ -183,6 +183,8 @@ files:
183
183
  - lib/servactory/input_arguments/checks/required.rb
184
184
  - lib/servactory/input_arguments/checks/type.rb
185
185
  - lib/servactory/input_arguments/collection.rb
186
+ - lib/servactory/input_arguments/define_input_conflict.rb
187
+ - lib/servactory/input_arguments/define_input_method.rb
186
188
  - lib/servactory/input_arguments/dsl.rb
187
189
  - lib/servactory/input_arguments/input_argument.rb
188
190
  - lib/servactory/input_arguments/option.rb