Skip to content

Instantly share code, notes, and snippets.

@eric-hemasystems
Created August 16, 2021 16:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eric-hemasystems/f66777bc46879bd8ba6df42751089ae3 to your computer and use it in GitHub Desktop.
Save eric-hemasystems/f66777bc46879bd8ba6df42751089ae3 to your computer and use it in GitHub Desktop.
Rails `delegate to:` for hashes
module HashDelegate
# Like `delegate :foo, to: :bar` only for hashes instead
# of objects. So these are the same:
#
# def foo
# bar[:foo]
# end
#
# hash_delegate :foo, to: :bar
#
# Like `delegate` instance variable can also be used:
#
# def foo
# @bar[:foo]
# end
#
# hash_delegate :foo, to: :@bar
#
# The above examples assume the keys are symbols. If they are strings you
# can still using this DSL you just need to provide a string regarding the
# key you are delegating:
#
# hash_delegate 'foo', to: :bar
#
# Is the same as:
#
# def foo
# bar['foo']
# end
#
# `prefix` and `private` options also exist and work just like `delegate`
def hash_delegate *keys, to:, prefix: false, private: false
methods = []
location = caller_locations(1, 1).first
file, line = location.path, location.lineno
keys.each do |key|
method = if prefix
"#{prefix == true ? to : prefix}_#{key}"
else
key
end
methods << method
module_eval <<~METH, file, line
def #{method}
#{to}[#{key.inspect}]
end
METH
end
private(*methods) if private
end
end
RSpec.describe HashDelegate do
let :klass do
Class.new do
extend HashDelegate
def initialize hsh
@hsh = hsh
end
def hsh
@hsh
end
hash_delegate :ivar, to: :@hsh
hash_delegate :meth, to: :hsh
hash_delegate 'str', to: :hsh
hash_delegate :pvt, to: :hsh, private: true
hash_delegate :prefix, to: :hsh, prefix: true
hash_delegate :prefix, to: :hsh, prefix: :named
end
end
it 'can delegate to an instance variable' do
obj = klass.new ivar: 'val'
expect( obj.ivar ).to eq 'val'
end
it 'can delegate to a method' do
obj = klass.new meth: 'val'
expect( obj.meth ).to eq 'val'
end
it 'can delegate a string' do
obj = klass.new 'str' => 'val'
expect( obj.str ).to eq 'val'
end
it 'can make delegation private' do
obj = klass.new pvt: 'val'
expect( obj.respond_to? :pvt ).to be false
expect( obj.send(:pvt) ).to eq 'val'
end
it 'can add prefix to delegation' do
obj = klass.new prefix: 'val'
expect( obj.hsh_prefix ).to eq 'val'
end
it 'can add named prefix to delegation' do
obj = klass.new prefix: 'val'
expect( obj.named_prefix ).to eq 'val'
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment