Skip to content

RobertDober/l42_map

Repository files navigation

Issue Count CI Coverage Status Gem Version Gem Downloads

l42_map

Immutable OpenStruct On Steroids, combining Hash and OpenStruct semantics

How does it work?

Let us speculate about that:

Context Polluting the global namespace, or not?

Given that we included l42/map

    require 'l42/map'

Then the global namespace is not polluted

  expect {
    Map
  }.to raise_error(NameError, "uninitialized constant Map")

But we can access it inside the namespace

  expect {
    L42::Map
  }.not_to raise_error

In order to access Map inside the global namespace one needs to do

    require 'l42/map/global'

Context Setup

Given instance of L42::Map

  let(:empty) { L42::Map.new }
  let(:single) { L42::Map.new(a: 1) }
  let(:map) { L42::Map.new(a: 10, b: 20, c: 30) }

Context The OpenStruct API

Then they can be accessed by name

  expect(single.a).to eq(1)
  expect(map.a).to eq(10)
  expect(map.c).to eq(30)

And we can use the [] syntax

  expect(empty[:a]).to be_nil
  expect(single[:a]).to eq(1)
  expect(map[:b]).to eq(20)

Context Extensions to the OpenStruct API

And we can use fetch

  expect {
    empty.fetch(:a)
  }.to raise_error(KeyError)

  expect(empty.fetch(:a, 42)).to eq(42)
  expect(empty.fetch(:a) { 43 }).to eq(43)

  expect(single.fetch(:a)).to eq(1)

And we can merge into new instances

  clone = empty.merge(x: "x")
  expect(clone.x).to eq("x")
  expect(empty).to be_empty

Context with_default

Given a Map instance with with_default

  let(:wd) { L42::Map.new.with_default(42) }

Then any access will yield 42

    expect(wd.some_value).to eq(42)

And the default value is passed on to clones

    also_wd = wd.merge(a: 43)
    expect(wd.a).to eq(42)
    expect(also_wd.a).to eq(43)
    expect(also_wd.b).to eq(42)

Context Hash methods

And our instance behaves much like a Hash

    expect(map.slice(:a, :c)).to eq(L42::Map.new(a: 10, c: 30))

Additional methods

And we can remove keys

    expect(map.without(:a, :b)).to eq(L42::Map.new(c: 30))

Context Enumerable

And we can iterate

    result = map.inject(0) { |s, (_k, v)| s + v }
    expect(result).to eq(60)

Context Pattern Matching

And of course it behaves like a Hash in pattern matching

    map => c:
    expect(c).to eq(30)
    expect {
      map => a:, b:, **nil
    }.to raise_error(NoMatchingPatternError) # sic, it is indeed a PatternMatchingError

LICENSE

Copyright 2022] Robert Dober robert.dober@gmail.com,

Apache-2.0 c.f LICENSE

About

An Immutable OpenStruct on Steroids

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages