0.11
No release in over 3 years
Easily create value objects without the pain of Ruby's Struct (or its setters)
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
 Dependencies

Development

 Project Readme

ImmutableStruct¶ ↑

Build Status

Creates struct-like classes (that can build value objects) that do not have setters and also have better constructors than Ruby's built-in Struct.

This is highly useful for creating presenters, non-database-related models, or other quick and dirty classes in your application. Instead of using a Hash or OpenStruct, you can create a bit more clarity around your types by using ImmutableStruct, which is almost as convienient.

Install¶ ↑

Add to your Gemfile:

gem 'immutable-struct'

Then install:

bundle install

If not using bundler, just use RubyGems:

gem install immutable-struct

To use¶ ↑

Person = ImmutableStruct.new(:name, :age, :job, :active?, [:addresses]) do
  def minor?
    age < 18
  end
end

p = Person.new(name: "Dave",   # name will be 'Dave'
               age: 40,        # age will be 40
                               # job is omitted, so will be nil
               active: true)   # active and active? will be true
                               # addresses is omitted, but since we've selected
                               # Array coercion, it'll be []
p.name      # => "Dave"
p.age       # => 40
p.active?   # => true
p.minor?    # => false
p.addresses # => []

p2 = Person.new(name: "Dave", age: 40, active: true)

p == p2     # => true
p.eql?(p2)  # => true

SimilarPerson = ImmutableStruct.new(:name, :age, :job, :active?, [:addresses])

sp = SimilarPerson.new(name: "Dave", age: 40, active: true)

p == sp     # => false         # Different class leads to inequality

new_person = p.merge(name: "Other Dave", age: 41) # returns a new object with merged attributes
new_person.name    # => "Other Dave"
new_person.age     # => 41
new_person.active? # => true

You can coerce values into struct types by using the from method. This is similar to Ruby's conversion functions, e.g. Integer(“1”).

dave = Person.from(p)
dave.equal?(p) # => true (object equality)

daveish = Person.from(dave.to_h)
daveish.equal?(dave) # => false
daveish == dave      # => true

You can treat the interior of the block as a normal class definition with the exception of setting constants. Use const_set to scope constants as-expected.

Point = ImmutableStruct.new(:x, :y) do
  const_set(:ZERO, 0)
  ONE_HUNDRED = 100
end
Point::ZERO # => 0
::ONE_HUNDRED # => 100
::ZERO # => NameError: uninitialized constant ZERO