Published in


OpenStruct in Ruby

In this article, we’re going to explore the following topics:

  • the OpenStruct class
  • data structure: creation and manipulation
  • OpenStruct and lazy loading
  • data structures behind the scene

Feel free to read the Struct in Ruby article if you are unfamiliar with data structures in Ruby. This will give you a better comprehension of the technical notions of this article.

Before to start

Please allow me to introduce here the platform that helped me to learn a big part of my knowledge about Ruby. Indeed, Pluralsight is an amazing platform.

With 50+ courses that cover various topics on Ruby and Ruby on Rails, it’s the best way to take your knowledge to the next level!

Try it for free 👇😉

Thank you for your time!

The OpenStruct class

The OpenStruct class allows to create flexible data structures. Indeed, the structure doesn’t need to provide a rigid list of members as it doesn’t define any structure type but data structures that you fill in afterwards. It provides a pair of getter/setter methods for each attribute. This is similar to attr_accessor method for classes.

Feel free to read Attributes in Ruby if you are unfamiliar with the attr_* methods in Ruby.

This class is part of the Ruby’s Standard Library (a.k.a stdlib). Let’s have a look to OpenStruct ancestor chain

irb> require 'ostruct'
=> true
irb> OpenStruct.ancestors
=> [OpenStruct, Object, Kernel, BasicObject]

As it’s not part of the Ruby Core, we have to require the ostruct library which contains the OpenStruct class definition. OpenStruct inherits from Object— which is the default parent class in Ruby. Note that it doesn’t include any particular module. Indeed, unlike the Struct class, it doesn’t include the Enumerable module.

Feel free to read the Ruby Object Model article if you are unfamiliar with the Object class and the ancestor chain.

Feel free to read the The Enumerable module in Ruby: Part I article if you are unfamiliar with the Enumerable module.

Data structure: creation and manipulation

The OpenStruct::new is in charge of creating new data structure. This method accepts a Hash, a Struct or an OpenStruct as parameter

Firstly, we instantiate a new OpenStruct object with one attribute called ram and we store this object in the computer variable. Then we access ram attribute by 3 different ways:

  • computer.ram: the ram accessor method
  • computer[:ram]: OpenStruct#[] method with a symbol key
  • computer['ram']: OpenStruct#[] method with a string key

Next, we define the screens attribute. There is 3 ways to define a new attribute or to modify the value of an existing one:

  • computer.screens=: the screens= accessor method
  • computer[:screens]=: OpenStruct#[]= method with a symbol key
  • computer['screens']=: OpenStruct#[]= method with a string key

Finally, we access the value of the screens attribute the same way as the ram attribute. Unlike Struct that defines a structure type with a rigid attributes list, the OpenStruct class defines a new data structure that can add attributes on-the-fly — like the screen attribute in the above example.

OpenStruct and lazy loading

In order to save memory and speed up the access to an attribute, the accessor methods of an attribute are lazy loaded at certain points. This means that the methods are defined only when a set of defined actions are triggered. This allows your program to only define the minimum amount of required methods to make your data structure works

After the OpenStruct initialization, we see that computer.ram and computer.ram= accessor methods are not defined yet.

Indeed, we can see that after a call to computer.ram, computer.ram and computer.ram= methods are now defined.

Note that a call to computer[:ram] doesn’t define the accessor methods.

Then we set a value to the :screens attribute via the OpenStruct#[]= method. After this method call, the computer.screens and computer.screens= accessors are defined. To recap, accessor methods are defined only when:

  • an accessor method of an existing (or not) attribute is called for the first time.
  • the OpenStruct#[]= method is called for the first time — computer[:screens] = 2 for example.

Data structures behind the scene

Now that we are more familiar with the OpenStruct class, let’s dive into what happens behind the scene when we create and manipulate an OpenStruct object. Each data structure from an OpenStruct defines an internal hash that contains a mapping table for each attribute and value. This container is commonly called the table

Note that each attribute name is converted to a Symbol before to be included into the table. Now let’s have a look to the evolution of the computer ‘s table

Here, the table of the computer’s data structure is { ram: "4GB" }. Now let’s add the screens attribute to the computer object

At this point the content of the table is { ram: "4GB", screens: 2 }. What happens when an attribute that doesn’t exist is called? For example, a call to the computer.cores= setter method

When a method is not defined within the entire ancestor chain of a given object then Ruby automatically calls the first defined method_missing Hook Method in this ancestor chain. OpenStruct defines its own version of method_missing. So when computer.cores = 2 is called then the OpenStruct#method_missing method is called behind the scene. Let’s detail the execution flow of this method when the computer.cores = 2 is called:

  • 1/ getter and setter methods are defined and the key passed as parameter is converted into a Symbol:cores
  • 2/ the value is inserted into the internal hash using this key — @table[:cores] = 2
  • 3/ the value is returned — 2

This execution flow is slight the same for a call to computer[:cores] = 2 and computer.cores.

Voilà !

Thank you for taking the time to read this post :-)

Feel free to 👏 and share this article if it has been useful for you. 🚀

Here is a link to my last article: Struct in Ruby.




E-Learning platform for Ruby and Ruby on Rails

Recommended from Medium

Lets Deploy our web-server from Git2Docker

A New Journey Has Started!

Hardhat vs Truffle

GraalVM for Java

5 Reasons your Product needs a Vision

Ulord Project Progress (From June 11 to June 17, 2020)

Cosmic JS vs. WordPress

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Tech - RubyCademy

Tech - RubyCademy

More from Medium

Rack in questions and answers

Creating your own backend using Ruby and Active Record

How to Use the Destroy and Delete Methods in Rails

Passing a Nested Array of Objects to a Controller to Update Associated Records in Rails