How are #{...} construct used in Ruby? - 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.

Related

Why is there no `.split!` in Ruby?

It just seems pretty logical to have it when there's even a downcase!. Has anyone else run into this use case in Ruby?
For the curious, I'm trying to do this:
def some_method(foo)
foo.downcase!.split!(" ")
## do some stuff with foo later. ##
end
some_method("A String like any other")
Instead of this:
def some_method(foo)
foo = foo.downcase.split(" ")
## do some stuff with foo later. ##
end
some_method("A String like any other")
Which isn't a really big deal...but ! just seems cooler.
Why is there no .split! in Ruby?
It just seems pretty logical to have it when there's even a downcase!.
It may be logical, but it is impossible: objects cannot change their class or their identity in Ruby. You may be thinking of Smalltalk's become: which doesn't and cannot exist in Ruby. become: changes the identity of an object and thus can also change its class.
I don't see this "use case" as very important.
The only thing a "bang method" is doing is saving you the trouble of assigning a variable.
The reason "bang methods" are the exception instead of the rule is they can produce confusing results if you don't understand them.
i.e. if you write
a = "string"
def my_upcase(string)
string.upcase!
end
b = my_upcase(a)
then both a and b will have transformed value even if you didn't intend to change a. Removing the exclamation point fixes this example, but if you're using mutable objects such as hashes and arrays you'll have to look out for this in other situations as well.
a = [1,2,3]
def get_last_element(array)
array.pop
end
b = get_last_element(a)
Since Array#pop has side effects, a is now 1,2. It has the last element removed, which might not have been what you intended. You could replace .pop here with [-1] or .last to get rid of the side effect
The exclamation point in a method name is essentially warning you that there are side effects. This is important in the concept of functional programming, which prescribes side effect free code. Ruby is very much a functional programming language by design (although it's very object oriented as well).
If your "use case" boils down to avoiding assigning a variable, that seems like a really minor discomfort.
For a more technical reason, though, see Jorg Mittag's answer. It's impossible to write a method which changes the class of self
this
def some_method(foo)
foo = foo.downcase.split(" ")
end
some_method("A String like any other")
is the same as this
def some_method(foo)
foo.downcase.split
end
some_method("A String like any other")
Actually, both of your methods return the same result. We can look at a few examples of methods that modify the caller.
array.map! return a modified original array
string.upcase! return a modified original string
However,
split modifies the class of the caller, changing a string to an array.
Notice how the above examples only modify the content of the object, instead of changing its class.
This is most likely why there isn't a split! method, although it's pretty easy to define one yourself.
#split creates an array out of a string, you can't permanently mutate(!) the string into being an array. Because the method is creating a new form from the source information(string), the only thing you need to do to make it permanent, is to bind it to a variable.

How can I avoid Ruby Perlisms in `gsub` and `match`?

I'd like to avoid my method:
def page_cover
return cover_title unless cover_title.match(/#{ capitalised_acronyms }/).present?
cover_title.gsub(/#{ capitalised_acronyms }/, $&.upcase)
end
because it looks Perly, and I heard of a news that it might be deprecated in the future. From the book:
Prefer String#match to String#=~. The former returns all the match information in a MatchData object instead of several special global variables.
Use the longer, more descriptive global variable aliases as opposed to their short cryptic names (e.g., $LOAD_PATH instead of $:). Most of the longer names are only available after loading the English library.
Avoid methods that implicitly read from, or write to, the $_ global variable (e.g., Kernel#print, Regexp#~, etc.).
I think I violated #3 in:
$&.upcase
Any suggestions are welcome.
While I neither agree nor disagree with a general principle of avoiding "Perlisms," I do agree that avoiding $+punctuation variables is a good way to improve the readability of your code.
There is no in-place substitute for $& (which holds the last regex match). You can, however, access the matched string using the MatchData object returned by String#match.
def page_cover
if matchdata = cover_title.match(/#{capitalised_acronyms}/)
cover_title.gsub(/#{capitalised_acronyms}/, matchdata[0].upcase)
else
cover_title
end
end
NOTE:
As written, this method (like your example method) will replace all matched lowercase acronyms with an uppercase version of only the first acronym matched: i.e. "I love the nfl and nba" becomes "I love the NFL and NFL". If that is unintentional, there is a much simpler way to write this method:
def page_cover
cover_title.gsub(/#{capitalised_acronyms}/, &:upcase)
end
This uses the & operator (which is completely unrelated to $&) to convert the symbol :upcase into the block {|x| x.upcase } behind the scenes.

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.

Rails 3 - Check if string/text includes a certain word/character via regex in controller

I am working on a quoting mechanism in my app, where it should be possible to simply type #26, for example, in the comment form in order to quote comment 26 of that topic.
To check if a user wants to quote one or more comments in the first place, I put an if condition after my current_user.comments.build and before #comment.save.
But, just to make my question a bit more general and easier to adapt:
if #comment.content.include?(/\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i)
I want something like this. That example was for checking if the comment's content includes emails. But logically I get an "can't convert regexp to string" error.
How can you do the include? method in rails with an regexp? So, to check whether a text includes a string of a certain regex format?
Or is the controller the wrong place for such regex actions?
I do ruby regex'es this way:
stringObj.match(/regex/)
There's also
if #comment.content =~ /regex/
If you had an array of all previous comments #prev_comments and wanted to replace them all in one shot, you could:
pattern = /#(\d+)/
#comment.content.gsub(pattern) do
cur_match = Regexp.last_match
idx = cur_match[1].to_i - 1
#prev_comments[idx]
end
Trick is using Regexp.last_match to get the current match, which made me wonder if it was thread safe. It is, apparently.
adapted (stolen) from the below more general String extension
class String
def js_replace(pattern, &block)
gsub(pattern) do |_|
md = Regexp.last_match
args = [md.to_s, md.captures, md.begin(0), self].flatten
block.call(*args)
end
end
end
Source: http://vemod.net/string-js_replace
To match the nature of .include?
stringObj.match(/regex/).present?
Would give similar true/false outcomes if you're using Rails (or ActiveSupport)

Why is "#{String}" a common idiom in 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.

Resources