servactory 3.1.0.rc2 → 3.1.0.rc4

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -0
  3. data/README.md +7 -2
  4. data/config/locales/ja.yml +134 -0
  5. data/lib/generators/README.md +2 -2
  6. data/lib/generators/servactory/extension/USAGE +1 -1
  7. data/lib/generators/servactory/install/USAGE +2 -2
  8. data/lib/generators/servactory/rspec/USAGE +4 -4
  9. data/lib/generators/servactory/rspec/rspec_generator.rb +2 -2
  10. data/lib/generators/servactory/service/USAGE +5 -5
  11. data/lib/servactory/actions/collection.rb +21 -5
  12. data/lib/servactory/actions/stages/collection.rb +19 -1
  13. data/lib/servactory/actions/tools/rules.rb +1 -1
  14. data/lib/servactory/actions/tools/runner.rb +1 -1
  15. data/lib/servactory/configuration/collection_mode/class_names_collection.rb +1 -1
  16. data/lib/servactory/configuration/config.rb +16 -0
  17. data/lib/servactory/configuration/configurable.rb +7 -4
  18. data/lib/servactory/configuration/factory.rb +30 -3
  19. data/lib/servactory/configuration/hash_mode/class_names_collection.rb +5 -1
  20. data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +156 -29
  21. data/lib/servactory/context/warehouse/inputs.rb +1 -1
  22. data/lib/servactory/context/workspace/inputs.rb +2 -2
  23. data/lib/servactory/context/workspace/internals.rb +2 -2
  24. data/lib/servactory/context/workspace/outputs.rb +2 -2
  25. data/lib/servactory/context/workspace.rb +7 -7
  26. data/lib/servactory/info/builder.rb +4 -3
  27. data/lib/servactory/inputs/dsl.rb +5 -1
  28. data/lib/servactory/inputs/input.rb +1 -2
  29. data/lib/servactory/inputs/tools/validation.rb +10 -18
  30. data/lib/servactory/internals/dsl.rb +7 -1
  31. data/lib/servactory/maintenance/attributes/base.rb +4 -1
  32. data/lib/servactory/maintenance/attributes/collection.rb +61 -6
  33. data/lib/servactory/maintenance/options/collection.rb +71 -30
  34. data/lib/servactory/maintenance/options/option.rb +3 -12
  35. data/lib/servactory/maintenance/options/registrar.rb +3 -13
  36. data/lib/servactory/maintenance/validations/checkers/type.rb +23 -5
  37. data/lib/servactory/maintenance/validations/performer.rb +12 -23
  38. data/lib/servactory/outputs/dsl.rb +7 -1
  39. data/lib/servactory/result.rb +21 -8
  40. data/lib/servactory/test_kit/rspec/helpers/exception_validator.rb +112 -0
  41. data/lib/servactory/test_kit/rspec/helpers/input_validator.rb +3 -1
  42. data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +125 -125
  43. data/lib/servactory/test_kit/rspec/helpers/service_inputs_guard.rb +98 -0
  44. data/lib/servactory/test_kit/rspec/helpers/service_inputs_matcher.rb +75 -0
  45. data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +185 -50
  46. data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +45 -10
  47. data/lib/servactory/test_kit/rspec/helpers/wrap_block_adapter.rb +74 -0
  48. data/lib/servactory/test_kit/rspec/helpers.rb +4 -0
  49. data/lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb +72 -16
  50. data/lib/servactory/test_kit/rspec/matchers/base/message_expectation.rb +194 -0
  51. data/lib/servactory/test_kit/rspec/matchers/base/submatcher.rb +8 -0
  52. data/lib/servactory/test_kit/rspec/matchers/concerns/option_helper_rules.rb +148 -0
  53. data/lib/servactory/test_kit/rspec/matchers/concerns/proc_message.rb +117 -0
  54. data/lib/servactory/test_kit/rspec/matchers/concerns/required_message.rb +92 -0
  55. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +27 -4
  56. data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +10 -3
  57. data/lib/servactory/test_kit/rspec/matchers/submatchers/input/required_submatcher.rb +58 -23
  58. data/lib/servactory/test_kit/rspec/matchers/submatchers/input/valid_with_submatcher.rb +124 -59
  59. data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/message_submatcher.rb +133 -39
  60. data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/must_submatcher.rb +192 -17
  61. data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/target_submatcher.rb +4 -2
  62. data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/types_submatcher.rb +6 -0
  63. data/lib/servactory/test_kit/rspec/matchers.rb +15 -0
  64. data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +34 -11
  65. data/lib/servactory/tool_kit/dynamic_options/format.rb +3 -4
  66. data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +3 -3
  67. data/lib/servactory/tool_kit/dynamic_options/max.rb +3 -3
  68. data/lib/servactory/tool_kit/dynamic_options/min.rb +3 -3
  69. data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +35 -7
  70. data/lib/servactory/tool_kit/dynamic_options/must.rb +2 -3
  71. data/lib/servactory/tool_kit/dynamic_options/schema.rb +40 -25
  72. data/lib/servactory/tool_kit/dynamic_options/target.rb +4 -4
  73. data/lib/servactory/utils.rb +13 -3
  74. data/lib/servactory/version.rb +1 -1
  75. data/lib/servactory.rb +3 -4
  76. metadata +20 -4
@@ -72,7 +72,7 @@ module Servactory
72
72
  # Specify format directly as the option value:
73
73
  #
74
74
  # ```ruby
75
- # class ValidateUserService < ApplicationService::Base
75
+ # class Users::Validate < ApplicationService::Base
76
76
  # input :uuid, type: String, format: :uuid
77
77
  # input :email, type: String, format: :email
78
78
  # input :password, type: String, format: :password
@@ -179,7 +179,7 @@ module Servactory
179
179
  #
180
180
  # @param option_name [Symbol] The option name (default: :format)
181
181
  # @param formats [Hash] Custom format definitions to merge with defaults
182
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
182
+ # @return [Servactory::Maintenance::Options::Helper]
183
183
  def self.use(option_name = :format, formats: {})
184
184
  instance = new(option_name)
185
185
  instance.assign(formats)
@@ -232,7 +232,7 @@ module Servactory
232
232
  # @param internal [Object, nil] Internal attribute if applicable
233
233
  # @param output [Object, nil] Output attribute if applicable
234
234
  # @return [Boolean, Array] true if valid, or [false, reason]
235
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
235
+ # rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
236
236
  def common_condition_with(value:, option:, input: nil, internal: nil, output: nil)
237
237
  option_value = option.value&.to_sym
238
238
 
@@ -269,7 +269,6 @@ module Servactory
269
269
  # Run validator callback.
270
270
  option.properties.fetch(:validator, format_options.fetch(:validator)).call(value:)
271
271
  end
272
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
273
272
 
274
273
  ########################################################################
275
274
 
@@ -20,7 +20,7 @@ module Servactory
20
20
  # Use in your service definition:
21
21
  #
22
22
  # ```ruby
23
- # class CreateUserService < ApplicationService::Base
23
+ # class Users::Create < ApplicationService::Base
24
24
  # input :role, type: String, inclusion: { in: %w[admin user guest] }
25
25
  # input :status, type: Symbol, inclusion: { in: [:active, :inactive] }
26
26
  # input :level, type: Integer, inclusion: { in: [1, 2, 3, 4, 5] }
@@ -32,7 +32,7 @@ module Servactory
32
32
  # Specify inclusion values directly:
33
33
  #
34
34
  # ```ruby
35
- # class CreateUserService < ApplicationService::Base
35
+ # class Users::Create < ApplicationService::Base
36
36
  # input :role, type: String, inclusion: %w[admin user guest]
37
37
  # input :status, type: Symbol, inclusion: [:active, :inactive]
38
38
  # input :level, type: Integer, inclusion: 1..10
@@ -87,7 +87,7 @@ module Servactory
87
87
  # Creates an Inclusion validator instance.
88
88
  #
89
89
  # @param option_name [Symbol] The option name (default: :inclusion)
90
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
90
+ # @return [Servactory::Maintenance::Options::Helper]
91
91
  def self.use(option_name = :inclusion)
92
92
  instance = new(option_name, :in)
93
93
  instance.must(:be_inclusion)
@@ -35,7 +35,7 @@ module Servactory
35
35
  # Use in your service definition:
36
36
  #
37
37
  # ```ruby
38
- # class ProcessDataService < ApplicationService::Base
38
+ # class Data::Process < ApplicationService::Base
39
39
  # input :count, type: Integer, max: 100
40
40
  # input :name, type: String, max: 255
41
41
  # input :items, type: Array, max: 50
@@ -47,7 +47,7 @@ module Servactory
47
47
  # Specify maximum value directly:
48
48
  #
49
49
  # ```ruby
50
- # class ProcessDataService < ApplicationService::Base
50
+ # class Data::Process < ApplicationService::Base
51
51
  # input :count, type: Integer, max: 100
52
52
  # input :name, type: String, max: 255
53
53
  # input :items, type: Array, max: 50
@@ -97,7 +97,7 @@ module Servactory
97
97
  # Creates a Max validator instance.
98
98
  #
99
99
  # @param option_name [Symbol] The option name (default: :max)
100
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
100
+ # @return [Servactory::Maintenance::Options::Helper]
101
101
  def self.use(option_name = :max)
102
102
  new(option_name).must(:be_less_than_or_equal_to)
103
103
  end
@@ -35,7 +35,7 @@ module Servactory
35
35
  # Use in your service definition:
36
36
  #
37
37
  # ```ruby
38
- # class ProcessDataService < ApplicationService::Base
38
+ # class Data::Process < ApplicationService::Base
39
39
  # input :age, type: Integer, min: 18
40
40
  # input :password, type: String, min: 8
41
41
  # input :tags, type: Array, min: 1
@@ -47,7 +47,7 @@ module Servactory
47
47
  # Specify minimum value directly:
48
48
  #
49
49
  # ```ruby
50
- # class ProcessDataService < ApplicationService::Base
50
+ # class Data::Process < ApplicationService::Base
51
51
  # input :age, type: Integer, min: 18
52
52
  # input :password, type: String, min: 8
53
53
  # input :tags, type: Array, min: 1
@@ -97,7 +97,7 @@ module Servactory
97
97
  # Creates a Min validator instance.
98
98
  #
99
99
  # @param option_name [Symbol] The option name (default: :min)
100
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
100
+ # @return [Servactory::Maintenance::Options::Helper]
101
101
  def self.use(option_name = :min)
102
102
  new(option_name).must(:be_greater_than_or_equal_to)
103
103
  end
@@ -35,7 +35,7 @@ module Servactory
35
35
  # Use in your service definition:
36
36
  #
37
37
  # ```ruby
38
- # class ProcessOrderService < ApplicationService::Base
38
+ # class Orders::Process < ApplicationService::Base
39
39
  # input :quantity, type: Integer, multiple_of: 5
40
40
  # input :price, type: Float, multiple_of: 0.25
41
41
  # input :batch_size, type: Integer, multiple_of: 100
@@ -47,7 +47,7 @@ module Servactory
47
47
  # Specify divisor directly:
48
48
  #
49
49
  # ```ruby
50
- # class ProcessOrderService < ApplicationService::Base
50
+ # class Orders::Process < ApplicationService::Base
51
51
  # input :quantity, type: Integer, multiple_of: 5
52
52
  # input :price, type: Float, multiple_of: 0.25
53
53
  # input :batch_size, type: Integer, multiple_of: 100
@@ -93,14 +93,22 @@ module Servactory
93
93
  # ## Important Notes
94
94
  #
95
95
  # - Divisor must be a non-zero Numeric
96
- # - Uses epsilon comparison for floating point precision
96
+ # - Integer, Rational and BigDecimal operands are checked exactly
97
+ # - Float operands are accepted when the remainder is within an epsilon
98
+ # tolerance of zero or of the divisor, scaled by the absolute value
99
+ # of the checked value
97
100
  # - Returns false for non-numeric values
98
101
  # - Provides specific error messages for blank and zero divisors
99
102
  class MultipleOf < Must
103
+ # Relative tolerance for Float remainders, scaled by the absolute
104
+ # value of the checked value.
105
+ FLOAT_TOLERANCE = Float::EPSILON * 2
106
+ private_constant :FLOAT_TOLERANCE
107
+
100
108
  # Creates a MultipleOf validator instance.
101
109
  #
102
110
  # @param option_name [Symbol] The option name (default: :multiple_of)
103
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
111
+ # @return [Servactory::Maintenance::Options::Helper]
104
112
  def self.use(option_name = :multiple_of)
105
113
  new(option_name).must(:be_multiple_of)
106
114
  end
@@ -148,14 +156,34 @@ module Servactory
148
156
  return false unless option.value.is_a?(Numeric)
149
157
  return false if option.value.zero?
150
158
 
151
- # Calculate remainder with epsilon tolerance for floats.
152
- remainder = value % option.value
153
- remainder.zero? || remainder.abs < Float::EPSILON * [value.abs, option.value.abs].max
159
+ multiple_of?(value, option.value)
154
160
  else
155
161
  false
156
162
  end
157
163
  end
158
164
 
165
+ # Checks whether value is a multiple of divisor.
166
+ #
167
+ # Exact for Integer, Rational and BigDecimal operands. When either
168
+ # operand is a Float, the remainder of the absolute operands counts
169
+ # as a multiple when it is within the tolerance of zero or of the
170
+ # divisor, with the tolerance scaled by the absolute value of the
171
+ # checked value.
172
+ #
173
+ # @param value [Numeric] Value to check
174
+ # @param divisor [Numeric] Non-zero divisor
175
+ # @return [Boolean] true if value is multiple of divisor
176
+ def multiple_of?(value, divisor)
177
+ return (value % divisor).zero? unless value.is_a?(Float) || divisor.is_a?(Float)
178
+
179
+ remainder = value.abs % divisor.abs
180
+ return true if remainder.zero?
181
+ return false unless remainder.finite? && divisor.finite?
182
+
183
+ tolerance = FLOAT_TOLERANCE * value.abs
184
+ remainder <= tolerance || divisor.abs - remainder <= tolerance
185
+ end
186
+
159
187
  ########################################################################
160
188
 
161
189
  # Generates error message for input validation failure.
@@ -152,7 +152,7 @@ module Servactory
152
152
  @body_fallback = body_fallback
153
153
  end
154
154
 
155
- # Creates an OptionHelper for registration with Servactory.
155
+ # Creates an option helper for registration with Servactory.
156
156
  #
157
157
  # @param name [Symbol] Internal validation name
158
158
  # @return [Servactory::Maintenance::Options::Helper]
@@ -221,7 +221,7 @@ module Servactory
221
221
  #
222
222
  # @param option [WorkOption] Parsed option data
223
223
  # @return [Proc] Lambda that generates error message
224
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
224
+ # rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
225
225
  def must_content_message_with(option)
226
226
  is_option_message_present = option.message.present?
227
227
  is_option_message_proc = option.message.is_a?(Proc) if is_option_message_present
@@ -274,7 +274,6 @@ module Servactory
274
274
  end
275
275
  end
276
276
  end
277
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
278
277
 
279
278
  ########################################################################
280
279
 
@@ -29,7 +29,7 @@ module Servactory
29
29
  # Define schema in your service:
30
30
  #
31
31
  # ```ruby
32
- # class CreateUserService < ApplicationService::Base
32
+ # class Users::Create < ApplicationService::Base
33
33
  # input :user_data,
34
34
  # type: Hash,
35
35
  # schema: {
@@ -49,7 +49,7 @@ module Servactory
49
49
  # Specify schema definition directly:
50
50
  #
51
51
  # ```ruby
52
- # class CreateUserService < ApplicationService::Base
52
+ # class Users::Create < ApplicationService::Base
53
53
  # input :user_data,
54
54
  # type: Hash,
55
55
  # schema: {
@@ -127,8 +127,9 @@ module Servactory
127
127
  # Creates a Schema validator instance.
128
128
  #
129
129
  # @param option_name [Symbol] The option name (default: :schema)
130
- # @param default_hash_mode_class_names [Array<Class>] Valid Hash-like types
131
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
130
+ # @param default_hash_mode_class_names [Servactory::Configuration::HashMode::ClassNamesCollection]
131
+ # Valid Hash-like types
132
+ # @return [Servactory::Maintenance::Options::Helper]
132
133
  def self.use(option_name = :schema, default_hash_mode_class_names:)
133
134
  instance = new(option_name, :is, false)
134
135
  instance.assign(default_hash_mode_class_names)
@@ -137,7 +138,8 @@ module Servactory
137
138
 
138
139
  # Assigns the list of valid Hash-compatible class names.
139
140
  #
140
- # @param default_hash_mode_class_names [Array<Class>] Hash-like types to accept
141
+ # @param default_hash_mode_class_names [Servactory::Configuration::HashMode::ClassNamesCollection]
142
+ # Hash-like types to accept
141
143
  # @return [void]
142
144
  def assign(default_hash_mode_class_names)
143
145
  @default_hash_mode_class_names = default_hash_mode_class_names
@@ -179,13 +181,15 @@ module Servactory
179
181
  # @param value [Object] Hash value to validate
180
182
  # @param option [WorkOption] Schema configuration
181
183
  # @return [Boolean, Array] true if valid, or [false, reason, meta]
182
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
184
+ # rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
183
185
  def common_condition_with(attribute:, value:, option:)
184
186
  # Schema disabled, skip validation.
185
187
  return true if option.value == false
186
188
 
187
189
  # Attribute type must be Hash-compatible.
188
- return [false, :wrong_type] unless @default_hash_mode_class_names.intersect?(attribute.types)
190
+ unless @default_hash_mode_class_names.intersect?(attribute.types)
191
+ return [false, :wrong_type, wrong_type_meta_for(attribute:)]
192
+ end
189
193
 
190
194
  # Skip validation for blank optional values.
191
195
  if value.blank? && ((attribute.input? && attribute.optional?) || attribute.internal? || attribute.output?)
@@ -206,7 +210,19 @@ module Servactory
206
210
 
207
211
  [is_success, reason, meta]
208
212
  end
209
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
213
+
214
+ # Builds failure metadata for an attribute whose type is not Hash-compatible.
215
+ #
216
+ # @param attribute [Object] The attribute being validated
217
+ # @return [Hash] Metadata with the Hash-compatible types as expected
218
+ # and the declared attribute types as given
219
+ def wrong_type_meta_for(attribute:)
220
+ {
221
+ key_name: nil,
222
+ expected_type: @default_hash_mode_class_names.to_a.join(", "),
223
+ given_type: attribute.types.join(", ")
224
+ }
225
+ end
210
226
 
211
227
  # Recursively validates object against schema definition.
212
228
  #
@@ -229,16 +245,18 @@ module Servactory
229
245
  end
230
246
 
231
247
  # Validate each schema field.
232
- errors = schema.map do |schema_key, schema_value|
248
+ schema.each do |schema_key, schema_value|
233
249
  attribute_type = schema_value.fetch(:type, String)
234
250
 
235
251
  if attribute_type == Hash
236
252
  # Recursively validate nested Hash.
237
- validate_for!(
253
+ result = validate_for!(
238
254
  object: object.fetch(schema_key, {}),
239
255
  schema: schema_value.except(*RESERVED_OPTIONS),
240
256
  root_schema_key: schema_key
241
257
  )
258
+
259
+ return result unless result == true
242
260
  else
243
261
  is_success, given_type = validate_with(
244
262
  object:,
@@ -250,12 +268,11 @@ module Servactory
250
268
 
251
269
  next if is_success
252
270
 
253
- [false, :wrong_element_type, { key_name: schema_key, expected_type: attribute_type, given_type: }]
271
+ return [false, :wrong_element_type, { key_name: schema_key, expected_type: attribute_type, given_type: }]
254
272
  end
255
273
  end
256
274
 
257
- # Return first error or true.
258
- errors.compact.first || true
275
+ true
259
276
  end
260
277
 
261
278
  # Validates a single field against its type specification.
@@ -277,11 +294,11 @@ module Servactory
277
294
  return true
278
295
  end
279
296
 
280
- value = object[schema_key]
297
+ value = object.fetch(schema_key, nil)
281
298
  prepared_value = prepare_value_from(schema_value:, value:, required: attribute_required)
282
299
 
283
300
  [
284
- Array(attribute_type).uniq.any? { |type| prepared_value.is_a?(type) },
301
+ Array(attribute_type).any? { |type| prepared_value.is_a?(type) },
285
302
  prepared_value.class.name
286
303
  ]
287
304
  end
@@ -297,7 +314,7 @@ module Servactory
297
314
  required || (
298
315
  !required && !fetch_default_from(schema_value).nil?
299
316
  ) || (
300
- !required && !object[schema_key].nil?
317
+ !required && !object.fetch(schema_key, nil).nil?
301
318
  )
302
319
  end
303
320
 
@@ -330,19 +347,18 @@ module Servactory
330
347
  # @param object [Hash] Object to modify
331
348
  # @param schema [Hash] Schema definition
332
349
  # @return [void]
333
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
350
+ # rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
334
351
  def prepare_object_with!(object:, schema:)
335
- schema.map do |schema_key, schema_value|
352
+ schema.each do |schema_key, schema_value|
336
353
  attribute_type = schema_value.fetch(:type, String)
337
354
  required = schema_value.fetch(:required, true)
338
- object_value = object[schema_key]
355
+ object_value = object.fetch(schema_key, nil)
339
356
 
340
357
  if attribute_type == Hash
341
358
  # Apply nested Hash defaults.
342
- default_value = schema_value.fetch(:default, {})
343
-
344
- if !required && !default_value.nil? && !Servactory::Utils.value_present?(object_value)
345
- object[schema_key] = default_value
359
+ if !required && !Servactory::Utils.value_present?(object_value)
360
+ default_value = schema_value.key?(:default) ? schema_value[:default].deep_dup : {}
361
+ object[schema_key] = default_value unless default_value.nil?
346
362
  end
347
363
 
348
364
  # Recursively prepare nested objects.
@@ -360,14 +376,13 @@ module Servactory
360
376
 
361
377
  # Execute prepare callback if defined.
362
378
  unless (input_prepare = schema_value[:prepare]).nil?
363
- object[schema_key] = input_prepare.call(value: object[schema_key])
379
+ object[schema_key] = input_prepare.call(value: object.fetch(schema_key, nil))
364
380
  end
365
381
 
366
382
  object
367
383
  end
368
384
  end
369
385
  end
370
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
371
386
 
372
387
  ########################################################################
373
388
  ########################################################################
@@ -37,8 +37,8 @@ module Servactory
37
37
  # Use in your service definition:
38
38
  #
39
39
  # ```ruby
40
- # class ProcessOrderService < ApplicationService::Base
41
- # input :service_class, type: Class, target: UserService
40
+ # class Orders::Process < ApplicationService::Base
41
+ # input :service_class, type: Class, target: Payments::Charge
42
42
  # input :handler_class, type: Class, target: [CreateHandler, UpdateHandler]
43
43
  # end
44
44
  # ```
@@ -48,7 +48,7 @@ module Servactory
48
48
  # Specify target class directly or as an array:
49
49
  #
50
50
  # ```ruby
51
- # input :service_class, type: Class, target: UserService
51
+ # input :service_class, type: Class, target: Payments::Charge
52
52
  # input :handler_class, type: Class, target: [CreateHandler, UpdateHandler]
53
53
  # ```
54
54
  #
@@ -100,7 +100,7 @@ module Servactory
100
100
  # Creates a Target validator instance.
101
101
  #
102
102
  # @param option_name [Symbol] The option name (default: :target)
103
- # @return [Servactory::Maintenance::Attributes::OptionHelper]
103
+ # @return [Servactory::Maintenance::Options::Helper]
104
104
  def self.use(option_name = :target)
105
105
  instance = new(option_name, :in)
106
106
  instance.must(:"be_#{option_name}")
@@ -5,13 +5,17 @@ module Servactory
5
5
  extend self
6
6
 
7
7
  def adapt(data)
8
- if defined?(Datory::Base) && data.is_a?(Datory::Base)
9
- data = Servactory::Utils.send(:instance_variables_to_hash_from, data)
10
- end
8
+ data = instance_variables_to_hash_from(data) if datory?(data)
11
9
 
12
10
  data.symbolize_keys
13
11
  end
14
12
 
13
+ # @param data [Object]
14
+ # @return [Boolean] True if #adapt accepts the data as service arguments
15
+ def adaptable?(data)
16
+ data.is_a?(Hash) || datory?(data)
17
+ end
18
+
15
19
  def define_attribute_with(input: nil, internal: nil, output: nil)
16
20
  return input if really_input?(input)
17
21
  return internal if really_internal?(internal)
@@ -108,6 +112,12 @@ module Servactory
108
112
 
109
113
  private
110
114
 
115
+ def datory?(data)
116
+ return false unless defined?(Datory::Base)
117
+
118
+ data.is_a?(Datory::Base)
119
+ end
120
+
111
121
  def instance_variables_to_hash_from(data) # rubocop:disable Metrics/MethodLength
112
122
  data.instance_variables.to_h do |key|
113
123
  value = data.instance_variable_get(key)
@@ -5,7 +5,7 @@ module Servactory
5
5
  MAJOR = 3
6
6
  MINOR = 1
7
7
  PATCH = 0
8
- PRE = "rc2"
8
+ PRE = "rc4"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
data/lib/servactory.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "zeitwerk"
4
4
 
5
+ require "forwardable"
6
+
5
7
  require "stroma"
6
8
 
7
9
  require "active_support/all"
@@ -10,16 +12,13 @@ require "uri"
10
12
 
11
13
  loader = Zeitwerk::Loader.for_gem
12
14
  loader.ignore("#{__dir__}/generators")
15
+ loader.ignore("#{__dir__}/servactory/engine.rb")
13
16
  loader.ignore("#{__dir__}/servactory/test_kit/rspec")
14
17
  loader.inflector.inflect(
15
18
  "dsl" => "DSL"
16
19
  )
17
20
  loader.setup
18
21
 
19
- # Eager load DSL to initialize Stroma::Registry.
20
- # Registry must be populated before any service class is defined.
21
- require_relative "servactory/dsl"
22
-
23
22
  module Servactory; end
24
23
 
25
24
  require "servactory/engine" if defined?(Rails::Engine)
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: 3.1.0.rc2
4
+ version: 3.1.0.rc4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
@@ -43,14 +43,20 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '0.4'
46
+ version: '0.5'
47
+ - - "<"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.0'
47
50
  type: :runtime
48
51
  prerelease: false
49
52
  version_requirements: !ruby/object:Gem::Requirement
50
53
  requirements:
51
54
  - - ">="
52
55
  - !ruby/object:Gem::Version
53
- version: '0.4'
56
+ version: '0.5'
57
+ - - "<"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.0'
54
60
  - !ruby/object:Gem::Dependency
55
61
  name: zeitwerk
56
62
  requirement: !ruby/object:Gem::Requirement
@@ -184,6 +190,7 @@ executables: []
184
190
  extensions: []
185
191
  extra_rdoc_files: []
186
192
  files:
193
+ - LICENSE
187
194
  - README.md
188
195
  - Rakefile
189
196
  - config/locales/de.yml
@@ -191,6 +198,7 @@ files:
191
198
  - config/locales/es.yml
192
199
  - config/locales/fr.yml
193
200
  - config/locales/it.yml
201
+ - config/locales/ja.yml
194
202
  - config/locales/ru.yml
195
203
  - lib/generators/README.md
196
204
  - lib/generators/servactory/base.rb
@@ -290,20 +298,28 @@ files:
290
298
  - lib/servactory/test_kit/rspec/helpers/argument_matchers.rb
291
299
  - lib/servactory/test_kit/rspec/helpers/concerns/error_messages.rb
292
300
  - lib/servactory/test_kit/rspec/helpers/concerns/service_class_validation.rb
301
+ - lib/servactory/test_kit/rspec/helpers/exception_validator.rb
293
302
  - lib/servactory/test_kit/rspec/helpers/fluent.rb
294
303
  - lib/servactory/test_kit/rspec/helpers/input_validator.rb
295
304
  - lib/servactory/test_kit/rspec/helpers/legacy.rb
296
305
  - lib/servactory/test_kit/rspec/helpers/mock_executor.rb
297
306
  - lib/servactory/test_kit/rspec/helpers/output_validator.rb
307
+ - lib/servactory/test_kit/rspec/helpers/service_inputs_guard.rb
308
+ - lib/servactory/test_kit/rspec/helpers/service_inputs_matcher.rb
298
309
  - lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb
299
310
  - lib/servactory/test_kit/rspec/helpers/service_mock_config.rb
311
+ - lib/servactory/test_kit/rspec/helpers/wrap_block_adapter.rb
300
312
  - lib/servactory/test_kit/rspec/matchers.rb
301
313
  - lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb
314
+ - lib/servactory/test_kit/rspec/matchers/base/message_expectation.rb
302
315
  - lib/servactory/test_kit/rspec/matchers/base/submatcher.rb
303
316
  - lib/servactory/test_kit/rspec/matchers/base/submatcher_context.rb
304
317
  - lib/servactory/test_kit/rspec/matchers/base/submatcher_registry.rb
305
318
  - lib/servactory/test_kit/rspec/matchers/concerns/attribute_data_access.rb
306
319
  - lib/servactory/test_kit/rspec/matchers/concerns/error_message_builder.rb
320
+ - lib/servactory/test_kit/rspec/matchers/concerns/option_helper_rules.rb
321
+ - lib/servactory/test_kit/rspec/matchers/concerns/proc_message.rb
322
+ - lib/servactory/test_kit/rspec/matchers/concerns/required_message.rb
307
323
  - lib/servactory/test_kit/rspec/matchers/concerns/value_comparison.rb
308
324
  - lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb
309
325
  - lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb
@@ -357,7 +373,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
357
373
  - !ruby/object:Gem::Version
358
374
  version: '0'
359
375
  requirements: []
360
- rubygems_version: 4.0.4
376
+ rubygems_version: 4.0.21
361
377
  specification_version: 4
362
378
  summary: A set of tools for building reliable services of any complexity
363
379
  test_files: []