Wim Haanstra
Wim Haanstra

Reputation: 5998

String interpolation with subhashes

In my code I want to use string interpolation for an email subject I am generating.

output = "this is my %{title}" % {title: "Text here"}

This works as expected, but is there a way to use hashes inside of hashes and still be able to use string interpolation?

It would be awesome if I could do something like:

output = "this is my %{title.text}" % {title: {text: "text here"}}

Upvotes: 4

Views: 922

Answers (3)

Stefan
Stefan

Reputation: 114268

In Ruby 2.3, sprintf checks the hash's default value, so you could provide a default_proc to dig up the nested value:

hash = {title: {text: "text here"}}
hash.default_proc = proc { |h, k| h.dig(*k.to_s.split('.').map(&:to_sym)) }

"this is my %{title.text}" % hash
#=> "this is my text here"

Kind of hacky, but it seems to work.

Upvotes: 4

Jordan Running
Jordan Running

Reputation: 106147

It's actually not hard to make this work if you write a simple utility method to "squash" a nested Hash's keys, e.g.:

def squash_hash(hsh, stack=[])
  hsh.reduce({}) do |res, (key, val)|
    next_stack = [ *stack, key ]
    if val.is_a?(Hash)
      next res.merge(squash_hash(val, next_stack))
    end
    res.merge(next_stack.join(".").to_sym => val)
  end
end

hsh = { foo: { bar: 1, baz: { qux: 2 } }, quux: 3 }

p squash_hash(hsh)
# => { :"foo.bar" => 1, :"foo.baz.qux" => 2, :quux => 3 }

puts <<END % squash_hash(hsh)
foo.bar: %{foo.bar}
foo.baz.qux: %{foo.baz.qux}
quux: %{quux}
END
# => foo.bar: 1
#    foo.baz.qux: 2
#    quux: 3

Upvotes: 0

Petr Gazarov
Petr Gazarov

Reputation: 3821

I don't think this is possible with % method. You'd have to use regular Ruby interpolation with "#{}". I'd also point out that you can use OpenStruct.

title = OpenStruct.new(text: 'text here')

output = "this is my #{title.text}" 

Upvotes: 2

Related Questions