DEV Community 👩‍💻👨‍💻

Burdette Lamar
Burdette Lamar

Posted on

All About Ruby OpenStruct

What's an OpenStruct?

From the documentation for Ruby's OpenStruct class:

An OpenStruct is a data structure, similar to a Hash, that allows the definition of arbitrary attributes with their accompanying values.

It's much like a Struct, but with more bells and whistles: you can, for example, add and delete attributes.

To demonstrate OpenStruct, I'm going to use Ruby and the Ruby Interactive Shell, irb, beginning with their versions:

p `ruby --version`.chomp
"ruby 2.6.3p62 (2019-04-16 revision 67580) [x64-mingw32]"
p `irb --version`.chomp
"irb 1.0.0 (2018-12-18)"

Read on:

The Basics

Make OpenStruct available (and show its superclass):

require 'ostruct'
p OpenStruct.superclass
Object

Create an OpenStruct object, then display it (method :to_s is an alias of method :inspect):

person = OpenStruct.new(:name => 'Burdette Lamar', :city => 'Houston', :state => 'TX')
puts person.to_s
#<OpenStruct name="Burdette Lamar", city="Houston", state="TX">

An undefined attribute returns nil, and does not raise an exception:

p person.country
nil

Add an attribute, by assigning to it:

person.country = 'USA'
p person.country
"USA"

Remove an attribute (method :delete_field returns the deleted value):

p person.delete_field(:country)
"USA"
p person
#<OpenStruct name="Burdette Lamar", city="Houston", state="TX">

Setting a attribute's value to nil does not delete the attribute.

person.state = nil
p person
#<OpenStruct name="Burdette Lamar", city="Houston", state=nil>

Accessing Attributes

Create an attribute with an accessor method, or method :[]= with an augument (symbol or string):

p person.hair_color = :gray
:gray
p person[:eye_color] = :brown
:brown
p person['hair_style'] = :ponytail
:ponytail

Get an attribute's value with its accessor method or method :[] with an argument:

p person.hair_color
:gray
p person[:eye_color]
:brown
p person['hair_style']
:ponytail

Change an attribute's value with its accessor method or method :[]= and an argument:

p person.hair_color = :silver
:silver
p person[:eye_color] = :dark_brown
:dark_brown
p person['hair_style'] = :tie_back
:tie_back

Weird names are allowed:

p person[:'Weight (pounds)'] = 191
191
p person[:'Weight (pounds)']
191
p person['Weight (pounds)']
191
p person.send(:'Weight (pounds)')
191

Get all attributes as a hash.

p person.to_h
{:name=>"Burdette Lamar", :city=>"Houston", :state=>nil, :hair_color=>:silver, :eye_color=>:dark_brown, :hair_style=>:tie_back, :"Weight (pounds)"=>191}

Process all attributes into a hash:

p person.to_h {|name, value| [name.to_s, value.to_s] }
{"name"=>"Burdette Lamar", "city"=>"Houston", "state"=>"", "hair_color"=>"silver", "eye_color"=>"dark_brown", "hair_style"=>"tie_back", "Weight (pounds)"=>"191"}

A new OpenStruct instance has no methods:

person = OpenStruct.new(:name => 'Burdette Lamar', :city => 'Houston', :state => 'TX')
p person.methods(false)
[]

Accessing an attribute with method :[] does not create accessor methods:

p person[:name]
"Burdette Lamar"
p person['name']
"Burdette Lamar"
p person.methods(false)
[]

But accessing it with method :[]= does create accessor methods:

person[:name] = 'Lamar, Burdette'
p person.methods(false)
[:name, :name=]

As does accessing an attribute with either of its accessor methods:

p person.state
"TX"
p person.methods(false)
[:state=, :name=, :name, :state]
person.city = 'Boston'
p person.methods(false)
[:city=, :state=, :name=, :name, :state, :city]

Dig for content in objects that support method :dig:

MyStruct = Struct.new(:foo)
ostruct = OpenStruct.new(
        :bar => MyStruct.new(
              Array.new([
                                Hash.new(
                                      :baz => 'Bag'
                                )
                          ])
        )
  )
p ostruct.dig(:bar, :foo, 0, :baz)
{:baz=>"Bag"}

Freeze an OpenStruct object:

ostruct = OpenStruct.new(:a => 0)
p ostruct.frozen?
false
ostruct.freeze
p ostruct.frozen?
true

For the Marshal library:

ostruct = OpenStruct.new(:a => 0, :b => 1)
p ostruct
#<OpenStruct a=0, b=1>
x = ostruct.marshal_dump
p x
{:a=>0, :b=>1}
ostruct.marshal_load(x)
p ostruct
#<OpenStruct a=0, b=1>

Iteration

Iterate with method :each_pair:

person.each_pair do |name, value|
  p [name, value]
end
[:name, "Lamar, Burdette"]
[:city, "Boston"]
[:state, "TX"]

Get an enumerator for the attributes.:

enum = person.each_pair
p enum
#<Enumerator: #<OpenStruct name="Lamar, Burdette", city="Boston", state="TX">:each_pair>

More Ways to Create an OpenStruct Object

Create an OpenStruct from a Struct:

Person = Struct.new(:name, :city, :state)
struct = Person.new('Burdette Lamar', 'Houston', 'TX')
ostruct = OpenStruct.new(struct)
p ostruct
#<OpenStruct name="Burdette Lamar", city="Houston", state="TX">

Create an OpenStruct from another OpenStruct:

another_ostruct = OpenStruct.new(ostruct)
p another_ostruct
#<OpenStruct name="Burdette Lamar", city="Houston", state="TX">

Create an empty OpenStruct:

ostruct = OpenStruct.new
p ostruct
#<OpenStruct>

Equality

A couple of OpenStruct instances to compare:

a = OpenStruct.new(:a => 0, :b => 1)
b = OpenStruct.new(:b => 1, :a => 0)
p a
#<OpenStruct a=0, b=1>
p b
#<OpenStruct b=1, a=0>

Methods :== and :eql? test equality.

p a == b
true
p a.eql?(b)
true

Method :hash returns an integer value, not a hash:

p a.hash
664573704
p b.hash
664573704

More

Top comments (0)

Stop sifting through your feed.

Find the content you want to see.

Change your feed algorithm by adjusting your experience level and give weights to the tags you follow.