Class: Treaty::Attribute::Option::Modifiers::CastModifier

Inherits:
Base
  • Object
show all
Defined in:
lib/treaty/attribute/option/modifiers/cast_modifier.rb

Overview

Converts attribute values between different types automatically.

## Usage Examples

Simple mode:

string :created_at, cast: :datetime
datetime :timestamp, cast: :string
integer :active, cast: :boolean

Advanced mode with custom error message:

string :created_at, cast: {
  to: :datetime,
  message: "Invalid date format"
}

## Use Cases

  1. **Request type conversion**: “‘ruby request do

    string :created_at, cast: :datetime
    

    end # Input: { created_at: “2024-01-15T10:30:00Z” } # Service receives: { created_at: DateTime object } “‘

  2. **Response type conversion**: “‘ruby response 200 do

    datetime :created_at, cast: :string
    

    end # Service returns: { created_at: DateTime object } # Output: { created_at: “2024-01-15T10:30:00Z” } “‘

  3. **Unix timestamp conversion**: “‘ruby integer :timestamp, cast: :datetime datetime :created_at, cast: :integer “`

## Supported Conversions

### From Integer

  • integer -> string: Converts to string representation

  • integer -> boolean: 0 = false, non-zero = true

  • integer -> date: Treats as Unix timestamp, converts to date

  • integer -> time: Treats as Unix timestamp

  • integer -> datetime: Treats as Unix timestamp, converts to datetime

### From String

  • string -> integer: Parses integer from string

  • string -> boolean: Parses truthy/falsy strings (true/false, yes/no, 1/0, on/off)

  • string -> date: Parses date string

  • string -> time: Parses time string

  • string -> datetime: Parses datetime string (ISO8601, RFC3339, etc.)

### From Boolean

  • boolean -> string: Converts to “true” or “false”

  • boolean -> integer: true = 1, false = 0

### From Date

  • date -> string: Converts to ISO8601 format

  • date -> integer: Converts to Unix timestamp

  • date -> time: Converts to Time at midnight

  • date -> datetime: Converts to DateTime at midnight

### From Time

  • time -> string: Converts to ISO8601 format

  • time -> integer: Converts to Unix timestamp

  • time -> date: Converts to Date

  • time -> datetime: Converts to DateTime

### From DateTime

  • datetime -> string: Converts to ISO8601 format

  • datetime -> integer: Converts to Unix timestamp

  • datetime -> date: Converts to Date

  • datetime -> time: Converts to Time

## Important Notes

  • Cast option only works with scalar types (integer, string, boolean, date, time, datetime)

  • Array and Object types are not supported for casting

  • Casting to the same type is allowed (no-op)

  • Nil values are not transformed (handled by RequiredValidator)

  • All conversion errors are caught and re-raised as Validation errors

## Error Handling

If conversion fails (e.g., invalid date string, non-numeric string to integer), the error is caught and converted to a Treaty::Exceptions::Validation error.

## Advanced Mode

Schema format: ‘{ to: :target_type, message: “Custom error” }` Note: Uses `:to` key instead of the default `:is` key.

Constant Summary collapse

ALLOWED_CAST_TYPES =

Types that support casting (scalar types only)

%i[integer string boolean date time datetime].freeze

Instance Method Summary collapse

Methods inherited from Base

#initialize, #target_name, #transforms_name?, #validate_value!

Constructor Details

This class inherits a constructor from Treaty::Attribute::Option::Base

Instance Method Details

#transform_value(value) ⇒ Object

Applies type conversion to the value Skips conversion for nil values (handled by RequiredValidator)

Parameters:

  • value (Object)

    The current value

Returns:

  • (Object)

    Converted value



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/treaty/attribute/option/modifiers/cast_modifier.rb', line 166

def transform_value(value) # rubocop:disable Metrics/MethodLength
  return value if value.nil? # Cast doesn't modify nil, required validator handles it.

  target_type = option_value
  conversion_lambda = conversion_matrix.dig(@attribute_type, target_type)

  # Call conversion lambda
  conversion_lambda.call(value:)
rescue StandardError => e
  attributes = {
    attribute: @attribute_name,
    from: @attribute_type,
    to: target_type,
    value:,
    error: e.message
  }

  # Catch all exceptions from conversion execution
  error_message = resolve_custom_message(**attributes) || I18n.t(
    "treaty.attributes.modifiers.cast.conversion_error",
    **attributes
  )

  raise Treaty::Exceptions::Validation, error_message
end

#validate_schema!void

This method returns an undefined value.

Validates that cast option is correctly configured

Raises:



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/treaty/attribute/option/modifiers/cast_modifier.rb', line 111

def validate_schema! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  # If option_schema is nil, cast is not used for this attribute
  return if @option_schema.nil?

  target_type = option_value

  # Validate that target type is a Symbol
  unless target_type.is_a?(Symbol)
    raise Treaty::Exceptions::Validation,
          I18n.t(
            "treaty.attributes.modifiers.cast.invalid_type",
            attribute: @attribute_name,
            type: target_type.class
          )
  end

  # Validate that source type supports casting
  unless ALLOWED_CAST_TYPES.include?(@attribute_type)
    raise Treaty::Exceptions::Validation,
          I18n.t(
            "treaty.attributes.modifiers.cast.source_not_supported",
            attribute: @attribute_name,
            source_type: @attribute_type,
            allowed: ALLOWED_CAST_TYPES.join(", ")
          )
  end

  # Validate that target type is allowed
  unless ALLOWED_CAST_TYPES.include?(target_type)
    raise Treaty::Exceptions::Validation,
          I18n.t(
            "treaty.attributes.modifiers.cast.target_not_supported",
            attribute: @attribute_name,
            target_type:,
            allowed: ALLOWED_CAST_TYPES.join(", ")
          )
  end

  # Validate that conversion from source to target is supported
  return if conversion_supported?(@attribute_type, target_type)

  raise Treaty::Exceptions::Validation,
        I18n.t(
          "treaty.attributes.modifiers.cast.conversion_not_supported",
          attribute: @attribute_name,
          from: @attribute_type,
          to: target_type
        )
end