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
You can’t perform that action at this time.