I'm reading some Ruby code and I don't understand this snippet:
thing = '${other-thing}/etc/'
It appears to substitute a value for the ${other-thing} and use that to build the String thing but I haven't been able to recreate this myself.
EDIT: Sorry to all, it turns out there was some preprocessing going on by Maven (a Java build tool). The accepted answer shows how one could do the substitution in straight Ruby.
$ irb
irb(main):001:0> a = "Hello"
=> "Hello"
irb(main):002:0> b = "world"
=> "world"
irb(main):003:0> puts "${a}, ${b}!" # Doesn't work.
${a}, ${b}!
=> nil
irb(main):004:0> puts "#{a}, #{b}!" # Works fine.
Hello, world!
=> nil
irb(main):005:0> puts '#{a}, #{b}!' # Doesn't work.
#{a}, #{b}!
=> nil
You wanted #{...}, not ${...} I believe. Also, you don't get substitutions inside of single-quoted strings, only double-quoted (or equivalents – there's dozens of ways to delimit strings in Ruby).
Related
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.
I have just started playing with Ruby and I'm stuck on something. Is
there some trick to modify the casefold attribute of a Regexp object after
it's been instantiated?
The best idea what I tried is the following:
irb(main):001:0> a = Regexp.new('a')
=> /a/
irb(main):002:0> aA = Regexp.new(a.to_s, Regexp::IGNORECASE)
=> /(?-mix:a)/i
But none of the below seems to work:
irb(main):003:0> a =~ 'a'
=> 0
irb(main):004:0> a =~ 'A'
=> nil
irb(main):005:0> aA =~ 'a'
=> 0
irb(main):006:0> aA =~ 'A'
=> nil
Something I don't understand is happening here. Where did the 'i' go on line
8?
irb(main):07:0> aA = Regexp.new(a.to_s, Regexp::IGNORECASE)
=> /(?-mix:a)/i
irb(main):08:0> aA.to_s
=> "(?-mix:a)"
irb(main):09:0>
I am using Ruby 1.9.3.
I am also unable understand the below code: why returning false:
/(?i:a)/.casefold? #=> false
As your console output shows, a.to_s includes the case sensitiveness as an option for your subexpression, so aA is being defined as
/(?-mix:a)/i
so you're asking ruby for a regular expression that is case insensitive, but the only thing in that case insensitive regexp is a group for when case sensitivity has be turned on, so the net effect is that 'a' is matched case sensitively
Since the result of to_s is just the regular expression string itself - no delimiters or external flags - the flags are translated into the (?i:...) syntax that sets or clears them temporarily inside the expression itself. This lets you get a Regexp object back out via a simple Regexp.new(s) call that will match the same strings.
The wrapping, unfortunately, includes explicitly clearing the flags that are not set on the object. So your first regex gets stringified into something between (?:-i...) - that is, the casefold option is explicitly turned off between the parentheses. Turning it back on for the object doesn't have any effect.
You can use a.source instead of a.to_s to get just the original expression, without the flag settings:
irb(main):001:0> a=/a/
=> /a/
irb(main):002:0> aA = Regexp.new(a.source, Regexp::IGNORECASE)
=> /a/i
irb(main):003:0> a =~ 'a'
=> 0
irb(main):004:0> a =~ 'A'
=> nil
irb(main):005:0> aA =~ 'a'
=> 0
irb(main):006:0> aA =~ 'A'
=> 0
As Frederick already explains, calling to_s on a regex will add modifiers around it that ensure that its properties like case-sensitiveness are preserved. So if you insert a case-sensitive regex into a case-insensitive regex, the inserted part will still be case-sensitive. Likewise the modifiers given to Regexp.new will have no effect if the first argument is a regex or the result of calling to_s on one.
To solve this issue, call source on the regex instead of to_s. Unlike to_s, source simply returns the source of regex without adding anything:
aA = Regexp.new(a.source, Regexp::IGNORECASE)
I am also unable understand the below code: why returning false:
/(?i:a)/.casefold?
Because (?i:...) sets the i flag locally, not globally. It only applies to the part of the regex within the parentheses, not the whole regex. Of course in this case the whole regex is within the parentheses, but that doesn't matter as far as methods like casefold? are concerned.
Take this snippet of code which is supposed to replace a href tag with its URL:
irb> s='<p>Click here!</p>'
irb> s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^<]*)<\/a>/, "#{$1}")
=> "<p></p>"
This regex fails (URL is not found). Then I escape the < character in the regex, and it works:
irb> s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^\<]*)<\/a>/, "#{$1}")
=> "<p>http://localhost/activate/57f7e805827f</p>"
1: According to RubyMine's inspections, this escape should not be necessary. Is this correct? If so, why is the escape of > apparently not necessary as well?
2: Afterwards in the same IRB session, with the same string, the original regex suddenly works too:
irb> s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^<]*)<\/a>/, "#{$1}")
=> "<p>http://localhost/activate/57f7e805827f</p>"
Is this because the $1 variable is not cleared when calling gsub again? If so, is it intentional behaviour or is this a Ruby regex bug?
3: When I change the string, and reexecute the same command, $1 will only change after calling gsub twice on the changed string:
irb> s='<p>Click here!</p>'
=> "<p>Click here!</p>"
irb> s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^\<]*)<\/a>/, "#{$1}")
=> "<p>http://localhost/activate/57f7e805827f</p>"
irb> s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^\<]*)<\/a>/, "#{$1}")
=> "<p>http://localhost/activate/xxxxyyy</p>"
Is this intentional? If so, what is the logic behind this?
4: As replacement character, some tutorials suggest using "#{$n}", others suggest using '\n'. With the backslash variant, the problems above do not appear. Why - what is the difference between the two?
Thank you!
$1 contains the first capture of the last match. In your example, it is evaluated before the matching (actually even before gsub is called), therefore the value of $1 is fixed to nil (because you did not match anything, yet). So you always get the first capture of the previous match, you do not even need to change your original regex to get the expected result the second time:
s='<p>Click here!</p>'
s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^<]*)<\/a>/, "#{$1}")
# => "<p></p>"
s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^<]*)<\/a>/, "#{$1}")
# => "<p>http://localhost/activate/57f7e805827f</p>"
You can pass a block to gsub though, which is evaluated after the matching, e. g.
s.gsub(/<a href="([^ '"]*)"([^>]*)?>([^<]*)<\/a>/){ $1 }
# => "<p>http://localhost/activate/57f7e805827f</p>"
This way, $1 behaves as you'd expect. I like to always use named captures so i don't have to keep track of the numbers when i add a capture, though:
s.gsub(/<a href="(?<href>([^ '"]*))"([^>]*)?>([^<]*)<\/a>/){ $~[:href] }
# => "<p>http://localhost/activate/57f7e805827f</p>"
Is there any single-line string literal syntax in Ruby that allows string interpolation but does not interpret a backslash as an escape character?
I.e.,
Where ruby_var = "foo"
I want to be able to type the equivalent of C:\some\windows\path\#{ruby_var}\path resulting in the string C:\some\windows\path\foo\path without having to escape the backslashes or resort to a multi-line heredoc.
puts "C:\some\windows\path\#{ruby_var}\path"
puts "C:\some\windows\path\path_#{ruby_var}\path"
=> C: omewindowspath#{ruby_var}path
=> C: omewindowspathpath_foopath
puts 'C:\some\windows\path\#{ruby_var}\path'
puts 'C:\some\windows\path\path_#{ruby_var}\path'
=> C:\some\windows\path\#{ruby_var}\path
=> C:\some\windows\path\path_#{ruby_var}\path
puts %{C:\some\windows\path\#{ruby_var}\path}
puts %{C:\some\windows\path\path_#{ruby_var}\path}
=> C: omewindowspath#{ruby_var}path
=> C: omewindowspathpath_foopath
puts %q{C:\some\windows\path\#{ruby_var}\path}
puts %q{C:\some\windows\path\path_#{ruby_var}\path}
=> C:\some\windows\path\#{ruby_var}\path
=> C:\some\windows\path\path_#{ruby_var}\path
ruby_var = "hello"
puts 'C:\some\windows\path\%s\path' % ruby_var
#=>C:\some\windows\path\hello\path
'C:\some\windows\path\%s\path' % ruby_var
#=> 'C:\some\windows\path\foo\path'
I don't think it is possible.
You should consider using forward slashes instead to make it look prettier; I believe the standard ruby libraries in Windows won't care what kind of slashes you use.
There is also:
File.join('C:', 'path', ruby_var)
This question already has answers here:
Are strings mutable in Ruby?
(3 answers)
Closed 7 years ago.
Consider the following code:
$ irb
> s = "asd"
> s.object_id # prints 2171223360
> s[0] = ?z # s is now "zsd"
> s.object_id # prints 2171223360 (same as before)
> s += "hello" # s is now "zsdhello"
> s.object_id # prints 2171224560 (now it's different)
Seems like individual characters can be changed w/o creating a new string. However appending to the string apparently creates a new string.
Are strings in Ruby mutable?
Yes, strings in Ruby, unlike in Python, are mutable.
s += "hello" is not appending "hello" to s - an entirely new string object gets created. To append to a string 'in place', use <<, like in:
s = "hello"
s << " world"
s # hello world
ruby-1.9.3-p0 :026 > s="foo"
=> "foo"
ruby-1.9.3-p0 :027 > s.object_id
=> 70120944881780
ruby-1.9.3-p0 :028 > s<<"bar"
=> "foobar"
ruby-1.9.3-p0 :029 > s.object_id
=> 70120944881780
ruby-1.9.3-p0 :031 > s+="xxx"
=> "foobarxxx"
ruby-1.9.3-p0 :032 > s.object_id
=> 70120961479860
so, Strings are mutable, but += operator creates a new String. << keeps old
Appending in Ruby String is not +=, it is <<
So if you change += to << your question gets addressed by itself
Strings in Ruby are mutable, but you can change it with freezing.
irb(main):001:0> s = "foo".freeze
=> "foo"
irb(main):002:0> s << "bar"
RuntimeError: can't modify frozen String
Ruby Strings are mutable. But you need to use << for concatenation rather than +.
In fact concatenating string with
+ operator(immutable) because it creates new string object.
<< operator(mutable) because it changes in the same object.
From what I can make of this pull request, it will become possible in Ruby 3.0 to add a "magic comment" that will make all string immutable, rather than mutable.
Because it seems you have to explicitly add this comment, it seems like the answer to "are string mutable by default?" will still be yes, but a sort of conditional yes - depends on whether you wrote the magic comment into your script or not.
EDIT
I was pointed to this bug/issue on Ruby-Lang.org that definitively states that some type of strings in Ruby 3.0 will in fact be immutable by default.