Skip to content

Attributes & Normalization

Declaring attributes

Attributes are declared with attribute, using ActiveModel's type system:

ruby
class Employee::Form < Dex::Form
  attribute :name, :string
  attribute :age, :integer
  attribute :bio, :string
  attribute :active, :boolean, default: true
  attribute :born_on, :date
end

Values are type-cast on assignment – "30" becomes 30 for an integer attribute, "1" becomes true for a boolean.

Available types

TypeRuby classCasts from
:stringStringanything via .to_s
:integerInteger"42"42
:floatFloat"3.14"3.14
:decimalBigDecimal"9.99"BigDecimal("9.99")
:booleanTrueClass/FalseClass"1", "true"true
:dateDate"2024-01-15"Date
:datetimeDateTimeISO 8601 strings
:timeTimeISO 8601 strings

Defaults

ruby
attribute :role, :string, default: "member"
attribute :tags, :string   # defaults to nil

Normalization

normalizes transforms values on every assignment – during initialization, assign_attributes, or direct setter calls:

ruby
class Employee::Form < Dex::Form
  attribute :email, :string
  attribute :phone, :string

  normalizes :email, with: -> { _1&.strip&.downcase.presence }
  normalizes :phone, with: -> { _1&.gsub(/\D/, "").presence }
end

form = Employee::Form.new(email: "  [email protected]  ", phone: "(555) 123-4567")
form.email  # => "[email protected]"
form.phone  # => "5551234567"

form.email = "  [email protected]  "
form.email  # => "[email protected]"

Multiple attributes

Apply the same normalizer to several attributes at once:

ruby
normalizes :first_name, :last_name, with: -> { _1&.strip.presence }

Nil handling

If your normalizer returns nil, the attribute becomes nil. This is useful with .presence to convert blank strings:

ruby
normalizes :bio, with: -> { _1&.strip.presence }

form = Employee::Form.new(bio: "   ")
form.bio  # => nil

WARNING

normalizes requires Rails 7.1+. On older Rails versions, the method won't be available.

Reading and writing

ruby
form = Employee::Form.new(name: "Alice", age: 30)

form.name               # => "Alice"
form.age                # => 30
form.name = "Bob"       # direct setter
form.assign_attributes(age: 31)  # bulk update

form.attribute_names    # => ["name", "age", "bio", "active", "born_on"]

String keys

Forms accept both symbol and string keys – handy when working with ActionController params:

ruby
form = Employee::Form.new("name" => "Alice", "age" => "30")
form.name  # => "Alice"
form.age   # => 30