# Hash to OpenStruct (#81)

**URL:** <https://www.ruby-forum.com/t/hash-to-openstruct-81/95669>\
**Category:** Ruby\
**Created:** [June 8, 2006, 3:24pm UTC](https://www.ruby-forum.com/t/hash-to-openstruct-81/95669 "2006-06-08T15:24:11Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [June 8, 2006, 3:24pm UTC](https://www.ruby-forum.com/t/hash-to-openstruct-81/95669/1 "2006-06-08T15:24:11Z")

</div>

The solutions for this quiz are bite size and filled-to-bursting with  
clever  
tricks, so let’s check out several. First, here’s the easiest way I saw  
to not  
solve the problem, by Ilmari H.:

```
class Hash
 def method_missing(mn,*a)
   mn = mn.to_s
   if mn =~ /=$/
     super if a.size > 1
     self[mn[0...-1]] = a[0]
   else
     super unless has_key?(mn) and a.empty?
     self[mn]
   end
 end
end

```

Ilmari doesn’t bother fiddling with imports here, deciding instead to  
just  
implement an OpenStructish interface directly in Hash. Any method call  
Hash  
doesn’t recognize will be sent to this method\_missing() callback.  
Inside the  
method, Ilmari checks to see if the call ended with an equals sign and  
had  
exactly one argument. If so, the assignment is made. Otherwise, if a  
matching  
key can be found, it is fetched and returned.

Of course, this kind of wholesale modification of Hash is quite  
dangerous. If  
some code is counting on the normal failure chain of an unknown method  
called on  
Hash, this code could easily break it.

To get away from that, we’re going to need to build the converters, as  
Jacob  
Fugal does here:

```
require "yaml"
require "ostruct"

class Object
 def to_openstruct
   self
 end
end

class Array
 def to_openstruct
   map{ |el| el.to_openstruct }
 end
end

class Hash
 def to_openstruct
   mapped = {}
   each{ |key,value| mapped[key] = value.to_openstruct }
   OpenStruct.new(mapped)
 end
end

module YAML
 def self.load_openstruct(source)
   self.load(source).to_openstruct
 end
end

p YAML.load_openstruct(File.read("sample.yml"))

```

This was a popular style of solution. People found ways to shorten it  
quite a  
bit, but it’s easy to see the goal with this one. Any normal Object is  
given a  
to\_openstruct() method that has no effect. Array’s version calls  
to\_openstruct() on each member and Hash passes the call down to each  
value.  
Finally, a method is added to YAML that does the load(), then starts the  
chain  
reaction at the top of the constructed object tree. This call gets  
passed down  
by Arrays and Hashes as we just saw and converts most Hashes to  
OpenStructs.  
(It doesn’t convert Hashes in the instance variables of custom objects  
that have  
been serialized.)

Now MenTaLguY threw a big monkey wrench into solutions like this when he  
posted  
this alternate test data:

```
---
&verily
lemurs:
  unite: *verily
  beneath:
    - patagonian
    - bread
    - products
thusly: [1, 2, 3, 4]

```

As you can see, YAML is allowed to have nested data structures. When I  
run  
Jacob’s solution on this I get the odd error:

```
Illegal instruction

```

One of MenTaLguY’s own solutions to this was to use some lazy evaluation  
and  
memoization:

```
require 'ostruct'
require 'lazy'

def hashes_to_openstructs( obj, memo={} )
  return obj unless Hash === obj
  memo[obj.object_id] ||= promise {
    OpenStruct.new( Hash[
      *obj.inject( [] ) { |a, (k, v)|
        a.push k, hashes_to_openstructs( v, memo )
      }
    ] )
  }
end

```

This is a recursive solution like Jacob’s, though trimmed down and less  
complete. The trick here is that instead of walking the whole object  
tree  
immediately, the code just promises to do it when needed. A promise()  
is just a  
magic object that springs to life when it is actually used for the first  
time  
(constructing itself by calling the block).

The other trick of this method is the memoization. Using the memo Hash  
and the  
||= operator, the conversion process caches each object by object\_id(). Any  
future calls for the same object, just get the already constructed  
version  
straight from the cache.

Then TRANS pointed out a interesting fact, YAML already has to  
understand all  
this recursive data structure stuff, so we really want to let it do all  
the hard  
work and just change what it loads. TRANS poked around in the innards  
of YAML  
and did get a working solution, but why the lucky stiff was drawn into  
the  
challenge and suggested this version:

```
require 'yaml'
require 'ostruct'

class << YAML::DefaultResolver
  alias_method :_node_import, :node_import
  def node_import(node)
    o = _node_import(node)
    o.is_a?(Hash) ? OpenStruct.new(o) : o
  end
end

```

This just overrides a piece of YAML behavior to check if the object just  
loaded  
was a Hash. When it is, it is replaced with an OpenStruct. Simple and  
very  
effective.

Now this YAML solution will convert Hashes inside the instance variables  
of  
objects. That’s probably a bad thing, since those classes likely  
weren’t  
designed with that in mind. You always have to weight the tradeoffs and  
choose  
a solution that will best meet your current needs.

A big thanks to all who couldn’t help but fiddle with Hans’s fun little  
challenge. I don’t believe I’ve ever seen such variation in the  
solutions  
before.

Tomorrow’s quiz is to help Benjohn B. get into shape…

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [June 8, 2006, 7:32pm UTC](https://www.ruby-forum.com/t/hash-to-openstruct-81/95669/2 "2006-06-08T19:32:17Z")

</div>

On Thu, 8 Jun 2006 22:20:44 +0900, Ruby Q. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> A promise() is just a magic object that springs to life when it is actually  
> used for the first time (constructing itself by calling the block).

This is, incidentally, the most concise explanation of promises I’ve  
ever seen. Mind if I borrow it for the lazy.rb rdoc?

-mental

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [June 8, 2006, 7:45pm UTC](https://www.ruby-forum.com/t/hash-to-openstruct-81/95669/3 "2006-06-08T19:45:25Z")

</div>

On Jun 8, 2006, at 12:29 PM, MenTaLguY wrote:

> On Thu, 8 Jun 2006 22:20:44 +0900, Ruby Q.  
> [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > A promise() is just a magic object that springs to life when it is  
> > actually  
> > used for the first time (constructing itself by calling the block).
> 
> This is, incidentally, the most concise explanation of promises  
> I’ve ever seen. Mind if I borrow it for the lazy.rb rdoc?

Not at all.

I’m a huge fan of lazy.rb. 😉

James Edward G. II
