Reputation: 142
Is it possible to pass a string to a method in ruby and have that method interpolate that string?
I have something like this in mind:
do_a_search("location = #{location}")
...
def do_a_search(search_string)
location = .... #get this from another source
Model.where(search_string)
end
The context is RoR but this is a general ruby question I think. I realise the example above looks a bit convoluted but I'm trying to refactor a bunch of very repetitive methods.
The issue is that if I put the string to be interpolated in double quotes, location doesn't exist when the method is called, if I put it in single quotes, it will never be interpolated...
What I really want to do is put it in single quotes and interpolate it later. I don't think this is possible, or am I missing something?
edit: to be clear (as I think I have oversimplified what I'm trying to do above), one of the issues here is that I might want to call this method in multiple contexts; I might actually want to call
do_a_search("country = #{country}")
or even
do_a_search("country = #{country} AND location = #{location})
(country also existing as a local var within my method). I therefore want to pass everything necessary for the substitution in my method call
I think the String.interpolate method from the facets gem would solve my problem but it doesn't work in rails 4
Upvotes: 4
Views: 2652
Reputation: 37409
You can late-bind the extrapolation by using binding
and ERB
:
require 'erb'
def do_a_search(search_string)
location = 'this'
ERB.new(search_string).result(binding)
end
do_a_search('location = <%= location %>')
# => "location = this"
Or, you can directly use eval
:
def do_a_search(search_string)
location = 'this'
eval search_string, binding
end
do_a_search('"location = #{location}"')
# => "location = this"
This will, of course, be acceptable only if the strings you receive in do_a_search
are trusted and/or sanitized.
Upvotes: 0
Reputation: 142
As I mentioned above, the Facets Gem would help with this, but doesn't seem to work in rails 4
However its code to extend String is very simple:
class String
def self.interpolate(&str)
eval "%{#{str.call}}", str.binding
end
end
I think that implementing & then using that inside the method is the most sensible way of doing what I'm looking for, but I'll accept Sawa's answer, because as Paul Richter & Chuck point out, it would I think work, but at the risk of "blowing up" if I don't quite get the calls right.
Upvotes: 2
Reputation: 237010
What you want is basically a format string, so I think you'd be better served by sprintf or %
.
do_a_search("location = %s")
...
def do_a_search(search_string)
location = .... #get this from another source
Model.where(search_string % location)
end
If you have multiple things you want to interpolate and don't want to enforce an order, you can use a Hash and named specifiers.
do_a_search("location = %{location}")
...
def do_a_search(search_string)
location = .... #get this from another source
Model.where(search_string % {location: location})
end
Upvotes: 1
Reputation: 168071
For that purpose, %
is used. First, create a string:
s = "location = %{location}"
Later, you can apply %
to it:
s % {location: "foo"} # => "location = foo"
If you do not have to name the parameter(s), then it is simpler:
s = "location = %s"
s % "foo" # => "location = foo"
Upvotes: 10