When given a string that is intended to be formatted with a hash of values to write into the string, is there a clean way to get all the keys that string is expecting values for?
I'm putting together text in a situation where there is a lot of room for customization, and several options for dynamic values to insert into the text. Some of the values are more expensive to get than others, so I'd like to be able to prepare my hash to send in to %
to only include the values that are needed in the string.
Ideally I'd be able to query the system that performs the formatting on the string, but I'm not seeing any documentation of such an interface. What I'd like is something like:
"Your request for %{item} is at position %<pos>d".formatting_keys
>>> [:item, :pos]
When passing a hash to String#%
, it will call the hash's default proc if a key is missing. You could utilize this behavior and make the proc sneakily collect the passed keys:
def format_keys(format_string)
keys = []
format_string % Hash.new { |_, k| keys << k ; 0 }
keys
end
format_keys("Your request for %{item} is at position %<pos>d")
#=> [:item, :pos]
Note that the proc's return value has to be a valid object for the various field types. I'm using 0
here which seems to work fine.
I'd like to be able to prepare my hash to send in to % to only include the values that are needed in the string.
Instead of a Hash, use an object that does the calculation on demand. That will be useful everywhere.
Use string interpolation to call the methods instead of format sequences.
class Whatever
def item
@item ||= calculate_item
end
def pos
@pos ||= calculate_pos
end
private
def calculate_item
# do something expensive
end
def calculate_pos
# do something expensive
end
end
obj = Whatever.new
puts "Your request for #{obj.item} is at position #{obj.pos.to_i}"
Using Ruby's own sequence parsing as per https://stackoverflow.com/a/74728162 is ideal, but you can also do your own:
class String
def format_keys
scan(
/
(?<!%) # don't match escaped sequence starts, e.g. "%%{"
(?:
(?<=%\{) [^\}]+ (?=\}) # contents of %{...}
| # OR
(?<=%\<) [^\>]+ (?=\>) # contents of %<...>
)
/x
)
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.