Created
February 2, 2017 08:07
-
-
Save jiahaog/2d8fb12bb5d151f25387377eb310ec6a to your computer and use it in GitHub Desktop.
Shaped Ruby Struct
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
# frozen_string_literal: true | |
# This class creates a object which behaves like a hash, except that it also | |
# responds to hash keys sent as messages. We need this type for GraphQL | |
# because the Gem sends messages to our objects instead of accessing it with | |
# `:[]`. | |
# | |
# It also performs type checking based on a type provided to the shape: | |
# | |
# === Example === | |
# | |
# test_case = ShapedStruct.from_hash({ | |
# books: { | |
# codes: [String, Integer, { | |
# description: String | |
# }] | |
# } | |
# }, { | |
# books: { | |
# codes: ['123', 1234, { | |
# description: 'some type' | |
# }] | |
# } | |
# }) | |
# | |
module ShapedStruct | |
# Creates a new ShapedStruct class | |
def self.from_hash(shape, hash) | |
new_hash = shape.map do |key, value| | |
new_value = case value | |
when Hash | |
from_hash(value, hash[key]) | |
when Array | |
from_array(value, hash[key]) | |
else | |
# key here is a type, check if the type is the same as the value | |
unless value === hash[key] | |
raise "Wrong format supplied, expected #{hash[key]} to be of type #{value}" | |
end | |
hash[key] | |
end | |
[key, new_value] | |
end.to_h | |
the_type = Struct.new(*shape.keys) | |
values_to_set = shape.keys.map do |key| | |
new_hash[key] | |
end | |
the_instance = the_type.new(*values_to_set) | |
the_instance | |
end | |
def self.from_array(shape, arr) | |
if shape.length != arr.length | |
raise "Shape length of length #{shape.length} does not match array of length #{shape.length}" | |
end | |
shape.zip(arr).map do |key, value| | |
case key | |
when Hash | |
from_hash(key, value) | |
when Array | |
from_array(key, value) | |
else | |
# key here is a type, check if the type is the same as the value | |
unless key === value | |
raise "Wrong format supplied, expected #{value} to be of type #{key}" | |
end | |
value | |
end | |
end | |
end | |
end | |
### Test cases | |
# test_case = ShapedStruct.from_hash({ | |
# books: String | |
# }, { | |
# books: 'some string' | |
# }) | |
# puts test_case.books | |
# will raise | |
# test_case = ShapedStruct.from_hash({ | |
# books: Integer | |
# }, { | |
# books: 1 | |
# }) | |
# puts test_case.books | |
# test_case = ShapedStruct.from_hash({ | |
# books: { | |
# code: String | |
# } | |
# }, { | |
# books: { | |
# code: '123' | |
# } | |
# }) | |
# puts test_case.books.code # 123 | |
# test_case = ShapedStruct.from_hash({ | |
# books: { | |
# codes: [String, Integer] | |
# } | |
# }, { | |
# books: { | |
# codes: ['123', 1234] | |
# } | |
# }) | |
# a = test_case.books.codes | |
# puts a[0] # 123 | |
# test_case = ShapedStruct.from_hash({ | |
# books: { | |
# codes: [String, Integer, { | |
# description: String | |
# }] | |
# } | |
# }, { | |
# books: { | |
# codes: ['123', 1234, { | |
# description: 'some type' | |
# }] | |
# } | |
# }) | |
# puts test_case.books.codes[2].description # some type |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment