Recursive OpenStruct in Ruby with nested Array of Hashes

Hey do you want to have your Hashes with collection/Array of Hashes more object oriented but along with it want it to behave as it was before?. Well you can use my library to do so. I have used the Ruby’s native OStructOStruct Library. RStruct Can be used as your De-Serializer for JSON type data in Postgres.

# This only supports Hashes inside Hashes
class RStruct
  # @author Shiva Bhusal
  # @blog cbabhusal.wordpress.com
  # Recursive OpenStruct
  # #
  def self.new(object)
    if object.is_a?(Hash)
      object = object.clone
      object.each do |key, value|
        object[key] = new(value)
      end
      OpenStruct.new(object)
    else
      object
    end
  end
end

Example:

> a = {l1: {l2: {name: 'shiva', name_is_valid?: true}}}
 => {:l1=>{:l2=>{:name=>"shiva", :name_is_valid?=>true}}}
> aa = RStruct.new(a)
 => #<OpenStruct l1=#<OpenStruct l2=#<OpenStruct name="shiva">>> 
> aa.l1.l2.name
 => "shiva"
> aa.l1.l2.name_is_valid?
 => true

For Arrays of Hashes inside the Hashes

class RStruct
  # @author Shiva Bhusal
  # @blog cbabhusal.wordpress.com
  # Recursive OpenStruct
  # #
  def self.new(object)
    if object.is_a?(Hash)
      object = object.clone
      object.each do |key, value|
        object[key] = new(value)
      end
      OpenStruct.new(object)
    elsif object.is_a?(Array)
      object.map do |item|
        new(item)
      end
    else
      object
    end
  end
end

For usage with Rails’s AController::Parameter

class RStruct
  # @author Shiva Bhusal
  # @blog cbabhusal.wordpress.com
  # Recursive OpenStruct
  # #
  def self.new(object)
    if object.is_a?(Hash)
      object = object.clone
      object.each do |key, value|
        new_value = value.to_hash.with_indifferent_access rescue value
        object[key] = new(new_value)
      end
      OpenStruct.new(object)
    elsif object.is_a?(Array)
      object.map do |item|
        new(item)
      end
    else
      object
    end
  end
end

Usages

> data = {key1: [{sub_key1: 'value1'}]}
 => {:key1=>[{:sub_key1=>"value1"}]}
 > structured_data = RStruct.new(data)
 => #<OpenStruct key1=[#<OpenStruct sub_key1="value1">]>
 > structured_data.key1.first.sub_key1
 => "value1"

 

And if you are trying to re-struct the data from Stripe Objects then you may want to modify the code like

if object.is_a?(Hash)
  object = object.clone
  object.each do |key, value|
    new_value = value.to_h rescue value
    object[key] = new(new_value)
  end
  OpenStruct.new(object)

2 thoughts on “Recursive OpenStruct in Ruby with nested Array of Hashes

  1. I just tried out and this way could be more efficient? but it’s shorter at least. 😀


    class Rstruct
    def self.new(hash)
    hash.each do |x,v|
    if v.is_a? Hash
    hash[x] = self.new(v)
    elsif v.is_a? Array
    v.each do |vv|
    hash[x] = []
    hash[x] << self.new(vv)
    end
    end
    end
    OpenStruct.new(hash)
    end
    end

    Like

Leave a comment