
Security News
Insecure Agents Podcast: Certified Patches, Supply Chain Security, and AI Agents
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.
immutability
Advanced tools
Makes instances immutable (deeply frozen) and versioned.
The project is a clone of the aversion gem by Josep M. Bach with some implementation differencies:
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.
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
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
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.
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
Add this line to your application's Gemfile:
# Gemfile
gem "immutability"
Then execute:
bundle
Or add it manually:
gem install immutability
Tested under rubies compatible to MRI 1.9+.
Uses RSpec 3.0+ for testing and hexx-suit for dev/test tools collection.
git checkout -b my-new-feature)rake mutant or rake exhort to ensure 100% mutant-proof coveragegit commit -am '[UPDATE] Add some feature')git push origin my-new-feature)See the MIT LICENSE.
FAQs
Unknown package
We found that immutability demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Security News
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.

Security News
Tailwind Labs laid off 75% of its engineering team after revenue dropped 80%, as LLMs redirect traffic away from documentation where developers discover paid products.

Security News
The planned feature introduces a review step before releases go live, following the Shai-Hulud attacks and a rocky migration off classic tokens that disrupted maintainer workflows.