servus 0.1.3 → 0.1.4

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -0
  3. data/CHANGELOG.md +7 -0
  4. data/IDEAS.md +5 -0
  5. data/READme.md +147 -42
  6. data/Rakefile +33 -0
  7. data/builds/servus-0.1.3.gem +0 -0
  8. data/builds/servus-0.1.4.gem +0 -0
  9. data/docs/core/1_overview.md +77 -0
  10. data/docs/core/2_architecture.md +92 -0
  11. data/docs/core/3_service_objects.md +121 -0
  12. data/docs/features/1_schema_validation.md +119 -0
  13. data/docs/features/2_error_handling.md +121 -0
  14. data/docs/features/3_async_execution.md +81 -0
  15. data/docs/features/4_logging.md +64 -0
  16. data/docs/guides/1_common_patterns.md +90 -0
  17. data/docs/guides/2_migration_guide.md +175 -0
  18. data/docs/integration/1_configuration.md +51 -0
  19. data/docs/integration/2_testing.md +164 -0
  20. data/docs/integration/3_rails_integration.md +99 -0
  21. data/docs/yard/Servus/Base.html +1645 -0
  22. data/docs/yard/Servus/Config.html +582 -0
  23. data/docs/yard/Servus/Extensions/Async/Call.html +400 -0
  24. data/docs/yard/Servus/Extensions/Async/Errors/AsyncError.html +140 -0
  25. data/docs/yard/Servus/Extensions/Async/Errors/JobEnqueueError.html +154 -0
  26. data/docs/yard/Servus/Extensions/Async/Errors/ServiceNotFoundError.html +154 -0
  27. data/docs/yard/Servus/Extensions/Async/Errors.html +128 -0
  28. data/docs/yard/Servus/Extensions/Async/Ext.html +119 -0
  29. data/docs/yard/Servus/Extensions/Async/Job.html +310 -0
  30. data/docs/yard/Servus/Extensions/Async.html +141 -0
  31. data/docs/yard/Servus/Extensions.html +117 -0
  32. data/docs/yard/Servus/Generators/ServiceGenerator.html +261 -0
  33. data/docs/yard/Servus/Generators.html +115 -0
  34. data/docs/yard/Servus/Helpers/ControllerHelpers.html +457 -0
  35. data/docs/yard/Servus/Helpers.html +115 -0
  36. data/docs/yard/Servus/Railtie.html +134 -0
  37. data/docs/yard/Servus/Support/Errors/AuthenticationError.html +287 -0
  38. data/docs/yard/Servus/Support/Errors/BadRequestError.html +283 -0
  39. data/docs/yard/Servus/Support/Errors/ForbiddenError.html +284 -0
  40. data/docs/yard/Servus/Support/Errors/InternalServerError.html +283 -0
  41. data/docs/yard/Servus/Support/Errors/NotFoundError.html +284 -0
  42. data/docs/yard/Servus/Support/Errors/ServiceError.html +489 -0
  43. data/docs/yard/Servus/Support/Errors/ServiceUnavailableError.html +290 -0
  44. data/docs/yard/Servus/Support/Errors/UnauthorizedError.html +200 -0
  45. data/docs/yard/Servus/Support/Errors/UnprocessableEntityError.html +288 -0
  46. data/docs/yard/Servus/Support/Errors/ValidationError.html +200 -0
  47. data/docs/yard/Servus/Support/Errors.html +140 -0
  48. data/docs/yard/Servus/Support/Logger.html +856 -0
  49. data/docs/yard/Servus/Support/Rescuer/BlockContext.html +585 -0
  50. data/docs/yard/Servus/Support/Rescuer/CallOverride.html +257 -0
  51. data/docs/yard/Servus/Support/Rescuer/ClassMethods.html +343 -0
  52. data/docs/yard/Servus/Support/Rescuer.html +267 -0
  53. data/docs/yard/Servus/Support/Response.html +574 -0
  54. data/docs/yard/Servus/Support/Validator.html +1150 -0
  55. data/docs/yard/Servus/Support.html +119 -0
  56. data/docs/yard/Servus/Testing/ExampleBuilders.html +523 -0
  57. data/docs/yard/Servus/Testing/ExampleExtractor.html +578 -0
  58. data/docs/yard/Servus/Testing.html +142 -0
  59. data/docs/yard/Servus.html +343 -0
  60. data/docs/yard/_index.html +535 -0
  61. data/docs/yard/class_list.html +54 -0
  62. data/docs/yard/css/common.css +1 -0
  63. data/docs/yard/css/full_list.css +58 -0
  64. data/docs/yard/css/style.css +503 -0
  65. data/docs/yard/file.1_common_patterns.html +154 -0
  66. data/docs/yard/file.1_configuration.html +115 -0
  67. data/docs/yard/file.1_overview.html +142 -0
  68. data/docs/yard/file.1_schema_validation.html +188 -0
  69. data/docs/yard/file.2_architecture.html +157 -0
  70. data/docs/yard/file.2_error_handling.html +190 -0
  71. data/docs/yard/file.2_migration_guide.html +242 -0
  72. data/docs/yard/file.2_testing.html +227 -0
  73. data/docs/yard/file.3_async_execution.html +145 -0
  74. data/docs/yard/file.3_rails_integration.html +160 -0
  75. data/docs/yard/file.3_service_objects.html +191 -0
  76. data/docs/yard/file.4_logging.html +135 -0
  77. data/docs/yard/file.ErrorHandling.html +190 -0
  78. data/docs/yard/file.READme.html +674 -0
  79. data/docs/yard/file.architecture.html +157 -0
  80. data/docs/yard/file.async_execution.html +145 -0
  81. data/docs/yard/file.common_patterns.html +154 -0
  82. data/docs/yard/file.configuration.html +115 -0
  83. data/docs/yard/file.error_handling.html +190 -0
  84. data/docs/yard/file.logging.html +135 -0
  85. data/docs/yard/file.migration_guide.html +242 -0
  86. data/docs/yard/file.overview.html +142 -0
  87. data/docs/yard/file.rails_integration.html +160 -0
  88. data/docs/yard/file.schema_validation.html +188 -0
  89. data/docs/yard/file.service_objects.html +191 -0
  90. data/docs/yard/file.testing.html +227 -0
  91. data/docs/yard/file_list.html +119 -0
  92. data/docs/yard/frames.html +22 -0
  93. data/docs/yard/index.html +674 -0
  94. data/docs/yard/js/app.js +344 -0
  95. data/docs/yard/js/full_list.js +242 -0
  96. data/docs/yard/js/jquery.js +4 -0
  97. data/docs/yard/method_list.html +542 -0
  98. data/docs/yard/top-level-namespace.html +110 -0
  99. data/lib/generators/servus/service/service_generator.rb +64 -1
  100. data/lib/generators/servus/service/templates/service.rb.erb +1 -1
  101. data/lib/servus/base.rb +258 -57
  102. data/lib/servus/config.rb +58 -12
  103. data/lib/servus/extensions/async/call.rb +50 -18
  104. data/lib/servus/extensions/async/errors.rb +23 -3
  105. data/lib/servus/extensions/async/ext.rb +10 -2
  106. data/lib/servus/extensions/async/job.rb +30 -9
  107. data/lib/servus/helpers/controller_helpers.rb +73 -37
  108. data/lib/servus/support/errors.rb +135 -45
  109. data/lib/servus/support/rescuer.rb +189 -36
  110. data/lib/servus/support/response.rb +49 -7
  111. data/lib/servus/support/validator.rb +120 -19
  112. data/lib/servus/testing/example_builders.rb +133 -0
  113. data/lib/servus/testing/example_extractor.rb +309 -0
  114. data/lib/servus/testing.rb +17 -0
  115. data/lib/servus/version.rb +1 -1
  116. metadata +117 -19
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servus
4
+ module Testing
5
+ # Extracts example values from JSON Schema definitions for use in testing.
6
+ #
7
+ # This class understands both OpenAPI-style `example` (singular) and
8
+ # JSON Schema-style `examples` (plural, array) keywords. It can handle
9
+ # nested objects, arrays, and complex schema structures.
10
+ #
11
+ # @example Basic extraction
12
+ # schema = {
13
+ # type: 'object',
14
+ # properties: {
15
+ # name: { type: 'string', example: 'John Doe' },
16
+ # age: { type: 'integer', example: 30 }
17
+ # }
18
+ # }
19
+ #
20
+ # extractor = ExampleExtractor.new(schema)
21
+ # extractor.extract
22
+ # # => { name: 'John Doe', age: 30 }
23
+ #
24
+ # @example With service class
25
+ # examples = ExampleExtractor.extract(MyService, :arguments)
26
+ # # => { user_id: 123, amount: 100.0 }
27
+ #
28
+ # @see https://json-schema.org/understanding-json-schema/reference/annotations
29
+ # @see https://spec.openapis.org/oas/v3.1.0#schema-object
30
+ class ExampleExtractor
31
+ # Extracts example values from a service class's schema.
32
+ #
33
+ # This is a convenience class method that loads the schema via the
34
+ # Validator and extracts examples in one call.
35
+ #
36
+ # @param service_class [Class] The service class to extract examples from
37
+ # @param schema_type [Symbol] Either :arguments or :result
38
+ # @return [Hash<Symbol, Object>] Extracted example values with symbolized keys
39
+ #
40
+ # @example Extract argument examples
41
+ # ExampleExtractor.extract(ProcessPayment::Service, :arguments)
42
+ # # => { user_id: 123, amount: 100.0, currency: 'USD' }
43
+ #
44
+ # @example Extract result examples
45
+ # ExampleExtractor.extract(ProcessPayment::Service, :result)
46
+ # # => { transaction_id: 'txn_123', status: 'approved' }
47
+ def self.extract(service_class, schema_type)
48
+ schema = load_schema(service_class, schema_type)
49
+ return {} unless schema
50
+
51
+ new(schema).extract
52
+ end
53
+
54
+ # Initializes a new ExampleExtractor with a schema.
55
+ #
56
+ # The schema is deeply symbolized on initialization to normalize all keys,
57
+ # eliminating the need for double lookups throughout extraction.
58
+ #
59
+ # @param schema [Hash, nil] A JSON Schema hash with properties and examples
60
+ #
61
+ # @example
62
+ # schema = { type: 'object', properties: { name: { example: 'Test' } } }
63
+ # extractor = ExampleExtractor.new(schema)
64
+ def initialize(schema)
65
+ @schema = deep_symbolize_keys(schema)
66
+ end
67
+
68
+ # Extracts all example values from the schema.
69
+ #
70
+ # Traverses the schema structure and collects example values from:
71
+ # - Simple properties with `example` or `examples` keywords
72
+ # - Nested objects (recursively)
73
+ # - Arrays (using array-level examples or generating from item schemas)
74
+ #
75
+ # @return [Hash<Symbol, Object>] Hash of example values with symbolized keys
76
+ #
77
+ # @example Simple properties
78
+ # schema = {
79
+ # type: 'object',
80
+ # properties: {
81
+ # name: { type: 'string', example: 'John' },
82
+ # age: { type: 'integer', examples: [30, 25, 40] }
83
+ # }
84
+ # }
85
+ # extractor = ExampleExtractor.new(schema)
86
+ # extractor.extract
87
+ # # => { name: 'John', age: 30 }
88
+ #
89
+ # @example Nested objects
90
+ # schema = {
91
+ # type: 'object',
92
+ # properties: {
93
+ # user: {
94
+ # type: 'object',
95
+ # properties: {
96
+ # id: { type: 'integer', example: 123 },
97
+ # name: { type: 'string', example: 'Jane' }
98
+ # }
99
+ # }
100
+ # }
101
+ # }
102
+ # extractor = ExampleExtractor.new(schema)
103
+ # extractor.extract
104
+ # # => { user: { id: 123, name: 'Jane' } }
105
+ def extract
106
+ return {} unless @schema.is_a?(Hash)
107
+
108
+ extract_examples_from_properties(@schema)
109
+ end
110
+
111
+ private
112
+
113
+ # Extracts examples from schema properties.
114
+ #
115
+ # Iterates through the properties hash and extracts example values
116
+ # for each property that has one defined.
117
+ #
118
+ # @param schema [Hash] Schema hash containing a :properties key
119
+ # @return [Hash<Symbol, Object>] Extracted examples with symbolized keys
120
+ #
121
+ # @api private
122
+ def extract_examples_from_properties(schema)
123
+ properties = schema[:properties]
124
+ return {} unless properties
125
+
126
+ properties.each_with_object({}) do |(key, property_schema), examples|
127
+ example_value = extract_example_value(property_schema)
128
+ examples[key.to_sym] = example_value unless example_value.nil? && !explicit_nil_example?(property_schema)
129
+ end
130
+ end
131
+
132
+ # Extracts a single example value from a property schema.
133
+ #
134
+ # Handles different types of properties:
135
+ # - Simple types with `example` or `examples` keywords
136
+ # - Nested objects (recursively extracts)
137
+ # - Arrays (uses array example or generates from items)
138
+ #
139
+ # @param property_schema [Hash] The schema for a single property
140
+ # @return [Object, nil] The example value, or nil if none found
141
+ #
142
+ # @api private
143
+ def extract_example_value(property_schema)
144
+ return nil unless property_schema.is_a?(Hash)
145
+
146
+ # Check for direct example keywords first
147
+ return get_example_from_keyword(property_schema) if example_keyword?(property_schema)
148
+
149
+ # Handle nested objects
150
+ return extract_examples_from_properties(property_schema) if nested_object?(property_schema)
151
+
152
+ # Handle arrays
153
+ return extract_array_example(property_schema) if array_type?(property_schema)
154
+
155
+ nil
156
+ end
157
+
158
+ # Checks if property has an example keyword (example or examples).
159
+ #
160
+ # @param property_schema [Hash] Property schema to check
161
+ # @return [Boolean] True if example or examples keyword exists
162
+ #
163
+ # @api private
164
+ def example_keyword?(property_schema)
165
+ property_schema.key?(:example) || property_schema.key?(:examples)
166
+ end
167
+
168
+ # Checks if property explicitly sets example to nil.
169
+ #
170
+ # This is important to distinguish between "no example" and "example is nil".
171
+ #
172
+ # @param property_schema [Hash] Property schema to check
173
+ # @return [Boolean] True if example is explicitly set to nil
174
+ #
175
+ # @api private
176
+ def explicit_nil_example?(property_schema)
177
+ property_schema.key?(:example) && property_schema[:example].nil?
178
+ end
179
+
180
+ # Gets the example value from the example/examples keyword.
181
+ #
182
+ # Handles both:
183
+ # - `:example` (singular): returns the value directly
184
+ # - `:examples` (plural): returns a value from the array
185
+ #
186
+ # @param property_schema [Hash] Property schema with example keyword
187
+ # @return [Object] The example value
188
+ #
189
+ # @api private
190
+ def get_example_from_keyword(property_schema)
191
+ # Check for :example (singular) first - OpenAPI style
192
+ return property_schema[:example] if property_schema.key?(:example)
193
+
194
+ # Check for :examples (plural) - JSON Schema style
195
+ examples = property_schema[:examples]
196
+ return nil unless examples.is_a?(Array) && examples.any?
197
+
198
+ examples.sample
199
+ end
200
+
201
+ # Checks if property is a nested object type.
202
+ #
203
+ # @param property_schema [Hash] Property schema to check
204
+ # @return [Boolean] True if type is object and has properties
205
+ #
206
+ # @api private
207
+ def nested_object?(property_schema)
208
+ property_schema[:type] == 'object' && property_schema[:properties]
209
+ end
210
+
211
+ # Checks if property is an array type.
212
+ #
213
+ # @param property_schema [Hash] Property schema to check
214
+ # @return [Boolean] True if type is array
215
+ #
216
+ # @api private
217
+ def array_type?(property_schema)
218
+ property_schema[:type] == 'array'
219
+ end
220
+
221
+ # Extracts example value for an array property.
222
+ #
223
+ # Handles two strategies:
224
+ # 1. If array has direct `example` keyword, use it
225
+ # 2. Otherwise, generate array with one item using item schema examples
226
+ #
227
+ # @param property_schema [Hash] Array property schema
228
+ # @return [Array, nil] Array example or nil if can't be generated
229
+ #
230
+ # @example Array with direct example
231
+ # { type: 'array', example: [1, 2, 3] }
232
+ # # => [1, 2, 3]
233
+ #
234
+ # @example Array with item schema examples
235
+ # {
236
+ # type: 'array',
237
+ # items: {
238
+ # type: 'object',
239
+ # properties: {
240
+ # id: { type: 'integer', examples: [1, 2] },
241
+ # name: { type: 'string', examples: ['John', 'Jane'] }
242
+ # }
243
+ # }
244
+ # }
245
+ # # => [{ id: 1, name: 'John' }]
246
+ #
247
+ # @api private
248
+ def extract_array_example(property_schema)
249
+ # If array has direct example, use it
250
+ return get_example_from_keyword(property_schema) if example_keyword?(property_schema)
251
+
252
+ # Otherwise, try to generate an array with one item from the items schema
253
+ items_schema = property_schema[:items]
254
+ return nil unless items_schema
255
+
256
+ # Generate one example item from the items schema
257
+ if nested_object?(items_schema)
258
+ item_example = extract_examples_from_properties(items_schema)
259
+ return [item_example] if item_example.any?
260
+ elsif example_keyword?(items_schema)
261
+ return [get_example_from_keyword(items_schema)]
262
+ end
263
+
264
+ nil
265
+ end
266
+
267
+ # Recursively converts all hash keys to symbols.
268
+ #
269
+ # Handles nested hashes and arrays of hashes, ensuring consistent
270
+ # key types throughout the structure.
271
+ #
272
+ # @param value [Object] The value to process
273
+ # @return [Object] The value with all hash keys symbolized
274
+ #
275
+ # @api private
276
+ def deep_symbolize_keys(value)
277
+ case value
278
+ when Hash
279
+ value.each_with_object({}) do |(key, val), result|
280
+ result[key.to_sym] = deep_symbolize_keys(val)
281
+ end
282
+ when Array
283
+ value.map { |item| deep_symbolize_keys(item) }
284
+ else
285
+ value
286
+ end
287
+ end
288
+
289
+ # Loads schema from service class using Validator.
290
+ #
291
+ # Reuses the existing Validator schema loading logic which handles:
292
+ # - DSL-defined schemas
293
+ # - Constant-defined schemas
294
+ # - File-based schemas
295
+ # - Schema caching
296
+ #
297
+ # @param service_class [Class] The service class
298
+ # @param schema_type [Symbol] Either :arguments or :result
299
+ # @return [Hash, nil] The loaded schema or nil
300
+ #
301
+ # @api private
302
+ def self.load_schema(service_class, schema_type)
303
+ Servus::Support::Validator.load_schema(service_class, schema_type.to_s)
304
+ end
305
+
306
+ private_class_method :load_schema
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servus
4
+ # Testing utilities for Servus services.
5
+ #
6
+ # This module provides helpers for extracting example values from JSON schemas
7
+ # to use in tests, making it easier to create test fixtures without manually
8
+ # maintaining separate factory files.
9
+ #
10
+ # @see Servus::Testing::ExampleBuilders
11
+ # @see Servus::Testing::ExampleExtractor
12
+ module Testing
13
+ end
14
+ end
15
+
16
+ require_relative 'testing/example_extractor'
17
+ require_relative 'testing/example_builders'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Servus
4
- VERSION = '0.1.3'
4
+ VERSION = '0.1.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Scholl
@@ -13,59 +13,61 @@ dependencies:
13
13
  name: active_model_serializers
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - ">="
16
+ - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: 0.10.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - ">="
23
+ - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0'
25
+ version: 0.10.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activesupport
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - ">="
30
+ - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: '8.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: '8.0'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: json-schema
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: '5'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: '5'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: actionpack
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ">="
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '0'
60
+ version: '8.0'
61
61
  type: :development
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ">="
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '0'
68
- description: A gem for managing service objects.
67
+ version: '8.0'
68
+ description: Servus is a Ruby gem that provides a structured way to create and manage
69
+ service objects, promoting clean code architecture and separation of concerns in
70
+ your applications.
69
71
  email:
70
72
  - sebscholl@gmail.com
71
73
  executables: []
@@ -74,13 +76,107 @@ extra_rdoc_files: []
74
76
  files:
75
77
  - ".rspec"
76
78
  - ".rubocop.yml"
79
+ - ".yardopts"
77
80
  - CHANGELOG.md
81
+ - IDEAS.md
78
82
  - LICENSE.txt
79
83
  - READme.md
80
84
  - Rakefile
81
85
  - builds/servus-0.0.1.gem
82
86
  - builds/servus-0.1.1.gem
83
87
  - builds/servus-0.1.2.gem
88
+ - builds/servus-0.1.3.gem
89
+ - builds/servus-0.1.4.gem
90
+ - docs/core/1_overview.md
91
+ - docs/core/2_architecture.md
92
+ - docs/core/3_service_objects.md
93
+ - docs/features/1_schema_validation.md
94
+ - docs/features/2_error_handling.md
95
+ - docs/features/3_async_execution.md
96
+ - docs/features/4_logging.md
97
+ - docs/guides/1_common_patterns.md
98
+ - docs/guides/2_migration_guide.md
99
+ - docs/integration/1_configuration.md
100
+ - docs/integration/2_testing.md
101
+ - docs/integration/3_rails_integration.md
102
+ - docs/yard/Servus.html
103
+ - docs/yard/Servus/Base.html
104
+ - docs/yard/Servus/Config.html
105
+ - docs/yard/Servus/Extensions.html
106
+ - docs/yard/Servus/Extensions/Async.html
107
+ - docs/yard/Servus/Extensions/Async/Call.html
108
+ - docs/yard/Servus/Extensions/Async/Errors.html
109
+ - docs/yard/Servus/Extensions/Async/Errors/AsyncError.html
110
+ - docs/yard/Servus/Extensions/Async/Errors/JobEnqueueError.html
111
+ - docs/yard/Servus/Extensions/Async/Errors/ServiceNotFoundError.html
112
+ - docs/yard/Servus/Extensions/Async/Ext.html
113
+ - docs/yard/Servus/Extensions/Async/Job.html
114
+ - docs/yard/Servus/Generators.html
115
+ - docs/yard/Servus/Generators/ServiceGenerator.html
116
+ - docs/yard/Servus/Helpers.html
117
+ - docs/yard/Servus/Helpers/ControllerHelpers.html
118
+ - docs/yard/Servus/Railtie.html
119
+ - docs/yard/Servus/Support.html
120
+ - docs/yard/Servus/Support/Errors.html
121
+ - docs/yard/Servus/Support/Errors/AuthenticationError.html
122
+ - docs/yard/Servus/Support/Errors/BadRequestError.html
123
+ - docs/yard/Servus/Support/Errors/ForbiddenError.html
124
+ - docs/yard/Servus/Support/Errors/InternalServerError.html
125
+ - docs/yard/Servus/Support/Errors/NotFoundError.html
126
+ - docs/yard/Servus/Support/Errors/ServiceError.html
127
+ - docs/yard/Servus/Support/Errors/ServiceUnavailableError.html
128
+ - docs/yard/Servus/Support/Errors/UnauthorizedError.html
129
+ - docs/yard/Servus/Support/Errors/UnprocessableEntityError.html
130
+ - docs/yard/Servus/Support/Errors/ValidationError.html
131
+ - docs/yard/Servus/Support/Logger.html
132
+ - docs/yard/Servus/Support/Rescuer.html
133
+ - docs/yard/Servus/Support/Rescuer/BlockContext.html
134
+ - docs/yard/Servus/Support/Rescuer/CallOverride.html
135
+ - docs/yard/Servus/Support/Rescuer/ClassMethods.html
136
+ - docs/yard/Servus/Support/Response.html
137
+ - docs/yard/Servus/Support/Validator.html
138
+ - docs/yard/Servus/Testing.html
139
+ - docs/yard/Servus/Testing/ExampleBuilders.html
140
+ - docs/yard/Servus/Testing/ExampleExtractor.html
141
+ - docs/yard/_index.html
142
+ - docs/yard/class_list.html
143
+ - docs/yard/css/common.css
144
+ - docs/yard/css/full_list.css
145
+ - docs/yard/css/style.css
146
+ - docs/yard/file.1_common_patterns.html
147
+ - docs/yard/file.1_configuration.html
148
+ - docs/yard/file.1_overview.html
149
+ - docs/yard/file.1_schema_validation.html
150
+ - docs/yard/file.2_architecture.html
151
+ - docs/yard/file.2_error_handling.html
152
+ - docs/yard/file.2_migration_guide.html
153
+ - docs/yard/file.2_testing.html
154
+ - docs/yard/file.3_async_execution.html
155
+ - docs/yard/file.3_rails_integration.html
156
+ - docs/yard/file.3_service_objects.html
157
+ - docs/yard/file.4_logging.html
158
+ - docs/yard/file.ErrorHandling.html
159
+ - docs/yard/file.READme.html
160
+ - docs/yard/file.architecture.html
161
+ - docs/yard/file.async_execution.html
162
+ - docs/yard/file.common_patterns.html
163
+ - docs/yard/file.configuration.html
164
+ - docs/yard/file.error_handling.html
165
+ - docs/yard/file.logging.html
166
+ - docs/yard/file.migration_guide.html
167
+ - docs/yard/file.overview.html
168
+ - docs/yard/file.rails_integration.html
169
+ - docs/yard/file.schema_validation.html
170
+ - docs/yard/file.service_objects.html
171
+ - docs/yard/file.testing.html
172
+ - docs/yard/file_list.html
173
+ - docs/yard/frames.html
174
+ - docs/yard/index.html
175
+ - docs/yard/js/app.js
176
+ - docs/yard/js/full_list.js
177
+ - docs/yard/js/jquery.js
178
+ - docs/yard/method_list.html
179
+ - docs/yard/top-level-namespace.html
84
180
  - lib/generators/servus/service/service_generator.rb
85
181
  - lib/generators/servus/service/templates/arguments.json.erb
86
182
  - lib/generators/servus/service/templates/result.json.erb
@@ -100,6 +196,9 @@ files:
100
196
  - lib/servus/support/rescuer.rb
101
197
  - lib/servus/support/response.rb
102
198
  - lib/servus/support/validator.rb
199
+ - lib/servus/testing.rb
200
+ - lib/servus/testing/example_builders.rb
201
+ - lib/servus/testing/example_extractor.rb
103
202
  - lib/servus/version.rb
104
203
  - sig/servus.rbs
105
204
  homepage: https://github.com/zarpay/servus
@@ -107,7 +206,6 @@ licenses:
107
206
  - MIT
108
207
  metadata:
109
208
  allowed_push_host: https://rubygems.org
110
- homepage_uri: https://github.com/zarpay/servus
111
209
  source_code_uri: https://github.com/zarpay/servus
112
210
  changelog_uri: https://github.com/zarpay/servus/blob/main/CHANGELOG.md
113
211
  rdoc_options: []