Issue Count Gem Version CI Coverage Status

Lab42::DiggyMethods

Access (nested) hash values by means of method chaining

Usage

gem install lab42_diggy_methods

With bundler

  gem 'lab42_diggy_methods'

In your code

require 'lab42/diggy_methods'

So what does it do?

Well let us speculate about it to find out:

Context: Diggy function

An extension to the Kernel as an alias for Lab42::DiggyMethods.new

Given

    let(:data) { {a: 1, b: {c: 2, d: {e: 3}}} }
    let(:diggy) { Diggy(**data) }

Then we can access its fields as follows

  expect(diggy.a).to eq(1)
  expect(diggy.b.d.__data__).to eq(e: 3)
  expect(diggy.b.d.e).to eq(3)

And we can use a shortcut for key.__data__ by using key!

    expect(diggy.b.d!).to eq(e: 3)

And that works for leave nodes too of course

    expect(diggy.a!).to eq(1)

And in case of missing keys

    expect{ diggy.b.d.f }.to raise_error(KeyError, "key not found: b.d.f")

And this works for arrays too:

    with_array = Diggy(a: [b: {}])
    expect { with_array.a.first.b.c }.to raise_error(KeyError, "key not found: a.b.c")

If we access unknown keys we get the usual KeyError error, however we must not pass, nonhashable data

But if we pass an array

    expect{ Diggy([:a]) }.to raise_error(ArgumentError)

Context: Using in ERB with __binding__

In order to take advantage of this syntax we want to pass the binding of a Diggy object to ERB

Given an ERB template

    require 'erb'
    let(:template_text) { "<%= data.person.name %>" }

And a Lab42::DiggyMethod instance

    let(:data) { Diggy(data: {person: {name: "YHS"}}) }

Then we can pass the binding to the template

    expect(ERB.new(template_text).result(data.__binding__)).to eq("YHS")

Context: Merging bindings

In some cases Diggy will not be the only useful binding in an ERB Template we can work around this as follows

Given a diggy and a module

    let(:diggy) { Diggy(a: 1) }
    let :mod  do
      Module.new do
        def b; 42 end
      end
    end

And we extend the diggy with the module

    before { diggy.extend(mod) }

Then the module's methods become accessible in the binding

    expect(diggy.__binding__.eval("b")).to eq(42)

Context: Iteration

If an element in a diggy object is an array we descend

Given yet another diggy™

    let(:diggy) { Diggy(a: [b: 1, c: 2]) }

Then we get an array

    expect(diggy.a).to be_an(Array)

And it contains diggy instances

    expect(diggy.a.first.b).to eq(1)

LICENSE

Copyright 2022 Robert Dober [email protected]

Apache-2.0 c.f LICENSE