Class: Servactory::ToolKit::DynamicOptions::Must

Inherits:
Object
  • Object
show all
Defined in:
lib/servactory/tool_kit/dynamic_options/must.rb

Overview

Base class for creating custom dynamic validation options.

## Purpose

Must provides a foundation for implementing custom validation rules that can be applied to service inputs, internals, and outputs. It handles the complexity of option parsing, condition evaluation, and error message generation, allowing subclasses to focus on validation logic.

## Usage

Create a custom validator by inheriting from Must:

“‘ruby class MyValidator < Servactory::ToolKit::DynamicOptions::Must

def self.use(option_name = :my_option)
  new(option_name).must(:my_validation)
end

def condition_for_input_with(input:, value:, option:)
  # Return true if valid, false otherwise
  value.meets_criteria?(option.value)
end

def message_for_input_with(input:, value:, option_name:, option_value:, **)
  "Input `#{input.name}` must satisfy #{option_name}"
end

# Implement similar methods for internal and output...

end “‘

Register in service configuration:

“‘ruby configuration do

input_option_helpers([
  MyValidator.use
])

end “‘

## Simple Mode

Custom validators support simple mode with direct value:

“‘ruby input :value, type: Integer, my_option: 10 “`

## Advanced Mode

Custom validators support advanced mode with custom messages:

With static message:

“‘ruby input :value, type: Integer, my_option:

is: 10,
message: "Custom validation failed"

“‘

With dynamic lambda message:

“‘ruby input :value, type: Integer, my_option:

is: 10,
message: lambda do |input:, value:, option_value:, **|
  "Input `#{input.name` failed validation with value `#value`"
end

} “‘

Lambda receives the following parameters:

  • For inputs: ‘input:, option_value:, value:, **`

  • For internals: ‘internal:, option_value:, value:, **`

  • For outputs: ‘output:, option_value:, value:, **`

## Architecture

The class uses a two-phase validation approach:

  1. **Condition phase**: ‘condition_for_*` methods return boolean

  2. **Message phase**: ‘message_for_*` methods generate error text

## Important Notes

  • Subclasses must implement all six abstract methods

  • Option values support both simple mode (‘option: value`) and advanced mode (`option: { is: value, message: “…” }`)

  • Custom messages can be strings or Procs for dynamic generation

Direct Known Subclasses

ConsistsOf, Format, Inclusion, Max, Min, MultipleOf, Schema, Target

Defined Under Namespace

Classes: WorkOption

Instance Method Summary collapse

Constructor Details

#initialize(option_name, body_key = :is, body_fallback = nil) ⇒ Must

Creates a new Must instance.

Parameters:

  • option_name (Symbol)

    Name of the option (e.g., :min, :max)

  • body_key (Symbol) (defaults to: :is)

    Key for extracting value in advanced mode

  • body_fallback (Object) (defaults to: nil)

    Default value when none provided



149
150
151
152
153
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 149

def initialize(option_name, body_key = :is, body_fallback = nil)
  @option_name = option_name
  @body_key = body_key
  @body_fallback = body_fallback
end

Instance Method Details

#condition_for_input_withBoolean

This method is abstract.

Subclasses must implement this method

Validates condition for input attribute.

Parameters:

  • input (Object)

    Input attribute object

  • value (Object)

    Current value being validated

  • option (WorkOption)

    Validation option configuration

Returns:

  • (Boolean)

    true if valid, false otherwise

Raises:

  • (RuntimeError)

    If not implemented in subclass



289
290
291
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 289

def condition_for_input_with(**)
  raise "Need to implement `condition_for_input_with(**attributes)` method"
end

#condition_for_internal_withBoolean

This method is abstract.

Subclasses must implement this method

Validates condition for internal attribute.

Parameters:

  • internal (Object)

    Internal attribute object

  • value (Object)

    Current value being validated

  • option (WorkOption)

    Validation option configuration

Returns:

  • (Boolean)

    true if valid, false otherwise

Raises:

  • (RuntimeError)

    If not implemented in subclass



301
302
303
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 301

def condition_for_internal_with(**)
  raise "Need to implement `condition_for_internal_with(**attributes)` method"
end

#condition_for_output_withBoolean

This method is abstract.

Subclasses must implement this method

Validates condition for output attribute.

Parameters:

  • output (Object)

    Output attribute object

  • value (Object)

    Current value being validated

  • option (WorkOption)

    Validation option configuration

Returns:

  • (Boolean)

    true if valid, false otherwise

Raises:

  • (RuntimeError)

    If not implemented in subclass



313
314
315
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 313

def condition_for_output_with(**)
  raise "Need to implement `condition_for_output_with(**attributes)` method"
end

#equivalent_with(name) ⇒ Proc

Builds the equivalence lambda for option transformation.

Parameters:

  • name (Symbol)

    Validation name

Returns:

  • (Proc)

    Lambda that transforms option data to must format



174
175
176
177
178
179
180
181
182
183
184
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 174

def equivalent_with(name)
  lambda do |data|
    option = WorkOption.new(@option_name, data, body_key: @body_key, body_fallback: @body_fallback)

    {
      must: {
        name => must_content_with(option)
      }
    }
  end
end

#message_for_input_withString

This method is abstract.

Subclasses must implement this method

Generates error message for input validation failure.

Parameters:

  • input (Object)

    Input attribute object

  • option_name (Symbol)

    Name of the failed option

  • option_value (Object)

    Expected value

Returns:

  • (String)

    Human-readable error message

Raises:

  • (RuntimeError)

    If not implemented in subclass



325
326
327
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 325

def message_for_input_with(**)
  raise "Need to implement `message_for_input_with(**attributes)` method"
end

#message_for_internal_withString

This method is abstract.

Subclasses must implement this method

Generates error message for internal validation failure.

Parameters:

  • internal (Object)

    Internal attribute object

  • option_name (Symbol)

    Name of the failed option

  • option_value (Object)

    Expected value

Returns:

  • (String)

    Human-readable error message

Raises:

  • (RuntimeError)

    If not implemented in subclass



337
338
339
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 337

def message_for_internal_with(**)
  raise "Need to implement `message_for_internal_with(**attributes)` method"
end

#message_for_output_withString

This method is abstract.

Subclasses must implement this method

Generates error message for output validation failure.

Parameters:

  • output (Object)

    Output attribute object

  • option_name (Symbol)

    Name of the failed option

  • option_value (Object)

    Expected value

Returns:

  • (String)

    Human-readable error message

Raises:

  • (RuntimeError)

    If not implemented in subclass



349
350
351
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 349

def message_for_output_with(**)
  raise "Need to implement `message_for_output_with(**attributes)` method"
end

#must(name) ⇒ Servactory::Maintenance::Attributes::OptionHelper

Creates an OptionHelper for registration with Servactory.

Parameters:

  • name (Symbol)

    Internal validation name

Returns:



159
160
161
162
163
164
165
166
167
168
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 159

def must(name)
  Servactory::Maintenance::Attributes::OptionHelper.new(
    name: @option_name,
    equivalent: equivalent_with(name),
    meta: {
      type: :dynamic_option,
      body_key: @body_key
    }
  )
end

#must_content_message_with(option) ⇒ Proc

Builds the error message lambda.

Handles three message sources:

  1. Custom Proc message (called with context)

  2. Custom String message (returned as-is)

  3. Default message from subclass implementation

rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

Parameters:

Returns:

  • (Proc)

    Lambda that generates error message



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 225

def must_content_message_with(option)
  is_option_message_present = option.message.present?
  is_option_message_proc = option.message.is_a?(Proc) if is_option_message_present

  lambda do |input: nil, internal: nil, output: nil, **attributes| # rubocop:disable Metrics/BlockLength
    default_attributes = { **attributes, option_name: option.name, option_value: option.value }

    if Servactory::Utils.really_input?(input)
      if is_option_message_present
        if is_option_message_proc
          option.message.call(
            input:,
            **default_attributes.delete(:meta) || {},
            **default_attributes
          )
        else
          option.message
        end
      else
        message_for_input_with(**default_attributes, input:)
      end
    elsif Servactory::Utils.really_internal?(internal)
      if is_option_message_present
        if is_option_message_proc
          option.message.call(
            internal:,
            **default_attributes.delete(:meta) || {},
            **default_attributes
          )
        else
          option.message
        end
      else
        message_for_internal_with(**default_attributes, internal:)
      end
    elsif Servactory::Utils.really_output?(output)
      if is_option_message_present
        if is_option_message_proc
          option.message.call(
            output:,
            **default_attributes.delete(:meta) || {},
            **default_attributes
          )
        else
          option.message
        end
      else
        message_for_output_with(**default_attributes, output:)
      end
    end
  end
end

#must_content_value_with(option) ⇒ Proc

Builds the validation condition lambda.

Parameters:

Returns:

  • (Proc)

    Lambda that evaluates validation condition



203
204
205
206
207
208
209
210
211
212
213
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 203

def must_content_value_with(option)
  lambda do |value:, input: nil, internal: nil, output: nil|
    if input.present? && input.input?
      condition_for_input_with(input:, value:, option:)
    elsif internal.present? && internal.internal?
      condition_for_internal_with(internal:, value:, option:)
    elsif output.present? && output.output?
      condition_for_output_with(output:, value:, option:)
    end
  end
end

#must_content_with(option) ⇒ Hash

Constructs the must content hash with value and message lambdas.

Parameters:

Returns:

  • (Hash)

    Hash with :is and :message keys



190
191
192
193
194
195
# File 'lib/servactory/tool_kit/dynamic_options/must.rb', line 190

def must_content_with(option)
  {
    is: must_content_value_with(option),
    message: must_content_message_with(option)
  }
end