3

I have a library that has an #execute method like this

def execute(query, **args)
  # ...
end

I have a class that generates the data for args (which has a lot of logic depending on user abilities)

class Abilities
  def to_h
    { user: user } # and a lot more data
  end
end

Now when I'm using #execute I always have to remember to use #to_h, which is pretty annoying and leads to mistakes when someone forgets it:

execute(query, abilities.to_h)

So I was wondering if my Abilities class could somehow respond to the ** (double splat) operator, so that I can simply pass the object:

execute(query, abilities)

When I try to call it like this, it throws an error:

ArgumentError: wrong number of arguments (given 2, expected 1)

So, is there any way to make my Abilities class behave like a Hash? I could derive it like this Abilities < Hash but then I have all the Hash logic on it, which seems pretty dirty.

23tux
  • 14,104
  • 15
  • 88
  • 187

2 Answers2

5

You can implement to_hash: (or define it as an alias for to_h)

class MyClass
  def to_hash
    { a: 1, b: 2 }
  end
end

def foo(**kwargs)
  p kwargs: kwargs
end

foo(MyClass.new)
#=> {:kwargs=>{:a=>1, :b=>2}}
Stefan
  • 109,145
  • 14
  • 143
  • 218
0

If you specify the API to execute in such a way that it accepts anything that supports to_h, then you have a solution:

def execute(query, args = {})
  args = args.to_h
  ...
end
Casper
  • 33,403
  • 4
  • 84
  • 79
  • unfortunately, I can't alter the definition of `#execute` because it's inside a gem, and I don't want to monkey patch it – 23tux Nov 18 '19 at 10:10