The Crystal Programming Language Forum

NamedTuples, arguments, and compile-time key presence

I’m trying to do something along the lines of below to achieve type-safe setting of attributes via a double splat

class ORM
  @field1 : String
  @field2 : Int32

  ATTRS = {
    :field1 => String,
    :field2 => Int32,
  }

  macro finished
    __generate_assigner__
  end

  macro __generate_assigner__
    def assign_attributes(**args)
      {% for name, type in ATTRS %}
        {% key = name.id.symbolize %}
        if args.has_key?({{key}})
          @{{name.id}} = args[{{key}}]
        end
      {% end %}
    end
  end
end

This won’t work, as the compile-time key presence check of the NamedTuple prevents this. https://carc.in/#/r/9gsq

Any ideas on how to achieve this, or something similar?

Thanks crystal massive :blush:

1 Like

What about just not doing this and using normal setters and/or an initializer?

1 Like

Here you go:

module Missing
  extend self
end

class ORM
  @field1 : String = "hello"
  @field2 : Int32 = 1

  ATTRS = {
    :field1 => String,
    :field2 => Int32,
  }

  macro finished
    __generate_assigner__
  end

  macro __generate_assigner__
    def assign_attributes(*,
      {% for name, type in ATTRS %}
        {{name.id}} : {{type}} | Missing = Missing,
      {% end %}
    )
      {% for name, type in ATTRS %}
        unless {{name.id}}.is_a?(Missing)
          @{{name.id}} = {{name.id}}
        end
      {% end %}
    end
  end
end

orm = ORM.new
p orm
orm.assign_attributes(field1: "bye")
p orm
orm.assign_attributes(field2: 10)
p orm
orm.assign_attributes(field1: "works", field2: 20)
p orm
5 Likes

Perfect solution.

Ary mate… you’re a bloody legend

4 Likes