Why is "#{String}" a common idiom in Ruby - ruby

A Ruby dev I know asked this; my answer is below... Are there other, better reasons?
Why do so many Ruby programmers do
"#{string}"
rather than
string
since the second form is simpler and more efficient?

Is this a common idiom for Ruby developers? I don't see it that much.

Smaller changes when you later need to do more than simply get the value of the string, but also prepend/append to it at the point of use seems to be the best motivation I can find for that idiom.

There is only one case where this is a recommended idiom :
fname = 'john'
lname = 'doe'
name = "#{fname} #{lname}"
The code above is more efficient than :
name = fname + ' ' + lname
or
name = [fname, lname].join(' ')

What's the broader context of some of the usages? The only thing I can come up with beyond what's already been mentioned is as a loose attempt at type safety; that is, you may receive anything as an argument, and this could ensure that whatever you pass in walks like a duck..or, well, a string (though string.to_s would arguably be clearer).
In general though, this is probably a code smell that someone along the way thought was Best Practices.

I use this kind of code, so that I can pass nil as string and it still will work on a string, rather than seeing some exceptions flying:
def short(string = nil)
"#{string}"[0..7]
end
And it's easier/faster to append some debug code, if it's already in quotes.
So in short: It's more convenient.

Interesting answers, everyone. I'm the developer who asked the original question. To give some more context, I see this occasionally at my current job, and also sometimes in sample code on the Rails list, with variables that are known in advance to contain strings. I could sort of understand it as a substitute for to_s, but I don't think that's what's going on here; I think people just forget that you don't need the interpolation syntax if you're just passing a string variable.
If anyone tried to tell me this was a best practice, I'd run away at top speed.

maybe it is easy way to convert any to string? Because it is the same as call to_s method. But it is quite strange way :).
a = [1,2,3]
"#{a}"
#=> "123"
a.to_s
#=> "123"

I could image this being useful in cases where the object being interpolated is not always a String, as the interpolation implicitly calls #to_s:
"#{'bla'}" => "bla"
"#{%r([a-z])}" => "(?-mix:[a-z])"
"#{{:bla => :blub}}" => "blablub"
May make sense when logging something, where you don't care so much about the output format, but never want an error because of a wrong argument type.

Related

Testing if an object is a string

I have a function that manipulates a string; however, sometimes my input isn't already a string. For example it could be a path object. I need to convert it to a string because I want to call methods like .gsub.
My question seems a bit simple, but I'm debating on the best approach for converting the object to a string.
I currently have two options:
str = str.to_s unless str.is_a? String
or
str = str.to_s
The second method is much simpler, but the first method actually describes what's going on. I'm wondering which of these two methods is better to use or if there's a better approach I haven't thought of?
I would prefer the second one.
I'd prefer the parameter/variable wasn't named str if it isn't a string.
Naming it str implies string, but then the code looks silly, and is harder to reason about.
I prefer second one. It is shorter, simplier and also describes what you want (any programmer will understand what will heppen). Also there is no notable difference in perfomance.
Go for the second approach without hesitation.
The first one is convoluted and doesn't really add any meaning.

better way to do assignment and check result

I have to use String.scan function, which returns empty array if there is no match.
I wanted to assign a variable with the scan function and check it there is a match, but unfortunately I cannot do that because it won't return nil or false on no match.
I wanted to do this (1 line):
if ip = str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
...
#use ip
end
but because it won't return nil on no match I must do:
ip_match = str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
unless ip_match.empty?
#use ip
end
Is there some more elegant way to write this - to be able to do assignment and empty check at the same time or some other way to beautify the code?
Thanks
Since scan returns an array, and even if you are sure there would be only one result, you could do this.
str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/).each do |ip|
#use ip
end
There's a difference between elegant and cryptic or "concise".
In Perl you'll often see people write something equivalent to:
if (!(ip = str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)).empty?)
It's a bit more concise, terse, tight, whatever you want to call it. It also leads to maintenance issues because of the = (equate) vs. what should normally be an equality test. If the code is passed to someone who doesn't understand the logic, they might mistakenly "correct" that, and then break the code.
In Ruby it's idiomatic to not use equate in a conditional test, because of the maintenance issue, and instead use the assignment followed by a test. It's clearer code.
Personally, I prefer to not use unless in that sort of situation. It's an ongoing discussion whether unless helps generate more understandable code; I prefer if (!ip_match.empty?) because it reads more like we'd normally talk -- I seldom start a statement with unless in conversation. Your mileage might vary.
I would preferably do something like this using String helper match
ip_validator = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
# match return nil if no match
if str.match ip_validator
# blah blah blah.....
end
help me keep code dry and clean.
May be this is not the most elegant , looking for others if any :)
Your ip_validator regex seems to be week check this out Rails 3: Validate IP String

How are #{...} construct used in Ruby?

I dont understand how #{...} construct is used in Ruby.
I've seen in used in the regexp example on http://www.ruby-doc.org/core-1.9.3/Regexp.html
place = "tokyo"
/#{place}/.match("Go to tokyo")
#=> #<MatchData "tokyo">
What exactly is this #{...} feature called and does anyone know of some good working examples of this.
Really appreciate the help.
Thanks!
Here's an example that's a bit simpler:
place = "Tokyo"
puts "Go to #{place}"
What the #{...} construct does is to execute the ruby code that it contains, and return a string representation of the result, which then is embedded in the string where the construct appears.
Another example:
place = "Tokyo"
puts "#{place} is a #{place.class} of #{place.length} characters"
In other words, your example is equivalent to:
/tokyo/.match("Go to tokyo")
Hope this helps.
Is called interpolation, and allows you to convert placeholders to the value they represent...
http://kconrails.com/2010/12/08/ruby-string-interpolation/
The #{...} is especially useful and used quite a lot in metaprogramming. It helps you to dispatch methods dynamically without knowing the name of these methods before run time.
if conf.rc and File.exists?( conf.rc )
YAML.load_file(conf.rc).each do |k,v|
conf.send("#{k}=" , v)
end
end
As you can see, until run time we do not know which methods are going to be dispatched. Through .send and #{...}, we can dynamically dispatch methods. For example, in above code depending on the values in conf.rc different methods can be dispatched.
Example is taken from Metaprogramming Ruby.

Call Ruby method with parameters separated by space

Not sure if this is possible but can I call a method from an irb shell with spaces between parameters rather than commas (don't ask) ? Lets say I have a method
def start_band(member1, member2, member3, member4)
#do something
end
And then I call it like the following:
irb>> start_band "John" "Paul" "George" "Ringo"
EDIT: Would it be possible to detect every keypress instead?
No, you can't do that. Not with strings anyway.
No.
You could use something like treetop to write a really simple DSL, or just play monkey-parsing games, but that won't solve your exact question.
The other obvious answer is this, which also fails:
irb>> start_band %W(John Paul George Ringo)
Creating an irb-like CLI isn't difficult, and may be adequate, depending on what your actual requirements are.
There is actually a very easy way to get rid of the commas. You can even get rid of the quotes, too:
def start_band(members)
#members is an array
end
start_band %w(John Paul George Ringo)
The limitation is that you can't use spaces inside your strings, and you still need start-end terminations (can use other characters instead of parenthesis though).
Durr! I really approached this the wrong way. I simply needed to run
#members = gets
to allow the input as required. Thanks for the responses nonetheless.

Ruby string mutability

This may be a bit of a nooby question, I have been trying to get better at ruby recently, and started reading the fantastic The Ruby Programming Language. Something that was mentioned is that string literals are considered mutable, so in a loop it is better to use a variable then a literal, as a new string will get instantiated at every iteration.
My question is why? At first I thought it was because of interpolation, but symbols are immutable and they support interpolation. Coming from a static background, it doesn't really make much sense to me.
EDIT:
After reading thenduks answer, I think I may have it. AFAIK, languages like Java or C# don't have destructive string methods (they use upcase, but not upcase!). Because of things like upcase! or <<, the literal cannot be immutable.
Not 100% sure on that, the other possibility is that it is a compile-time interning that happens, which is something that just doesn't happen in a scripting language.
Not really sure what exactly your question is, but consider the following code:
10.times { puts "abc".object_id }
This prints out 10 different id's. Why? Just because you know this string wont change doesn't mean Ruby does. If you think that "abc" should only be created once then what happens if you do:
10.times { puts "abc".upcase! }
The upcase! method mutates the string to be upper case, on the next iteration the string created in the first iteration isn't the same anymore.
Perhaps post a code example that is confusing to you?

Resources