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