Created
October 11, 2019 17:06
-
-
Save ericyd/dd380ae64ee0187104a6683ecea77683 to your computer and use it in GitHub Desktop.
Ostructify: Recursively create OpenStructs of your hash or array or hashes
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
require 'ostruct' | |
# Recursively return OpenStructs from a hash or nested hash | |
# Handles array of hashes too | |
# Returns primitives when they are neither Hash nor Array | |
def ostructify(thing, camelize: false, snakify: false) | |
if thing.class == Hash | |
thing.reduce(OpenStruct.new) do |ostruct, (key, value)| | |
key = camelize ? camelize(key) : key | |
key = snakify ? snakify(key) : key | |
ostruct[key] = ostructify(value, camelize: camelize, snakify: snakify) | |
ostruct | |
end | |
elsif thing.class == Array | |
thing.map { |element| ostructify(element, camelize: camelize, snakify: snakify) } | |
else | |
thing | |
end | |
end | |
# less robust than the ActiveSupport version but fine for our purposes | |
# camelize(:my_value) # => "myValue" | |
# camelize('my_value') # => "myValue" | |
# camelize('my_value/path') # => "myValue/path" | |
# camelize('my_value::module') # => "myValue::module" | |
def camelize(stringable) | |
string = stringable.to_s | |
string = string.split('_') | |
string.first + string.drop(1).map(&:capitalize).join('') | |
end | |
# Modified from ActiveSupport: | |
# https://github.com/rails/rails/blob/3523b9d0096ec0229e9db336f0fb042b17c73978/activesupport/lib/active_support/inflector/methods.rb#L92-L101 | |
# snakify(:myValue) # => "my_value" | |
# snakify('myValue') # => "my_value" | |
# snakify('myValue/path') # => "my_value/path" | |
# snakify('myValue::module') # => "my_value::module" | |
def snakify(stringable) | |
stringable.to_s | |
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') | |
.gsub(/([a-z\d])([A-Z])/, '\1_\2') | |
.tr("-", "_") | |
.downcase | |
end | |
# -------------------------- | |
# Tests | |
# -------------------------- | |
# Arrays | |
initial = [1, 2, 3] | |
actual = ostructify(initial) | |
expected = [1, 2, 3] | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
initial = [[1], [2], [3]] | |
actual = ostructify(initial) | |
expected = [[1], [2], [3]] | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
initial = [[1], [2], [{h: 1, a: [{id: 2}]}]] | |
actual = ostructify(initial) | |
expected = [[1], [2], [OpenStruct.new(h: 1, a: [OpenStruct.new(id: 2)])]] | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# Hashes | |
# hash with simple values | |
initial = {h: 1, 'h2' => 2} | |
actual = ostructify(initial) | |
expected = OpenStruct.new(h: 1, 'h2' => 2) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# hash with value that is array of hashes | |
initial = {h: 1, a: [ {t: 2}]} | |
actual = ostructify(initial) | |
expected = OpenStruct.new(h: 1, a: [ OpenStruct.new(t: 2)]) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# hash with value that is array of hashes with value that is array of strings | |
initial = {h: 1, a: [ {t: 2, aa: ['one']}]} | |
actual = ostructify(initial) | |
expected = OpenStruct.new(h: 1, a: [ OpenStruct.new(t: 2, aa: ['one'])]) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# hash with value that is array of hashes with value that is array of hashes | |
initial = {h: 1, a: [ {t: 2, aa: [{tt: 3}]}]} | |
actual = ostructify(initial) | |
expected = OpenStruct.new(h: 1, a: [ OpenStruct.new(t: 2, aa: [OpenStruct.new(tt: 3)])]) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# hash with some keys that are strings | |
initial = {'h': 1, 'a': [ {t: 2}]} | |
actual = ostructify(initial) | |
expected = OpenStruct.new('h': 1, 'a': [ OpenStruct.new(t: 2)]) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# integers | |
actual = ostructify(1) | |
expected = 1 | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# chars | |
actual = ostructify('a') | |
expected = 'a' | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# classes | |
actual = ostructify(Object) | |
expected = Object | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# Camelize | |
initial = { my_key: 1, my_other_key: 2} | |
actual = ostructify(initial, camelize: true) | |
expected = OpenStruct.new(myKey: 1, myOtherKey: 2) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
initial = { 'my_key' => 1, 'my_other_key' => 2} | |
actual = ostructify(initial, camelize: true) | |
expected = OpenStruct.new('myKey' => 1, 'myOtherKey' => 2) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
initial = { 'my_key' => 1, my_other_key: [ { my_nested_key: 3 }]} | |
actual = ostructify(initial, camelize: true) | |
expected = OpenStruct.new('myKey' => 1, myOtherKey: [ OpenStruct.new(myNestedKey: 3)]) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
# Snakify | |
initial = { myKey: 1, myOtherKey: 2} | |
actual = ostructify(initial, snakify: true) | |
expected = OpenStruct.new(my_key: 1, my_other_key: 2) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
initial = { 'myKey' => 1, 'myOtherKey' => 2} | |
actual = ostructify(initial, snakify: true) | |
expected = OpenStruct.new('my_key' => 1, 'my_other_key' => 2) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end | |
initial = { 'myKey' => 1, myOtherKey: [ { myNestedKey: 3 }]} | |
actual = ostructify(initial, snakify: true) | |
expected = OpenStruct.new('my_key' => 1, my_other_key: [ OpenStruct.new(my_nested_key: 3)]) | |
if actual == expected | |
puts 'ok' | |
else | |
raise "not equal. Expected: #{expected.inspect}, Actual: #{actual.inspect}" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment