Skip to content

Inputs - Transformations

Modify input values after coercion but before validation — e.g. normalization, formatting, and data cleanup.

Processing Pipeline

Each input flows through a fixed pipeline:

flowchart LR
    Source --> Default --> Coerce --> Transform --> Validate
Stage Description
Source Resolve value from context, method, proc, or callable
Default Apply default: when the resolved value is nil
Coerce Convert via coerce: (e.g., string → integer)
Transform Apply transform: to the coerced value
Validate Run validators (presence, format, etc.) on the final value

This means transformations receive already-coerced values, and validators see the final transformed output.

Declarations

Symbol References

Reference a method by symbol. The value's own method takes precedence (called as value.send(symbol) with no arguments); if the value doesn't respond to it, CMDx falls back to task.send(symbol, value):

class ProcessAnalytics < CMDx::Task
  input :options, transform: :compact_blank   # value.compact_blank or task#compact_blank(value)
end

Proc or Lambda

Use anonymous functions for inline transformations:

class CacheContent < CMDx::Task
  # Proc
  input :expire_hours, transform: proc { |v| v * 2 }

  # Lambda
  input :compression, transform: ->(v) { v.to_s.upcase.strip[0..2] }
end

Class or Module

Use any object that responds to #call(value, task) for reusable transformation logic:

class EmailNormalizer
  def self.call(value, _task)
    value.to_s.downcase.strip
  end
end

class PhoneNormalizer
  def call(value, _task)
    value.to_s.gsub(/\D/, "")
  end
end

class ProcessContacts < CMDx::Task
  # Class with a class-level `.call` — pass the class itself
  input :email, transform: EmailNormalizer

  # Instance with `#call` — instantiate when registering
  input :phone, transform: PhoneNormalizer.new
end

Pipeline Position

Validations run on transformed values, ensuring data consistency:

class ScheduleBackup < CMDx::Task
  # Coerce then clamp
  input :retention_days, coerce: :integer, transform: proc { |v| v.clamp(1, 5) }

  # Downcase then validate inclusion
  optional :frequency, transform: :downcase, inclusion: { in: %w[hourly daily weekly monthly] }
end

Optional + nil value

Transforms run only when the coerced value is non-nil. When an optional input's key is absent from context and no default is declared, the pipeline short-circuits after the default step — transforms (and validators) don't fire. Declare a default: or required if you need the transform to always run.