String interpolation without #{} [duplicate] - ruby

This question already has an answer here:
Why does string interpolation work in Ruby when there are no curly braces?
(1 answer)
Closed 8 years ago.
Please observe the following:
"abcd#fg" # => "abcd#fg"
"abcd#$fg" # => "abcd" characters #$ and after them are skipped
"abcd##fg" # => "abcd" characters ## and after them are skipped
It could be string interpolation just with # instead of #{}.
$fg = 8
"abcd#$fg" # => "abcd8"
#fg = 6
"abcd##fg" # => "abcd6"
It works like interpolation. Is it a bug or a feature?

You can actually interpolate global, instance, and class variables omitting the braces:
$world = 'world'
puts "hello, #$world"
# hello, world
In your example both $fg and #fg are uninitialized and thus evaluated to nil, that's why they are intorpolated as empty strings. When you write "abcd#fg" nothing is interpolated because the # is not followed by one of {, #, $.
You can find the feature documented in the RubySpec (thanks to #DavidMiani).
If you ask me, don't rely on this behaviour and always interpolate variables using braces, both for readability and to avoid problems such:
#variable = 'foo'
puts "##variable_bar"
This will output an empty string instead of the, probably, expected string "foo_bar", because it is trying to interpolate the undefined instance variable #variable_bar.

Related

Why does single quote not work with string interpolation in ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I tried string interpolation with single quotes using #{} but it does not work. When I tried it with double quotes it works. Please explain to me why this is so, if it's possible to do string interpolation with single quotes and how to do so in ruby if it is possible.
Ruby doesn't interpret single-quoted strings.
This might seem like a limitation at first, but it's actually a nice feature. It allows you to enter many characters without having to escape them, which results in more legible code:
file = 'C:\foo\bar\baz.txt'
# as opposed to:
file = "C:\\foo\\bar\\baz.txt"
Or when having a string about string interpolation itself: (note that Stack Overflow's syntax highlighting is misleading – there's no interpolation)
string = 'In Ruby, you can write "1 + 2 = #{ 1 + 2 }" to get "1 + 2 = 3".'
# instead of:
string = "In Ruby, you can write \"1 + 2 = \#{ 1 + 2 }\" to get \"1 + 2 = 3\"."
Apart from '...' and "...", Ruby also has %q(...) and %Q(...) style string literals (the former without, the latter with interpolation). This is especially useful if your string contains both, single and double quotes:
string = %q(A string containing '...' and "...")
You can even pick your own delimiter: (again, the syntax highlighter can't keep up)
string = %q#A string containing '...', "..." and (...)"#
And finally, you can mix and match different string literal styles:
string = %q(foo) 'bar' "baz"
#=> "foobarbaz"
This is how ruby language is designed.
From language docs: Literals
Double-quote strings allow escaped characters such as \n for newline,
\t for tab, etc.
Double-quote strings allow interpolation of other
values using #{...}:
value = 10
puts "Test value is #{value}"
# => Test value is 10
Interpolation may be disabled by escaping the “#” character or using single-quote strings
puts 'Test value is #{value}'
# => Test value is #{value}
Single-Quoted Strings and Escapes/Expressions
That's just how the language is defined: you can't interpolate using the embedded expression operator (#{}) within a single-quoted string. Unlike double-quoted strings, single-quoted strings aren't scanned for embedded expressions or most escapes. This is important when you need to do things like printing escape characters or unevaluated expressions such as:
puts 'This is a newline character: \n'
puts 'This is how you embed an expression: #{foo}'
That doesn't mean you can't approximate interpolation with single-quoted strings in other ways. For example:
'foo: %s' % 'bar'
#=> "foo: bar"
sprintf '%d, %d, %d', 1, 2, 3
#=> "1, 2, 3"

How to convert a string with special characters escaped to the actual special chars [duplicate]

This question already has answers here:
Best way to escape and unescape strings in Ruby?
(7 answers)
Closed 5 years ago.
How to convert a string with special characters escaped to the actual special chars?
For example, if I have this:
s = "hello\\nworld"
The output of puts is naturally this:
> puts s
hello\nworld
But how do I transform it into this?
hello
world
In other words, is there any function to unescape backslashed characters?
Your best bet is to do some string replacements.
s = "hello\\nworld"
puts s.gsub("\\n", "\n")
# >> hello
# >> world
The downside of this approach is that you have to explicitly list/process all special chars you need to unescape.
You interpolate \ by \\ ,
use one backslash instead of two:
s = "hello\nworld"
puts s
#=> hello
world
If your string is from a trusted source, you can use eval:
s = "hello\\nworld"
eval("\"#{s}\"") # => "hello\nworld"
Note that this allows arbitrary code execution:
s = "\"; loop { }; \""
eval("\"#{s}\"") # => infinite loop
Also this doesn't work for input like s = "\"".

Why does capturing named groups in Ruby result in "undefined local variable or method" errors?

I am having trouble with named captures in regular expressions in Ruby 2.0. I have a string variable and an interpolated regular expression:
str = "hello world"
re = /\w+/
/(?<greeting>#{re})/ =~ str
greeting
It raises the following exception:
prova.rb:4:in <main>': undefined local variable or methodgreeting' for main:Object (NameError)
shell returned 1
However, the interpolated expression works without named captures. For example:
/(#{re})/ =~ str
$1
# => "hello"
Named Captures Must Use Literals
You are encountering some limitations of Ruby's regular expression library. The Regexp#=~ method limits named captures as follows:
The assignment does not occur if the regexp is not a literal.
A regexp interpolation, #{}, also disables the assignment.
The assignment does not occur if the regexp is placed on the right hand side.
You'll need to decide whether you want named captures or interpolation in your regular expressions. You currently cannot have both.
Assign the result of #match; this will be accessible as a hash that allows you to look up your named capture groups:
> matches = "hello world".match(/(?<greeting>\w+)/)
=> #<MatchData "hello" greeting:"hello">
> matches[:greeting]
=> "hello"
Alternately, give #match a block, which will receive the match results:
> "hello world".match(/(?<greeting>\w+)/) {|matches| matches[:greeting] }
=> "hello"
As an addendum to both answers in order to make it crystal clear:
str = "hello world"
# => "hello world"
re = /\w+/
# => /\w+/
re2 = /(?<greeting>#{re})/
# => /(?<greeting>(?-mix:\w+))/
md = re2.match str
# => #<MatchData "hello" greeting:"hello">
md[:greeting]
# => "hello"
Interpolation is fine with named captures, just use the MatchData object, most easily returned via match.

String interpolation when not using a string literal

I have a Ruby script that used string interpolation to build error messages.
p "#{vName} is not a defined variable" => 'xxx is not a defined variable'
Another programmer came through and attempted to externalize the string literals to a separate configuration file. Of course, he doesn't get the substitution.
p err_string_from_config => '#{vName} is not a defined variable'
I've looked around, but couldn't come up with anything better than converting to sprintf strings and using printf.
Does anybody know how to get the #{} substitution to work on strings that are not double quote literals within the Ruby script?
Actually Ruby has functionality very similar to John's Python example:
$ irb
>> greeting = 'hello %s, my name is %s!'
>> interpolated = greeting % ['Mike', 'John']
=> "hello Mike, my name is John!"
>>
This is also useful if your argument is an array constant. If you must use #{} style interpolation you could use eval:
>> greeting = 'hi #{name}' # notice name is not defined yet
>> name = "mike"
>> eval '"' + greeting + '"'
The eval approach is going to be much slower than using % style interpolation, so it's a trade-off.
I suggest that you look at Liquid templating language which provides more powerful features (e.g. you can reference parameters by name).
Previous example would look like:
greeting = Liquid::Template.parse("hello {{your_name}}, my name is {{my_name}}!")
interpolated = greeting.render('your_name' => 'Mike', 'my_name' => 'John')
# => "hello Mike, my name is John!"
Here's how I do it, just for the record. A bit clearer imo.
gief = '#{later} please'
later = "later"
puts eval(%Q["#{gief}"])
# => later please
But honestly, that is such a hack. Unless you have a really good reason to use a string, use a proc instead. I always try to use plain ruby instead of evaling strings.
gief = Proc.new {|l| "#{l} please" }
later = "later"
puts gief.call(later)
# => later please

What is the colon 'operator' doing in this example?

I saw this example in an answer elsewhere and with the following it outputs foobar:
a = :foo
def bar(b)
:"#{b}bar"
end
c = bar(a)
c
The colon isn't an operator inside bar, it is simply a Symbol literal that uses string interpolation to build the Symbol. Some Symbols need to be quoted to avoid syntax issues, for example:
:'a+b'
You can also use double quotes with this syntax and those quotes behave just like double quotes for strings so they support string interpolation. So this:
:"#{b}bar"
is equivalent to:
"#{b}bar".to_sym
or
(b.to_s + 'bar').to_sym
If you #inspect your value you'll get a better idea of what it contains:
puts c.inspect
# :foobar

Resources