In Ruby,
OpenStruct
is a data structure,
similar to Hash
.
With this,
we can define
attributes with values
(key value pairs).
OpenStruct
is a class
which uses ruby’s metaprogramming for its implementation,
since its not part of the ruby core,
we have to require ostruct
library which defines it.
> require "ostruct"
Usage
Here is how we use it and other important methods
> user = OpenStruct.new(name: "Sandip")
> user.age = 200
> user.name # => "Sandip"
> user[:name] # => "Sandip"
> user["name"] # => "Sandip"
> user.age # => 200, same as [:age] or ["age"]
> user.to_h # => { :name => "Sandip", :age => 200 }
delete_field
> user
# => #<OpenStruct name="Sandip", age=200>
> user.delete_field(:age) # alternatively use String i.e. "age"
> user
# => #<OpenStruct name="Sandip">
eql?
OpenStruct will be equal when the item that it is matching against is OpenStruct and has same attributes.
To compare objects OpenStruct
uses hash
method,
the hash is computed from the attributes,
hence two OpenStructs with same attributes will have same hash.
> user_1 = OpenStruct.new(name: "Sandip", age: 200)
> user_2 = OpenStruct.new("name" => "Sandip", :age => "200")
> user_1.eql? user_2 # => false
> user_2.age = 200
> user_1.eql? user_2 # => true
> user_1 == user_2 # => true
> user_1.hash # => 3725528521673775168
> user_2.hash # => 3725528521673775168
> user_1.eql? 3725528521673775168 # => false
dig
> office_address = OpenStruct.new(city: "Pune", country: "IN")
> addresses = OpenStruct.new(home: nil, office: office_address)
> user = OpenStruct.new(name: "Sandip", addresses: addresses)
> user.dig(:addresses, :home, :city) # => nil
> user.dig(:addresses, "office", :city) # => "Pune"
each_pair
Yields attributes as symbols in key value pair, returns enumerator if no block is given.
> user = OpenStruct.new(name: "Sandip", age: 200)
> user.each_pair { |k, v| print "#{k} => #{v}\n" }
# => name => Sandip
# => age => 200
> user.each_pair.with_index { |(k, v), i| print "#{i}: #{k} => #{v}\n" }
# => 0: name => Sandip
# => 1: age => 200
Lazy loading methods
For memory optimization and efficiency, the methods are defined based on certain actions.
> vehicle = OpenStruct.new(make: "TATA", year: 2020)
> vehicle.singleton_methods # => []
> vehicle.make # => TATA
> vehicle.singleton_methods # => [ make, make=(x) ]
Use case
Here’s one use case from Ruby on Rails app best suited for OpenStruct
.
# app/models/user.rb
class User
# schema: name, email
end
# app/representers/user.rb
class UserRepresenter
attr_accessor :user
def initialize(user = nil)
@user = user || default_user
end
def attributes
{ name: user.name, email: user.email }
end
private
def default_user
OpenStruct.new(name: "Anonymous", email: "default@example.com")
end
end
> UserRepresenter.new(user).attributes
# => { name: "Sophie Brown", email: "sophie@example.com"}
> UserRepresenter.new.attributes
# => { name: "Anonymous", email: "default@example.com"}
That’s all folks! Thanks for reading 😎