Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

immutability

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

immutability

  • 0.0.5
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Immutability

Gem Version Build Status Dependency Status Code Climate Coverage Inline docs

Makes instances immutable (deeply frozen) and versioned.

Preamble

The project is a clone of the aversion gem by Josep M. Bach with some implementation differencies:

  • it uses ice_nine gem to freeze instances deeply.
  • instead of storing procedures that changed the instance, it stores reference to the previous state and the number of current version.

This approach to object's identity as a sequence of immutable snapshots is heavily inspired by 2009 year's brilliant talk "Are We There Yet?" by Rich Hickey.

Synopsis

Immutable objects without memory:

Include the Immutability module to make the object immutable (deeply frozen).

require "immutability"

class User
  include Immutability

  attr_reader :name, :age

  def initialize(name, age)
    @name = name
    @age  = 44
  end
end

young_andrew = User.new "Andrew", 44
young_andrew.name         #  => "Andrew"
young_andrew.age          #  => 44

# The instance is frozen deeply:
young_andrew.frozen?      #  => true
young_andrew.name.frozen? #  => true
young_andrew.age.frozen?  #  => true

Use update with a block to create a new instance with updated values (other instance values remains the same):

elder_andrew = young_andrew.update { @age = 45 }
elder_andrew.name         #  => "Andrew"
elder_andrew.age          #  => 45

# The instance is frozen deeply:
elder_andrew.frozen?      #  => true
elder_andrew.name.frozen? #  => true
elder_andrew.age.frozen?  #  => true

Immutable objects with memory

Include Immutability.with_memory module to add version and parent:

require "immutability"

class User
  include Immutability.with_memory

  attr_reader :name, :age

  def initialize(name, age)
    @name = name
    @age  = 44
  end
end

young_andrew = User.new "Andrew", 44
young_andrew.name         #  => "Andrew"
young_andrew.age          #  => 44

# The instance is frozen as well:
young_andrew.frozen?      #  => true
young_andrew.name.frozen? #  => true
young_andrew.age.frozen?  #  => true

# Now it is versioned:
young_andrew.version      # => 0
young_andrew.parent       # => nil

The method update stores reference to the #parent and increases #version:

elder_andrew = young_andrew.update { @age = 45 }
elder_andrew.name    #  => "Andrew"
elder_andrew.age     #  => 45

# Version is updated:
elder_andrew.version # => 1
elder_andrew.parent.equal? young_andrew # => true

You can check the previous state of the object using method #at:

# relative from the current version
elder_andrew.at(-2) == nil          # => true
elder_andrew.at(-1) == young_andrew # => true

# at some version in the past
elder_andrew.at(0)  == young_andrew # => true
elder_andrew.at(1)  == elder_andrew # => true
elder_andrew.at(2)  == nil          # => true

This can be used to check whether two instances has a cenancestor:

elder_andrew.at(0) == young_andrew.at(0) # => true

Notice, than no instances in the sequence can be garbage collected (they still refer to each other).

Use #forget_history methods to reset version and free old instances for GC:

reborn_andrew = elder_andrew.forget_history
reborn_andrew.name # => "Andrew"
reborn_andrew.age  # => 45

# History is forgotten
reborn_andrew.version # => 0
reborn_andrew.parent  # => nil

RSpec

be_immutable

Include immutability/rspec and use be_immutable RSpec matcher to check, whether an instance is deeply immutable (with all its variables):

include "immutability/rspec"

describe User, ".new" do
  subject { User.new "Andrew", 44 }

  it { is_expected.to be_immutable }
end

The matcher will pass if both the object and all its variables are immutable at any level of nesting.

frozen_double

Initializers of immutable objects freeze variables deeply. When you use doubles as initializers' arguments, RSpec will complain. To avoid this problem, use frozen_double instead of double:

include "immutability/rspec"

describe User, "#name" do
  subject { User.new(name, 44).name }

  # the name can be used by the initializer safely
  let(:name) { frozen_double :name, to_s: "Andrew" }

  it { is_expected.to eql "Andrew" }
end

The method returns an rspec double with two methods added:

name = frozen_double :name, to_s: "Andrew"
name.frozen?        # => true
name.freeze == name # => true

Installation

Add this line to your application's Gemfile:

# Gemfile
gem "immutability"

Then execute:

bundle

Or add it manually:

gem install immutability

Compatibility

Tested under rubies compatible to MRI 1.9+.

Uses RSpec 3.0+ for testing and hexx-suit for dev/test tools collection.

Contributing

  • Read the STYLEGUIDE
  • Fork the project
  • Create your feature branch (git checkout -b my-new-feature)
  • Add tests for it
  • Run rake mutant or rake exhort to ensure 100% mutant-proof coverage
  • Commit your changes (git commit -am '[UPDATE] Add some feature')
  • Push to the branch (git push origin my-new-feature)
  • Create a new Pull Request

License

See the MIT LICENSE.

FAQs

Package last updated on 14 Oct 2015

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc