DEV Community

christine
christine

Posted on • Originally published at christine-seeman.com on

Introduction to OpenStruct

Intro to OpenStruct


(Cause every article needs a little gif)

Last week I had a task to start working with some CSV data pulled in from the database. There was already existing code working with the data, so I needed to make sure whatever changes were applied, that they were backwards compatible. The data was an csv representation of an array. The current code was hard coded to go through the array in a fairly rigid process, so to add some flexibility, and allow better access the data in a controller, it was suggested to use OpenStruct.

First a quick overview on what is OpenStruct, it is a data structure that is similar to a hashe. It applies arbitrary attributes with accompanying values. It seems like magic, but it is just Ruby’s metaprogramming and it defines the methods on the classes themselves. A basic example of using OpenStruct on an object might be:

require "ostruct"

candy = OpenStruct.new
candy.name = "Snickers"
candy.type = "Chocolate"
candy.amount = 5
Enter fullscreen mode Exit fullscreen mode

Then if I needed to call and figure out the candy name, or type, I can call a method on the object itself (instead of having to do the Ruby hash way which would’ve been candy[:name] # => “Snickers”)

candy.name
> "Snickers"
candy.type
> "Chocolate"
Enter fullscreen mode Exit fullscreen mode

So the code went from using:

@csv = CSV.parse(parsed_text)
Enter fullscreen mode Exit fullscreen mode

To the following with OpenStruct:

@csv = CSV.parse(parsed_text, headers: true).map { |row| OpenStruct.new(row.to_h) }
Enter fullscreen mode Exit fullscreen mode

Making use of the csv data now went from:

@list = @csv[1..-1] || []
@count = @list.count
Enter fullscreen mode Exit fullscreen mode

And now:

@list = @csv.map { |row| row.to_h.values } || []
@count = @csv.count
Enter fullscreen mode Exit fullscreen mode

Accessing the values was much easier, but it is important to note with OpenStruct:

  1. It is not performant, which wasn’t something that needed to be kept in mind for this piece of code but if it is something that you do need to keep in mind, go for a hash or struct.
  2. OpenStruct != Struct , struct is similar to a custom created class that you don’t need to initialize or have any attr for. Struct is more performant, OpenStruct more flexible. Struct has to have the attributes defined, while it is dynamic with OpenStruct. For the ex. we have name, type and amount but couldn’t add on later an listing for populatarity.
candy = Struct.new(:name, :type, :amount)
snickers = candy.new("Snickers", "Chocolate", 5)

snickers.type 
> "Snickers"
Enter fullscreen mode Exit fullscreen mode

Top comments (5)

Collapse
 
derekjhopper profile image
Derek Hopper

Great post. I'll sometimes use OpenStruct in tests if I need a more complex mock. I think it reads better than some of the other mechanisms and you can do a lot with it. Recently, we needed to mock an API response which comes back as an object, so I used OpenStruct to create just the parts that were needed.

I know it's easy to reach for gems and libraries to help us, but sometimes you can get by just fine with PORO and Ruby standard library.

Collapse
 
cseeman profile image
christine

Awesome use-case, I might give that a try out too.

Collapse
 
stargator profile image
Stargator

Why is it called OpenStruct? I see implementations of it in Ruby and Python. But I wonder if there is an open source specification to allow it to be defined in other languages?

Collapse
 
cseeman profile image
christine

From my understanding of it pertaining to Ruby, that it is similar to a Struct data structure type, but more open and flexible. So to me the naming works for that definition, OpenStruct. I am not totally sure if it it is open source specification for it. But you can always check out the Ruby source code definition for it, which would get you on your way for programming it in another language. github.com/ruby/ruby/blob/trunk/li...

Collapse
 
cseeman profile image
christine

Awesome thanks! I'll check it out.