Skip to content

Instantly share code, notes, and snippets.

@esquinas

esquinas/hash_to_struct.rb

Last active Jul 23, 2020
Embed
What would you like to do?
Monkeypatch to mock this feature https://bugs.ruby-lang.org/issues/16986 but using a Hash#to_struct method.
# Usage:
# my_struct = { alpha: 1, beta: 2, 'charlie' => 3 }.to_struct
# my_struct.alpha # => 1
# my_struct.beta # => 2
# my_struct.charlie # => 3
# Remember, to compare structs: `my_struct.to_h == other_struct.to_h`
#
# Usage to get deep structs:
# my_deep_struct = {
# email: 'alice@example.com',
# address: {
# street: '123 Fake st.',
# city: nil,
# },
# }.to_struct(deep: true)
# my_deep_struct.email # => "alice@example.com"
# my_deep_struct.address.street # => "123 Fake st."
# my_deep_struct.address.city # => nil
# my_deep_struct.address.city = 'Awesomeland'
# my_deep_struct.address.city # => "Awesomeland"
#
# Usage to get "immutable" or frozen structs:
# frozen_struct = { alpha: 1, beta: 2, 'charlie' => 3 }.to_frozen_struct
# frozen_struct.charlie = 42 # => FrozenError (can't modify frozen #<Class:0x0...>)
class Hash
def to_struct(deep: false)
the_keys = keys.sort_by(&:to_s)
the_values = values_at(*the_keys)
the_values = recursively_convert_to_struct(the_values) if deep
Struct.new(*the_keys).new(*the_values)
end
def to_frozen_struct(deep: false)
recursively_freeze(
Marshal.load(Marshal.dump(self)).to_struct(deep: deep)
)
end
private
def recursively_freeze(object)
if object.respond_to?(:each_pair)
object.each_pair do |_key, value|
value = recursively_freeze(value)
end
elsif object.respond_to?(:each)
object.each do |obj|
obj = recursively_freeze(obj)
end
end
object.freeze
end
def recursively_convert_to_struct(values)
return values.to_struct(deep: true) if values.respond_to?(:to_struct)
if values.respond_to?(:map)
return values.map { |v| recursively_convert_to_struct(v) }
end
values
end
end
@elia

This comment has been minimized.

Copy link

@elia elia commented Jul 23, 2020

Given ruby's current naming I would suggest to rename to_immutable_struct to to_frozen_struct

@esquinas

This comment has been minimized.

Copy link
Owner Author

@esquinas esquinas commented Jul 23, 2020

Done! Thanks @elia, I made some renaming as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment