Last active
July 23, 2020 13:18
-
-
Save esquinas/6c47046b1557a7b372466032187b152f to your computer and use it in GitHub Desktop.
Monkeypatch to mock this feature https://bugs.ruby-lang.org/issues/16986 but using a Hash#to_struct method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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: '[email protected]', | |
# address: { | |
# street: '123 Fake st.', | |
# city: nil, | |
# }, | |
# }.to_struct(deep: true) | |
# my_deep_struct.email # => "[email protected]" | |
# 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Done! Thanks @elia, I made some renaming as well.