Formatters

Formatters format dates, money and other values the same way in every serializer. Define named formatters in a base serializer and use them with the :format attribute option.

class AppSerializer < Serega
  formatter :iso8601, [:iso8601, 3], allow_nil: true
  formatter :money, ->(cents, ctx) { "#{(cents / 100.0).round(2)} #{ctx[:currency]}" }
end

class OrderSerializer < AppSerializer
  attribute :created_at, format: :iso8601
  attribute :total_cents, format: :money
end

OrderSerializer.to_h(order, context: {currency: "EUR"})
# => {created_at: "2026-10-18T12:30:15.000Z", total_cents: "19.99 EUR"}

Child serializers get the formatters of their parents.

Formatter kinds

class AppSerializer < Serega
  # A method name: value.to_s
  formatter :string, :to_s

  # A method name with arguments: value.iso8601(3)
  formatter :iso8601, [:iso8601, 3]

  # A callable with the value
  formatter :yes_no, ->(value) { value ? "yes" : "no" }

  # A callable with the value and the context, as a second parameter or the :ctx keyword
  formatter :money, ->(cents, ctx) { "#{cents / 100.0} #{ctx[:currency]}" }
  formatter :price, ->(cents, ctx:) { "#{cents / 100.0} #{ctx[:currency]}" }

  # A block
  formatter(:upcase) { |value| value.upcase }
end

A method formatter is called directly in the generated serialization code, which makes it the fastest kind.

Formatters without a name

:format also accepts a formatter directly. A Symbol is always a formatter name, so a method name alone needs a named formatter.

attribute :rating, format: [:round, 1]
attribute :email, format: ->(email) { email.downcase }
attribute :nickname, format: {use: :upcase, allow_nil: true}

nil values

A formatter formats nil too: a callable receives nil, and a method is called on nil. With allow_nil: true a nil value stays nil.

formatter :iso8601, [:iso8601, 3], allow_nil: true
attribute :rating, format: {use: [:round, 1], allow_nil: true}

A :default value is formatted too.