Skip to content

Instantly share code, notes, and snippets.

@jiahaog
Created February 2, 2017 08:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jiahaog/2d8fb12bb5d151f25387377eb310ec6a to your computer and use it in GitHub Desktop.
Save jiahaog/2d8fb12bb5d151f25387377eb310ec6a to your computer and use it in GitHub Desktop.
Shaped Ruby Struct
# 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