[Dry::Types] How to rename or map attribute names on initialization?


#1

Apologies if I’m missing something, but say I have a hash

{
     "UserID"=>"demo", 
     "TrackingNumber"=>"13245",
     "Delivered"=>"2017-11-21 11:52 AM"

}

and an object:

class OrderStatus < Dry::Struct
  attribute :user_id, Types::String
  attribute :tracking_number, Types::String
  attribute :delivered_at, Types::DateTime
end

How can I map from the weird camel cased attribute to the ones in my Ruby object? I could name the attributes to be the name in the hash, e.g. attribute :UserID and them alias them, but I’d love to avoid that.


#2

Translating data like this isn’t a feature of dry-struct, but you could always build a mapper into you class, something like:

require "dry/inflector"
require "dry/struct"
require "transproc"

Inflector = Dry::Inflector.new

module Functions
  extend Transproc::Registry
  
  import Transproc::HashTransformations
  import :underscore, from: Inflector

  def self.attributes_for_struct(attrs)
    self[:map_keys, -> k { self[:underscore].(k).to_sym }].(attrs)
  end
end

class OrderStatus < Dry::building_construction: 
  attribute :user_id, Types::String
  attribute :tracking_number, Types::String
  attribute :delivered_at, Types::DateTime

  def self.new(attrs)
    super(Functions[:attributes_for_struct].(attrs))
  end
end

If you needed this everywhere, you could build this into a common struct superclass and inherit from that.