With Struct and OpenStruct objects can be created whose structure is determined at runtime.
Their flexibility allows data to be structured without creating equal classes. Especially since the instantiation of Struct objects consumes even less performance compared to classes.
So Structs should be considered.
Whenever data is related to each other or share the same context, it makes sense to also group it into a structure.
For example, geo data consist of latitude and longitude. Linking them together is more than obvious:
Struct.new 'Geolocation', :latitude, :longitude geolocation = Struct::Geolocation.new 53.6, 11.4 # => #<struct Struct::Geolocation latitude=53.6, longitude=11.4> geolocation.latitude # => 53.6 geolocation.longitude # => 11.4
Structs can be created in a variety of ways. Other notations:
# Assignment Geolocation = Struct.new :latitude, :longitude # Inheritance class Geolocation < Struct.new(:latitude, :longitude) end
It is because Struct#new returns a class.
In addition, structures can also contain logic:
Struct.new(:latitude, :longitude) do def east_of_greenwich? longitude.positive? end end
OpenStructs are not as performing as structures, but more flexible. However, they do not belong to the Ruby core:
require 'ostruct' geolocation = OpenStruct.new latitude: 53.6, longitude: 11.4 geolocation.zoom # => nil geolocation.zoom = 14 geolocation.zoom # => 14
On the one hand, an instance can be created without defining the structure, and on the other hand the instance also seems to respond to undefined methods. The reason is that OpenStruct
Is based on a Hash, and #method_missing captures missing method definitions and creates them at runtime.
This can be helpful in quickly mocking experimental objects or test doubles.