What exactly is gsub in Ruby? - ruby

I'm still learning Ruby and I'm practicing my get routes and post requests, User sign in, log in, etc for a basic website and I came across a "slug" method:
def slug
self.username.strip.downcase.gsub(" ","-")
end
I tried using irb to try and figure out what gsub is doing:
"hello".gsub(" ","-")
but it just gave me:
"hello"
And I tried using it on a array, but that didn't work as well.
But my question is what exactly is gsub doing here? What is gsub in general? What is the outcome of this method?
Thanks in advance!

In Ruby, Gsub is a method that can be called on strings. It replaces all instances of a substring with another one inside the string.
Sub is short for "substitute," and G stands for "global." Think of Gsub like a "replace all" function.
The general pattern is str.gsub("target string", "replacement string").
In your example, nothing changes because there are no spaces in the string "hello" that can be replaced with a "-" character.
"hello".gsub(" ","-")
The following example replaces the "!" in "hello!" with ", world", printing "hello, world!"
puts "hello!".gsub("!",", world!")
The following example replaces every instance of "!" in "!!!" with "123", printing "123123123"
puts "!!!".gsub("!", "123")

Related

How to change case of letters in string using RegEx in Ruby

Say I have a string : "hEY "
I want to convert it to "Hey "
string.gsub!(/([a-z])([A-Z]+ )/, '\1'.upcase)
That is the idea I have, but it seems like the upcase method does nothing when I use it within the gsub method. Why is that?
EDIT: I came up with this method:
string.gsub!(/([a-z])([A-Z]+ )/) { |str| str.downcase!.capitalize! }
Is there a way to do this within the regex though? I don't really understand the '\1' '\2' thing. Is that backreferencing? How does that work
#sawa Has the simple answer, and you've edited your question with another mechanism. However, to answer two of your questions:
Is there a way to do this within the regex though?
No, Ruby's regex does not support a case-changing feature as some other regex flavors do. You can "prove" this to yourself by reviewing the official Ruby regex docs for 1.9 and 2.0 and searching for the word "case":
https://github.com/ruby/ruby/blob/ruby_1_9_3/doc/re.rdoc
https://github.com/ruby/ruby/blob/ruby_2_0_0/doc/re.rdoc
I don't really understand the '\1' '\2' thing. Is that backreferencing? How does that work?
Your use of \1 is a kind of backreference. A backreference can be when you use \1 and such in the search pattern. For example, the regular expression /f(.)\1/ will find the letter f, followed by any character, followed by that same character (e.g. "foo" or "f!!").
In this case, within a replacement string passed to a method like String#gsub, the backreference does refer to the previous capture. From the docs:
"If replacement is a String it will be substituted for the matched text. It may contain back-references to the pattern’s capture groups of the form \d, where d is a group number, or \k<n>, where n is a group name. If it is a double-quoted string, both back-references must be preceded by an additional backslash."
In practice, this means:
"hello world".gsub( /([aeiou])/, '_\1_' ) #=> "h_e_ll_o_ w_o_rld"
"hello world".gsub( /([aeiou])/, "_\1_" ) #=> "h_\u0001_ll_\u0001_ w_\u0001_rld"
"hello world".gsub( /([aeiou])/, "_\\1_" ) #=> "h_e_ll_o_ w_o_rld"
Now, you have to understand when code runs. In your original code…
string.gsub!(/([a-z])([A-Z]+ )/, '\1'.upcase)
…what you are doing is calling upcase on the string '\1' (which has no effect) and then calling the gsub! method, passing in a regex and a string as parameters.
Finally, another way to achieve this same goal is with the block form like so:
# Take your pick of which you prefer:
string.gsub!(/([a-z])([A-Z]+ )/){ $1.upcase << $2.downcase }
string.gsub!(/([a-z])([A-Z]+ )/){ [$1.upcase,$2.downcase].join }
string.gsub!(/([a-z])([A-Z]+ )/){ "#{$1.upcase}#{$2.downcase}" }
In the block form of gsub the captured patterns are set to the global variables $1, $2, etc. and you can use those to construct the replacement string.
I don't know why you are trying to do it in a complicated way, but the usual way is:
"hEY".capitalize # => "Hey"
If you insist in using a regex and upcase, then you would also need downcase:
"hEY".downcase.sub(/\w/){$&.upcase} # => "Hey"
If you really want to just swap the case of every letter in the string, you can avoid the complexity of regex entirely because There's A Method For That™.
"hEY".swapcase # => "Hey"
"HellO thERe".swapcase # => "hELLo THerE"
There's also swapcase! to do it destructively.

Ruby Koans - Regex and .sub: Don't understand reason behind answer

For clarification, here's the exact question in the about_regular_expressions.rb file that I'm having trouble with:
def test_sub_is_like_find_and_replace
assert_equal __, "one two-three".sub(/(t\w*)/) { $1[0, 1] }
end
I know what the answer is to this, but I don't understand what's happening to get that answer. I'm pretty new to Ruby and to regex, and in particular I'm confused about the code between the braces and how that's coming into play.
The code inside the braces is a block that sub uses to replace the match:
In the block form [...] The value returned by the block will be substituted for the match on each call.
The block receives the match as an argument but the usual regex variables ($1, $2, ...) are also available.
In this specific case, the $1 inside the block is "two" and the array notation extracts the first character of $1 (which is "t" in this case). So, the block returns "t" and sub replaces the "two" in the original string with just "t".

how to change values between tags in a string

If I have the following string:
str="hello %%one_boy%%'s something %%four_girl%%'s something more"
how would I edit it to get the following output from printing str:
"hello ONE_BOY's something FOUR_GIRL's something more"
I have been trying to use 'gsub' and 'upcase' methods but am struggling with the regex to get each word between my '%%' symbols.
ruby-1.9.2-p136 :066 > str.gsub(/%%([^%]+)%%/) {|m| $1.upcase}
=> "hello ONE_BOY's something FOUR_GIRL's something more"
The [^%]+ says it will match 1 or more characters except %, and the $1is a global variable that stores the back reference to what was matched.
s.gsub(/%%([^%]+)%%/) { $1.upcase }
Here's a quick and dirty way:
"hello %%one_boy%%'s something %%four_girl%%'s something more".gsub(/(%%.*?%%)/) do |x|
x[2 .. (x.length-3)].upcase
end
The x[2 .. (x.length-3)] bit slices out the middle of the match (i.e. strips off the leading and trailing two characters).
If you're able to choose the delimiters, you might be able to use String.interpolate from the Facets gem:
one_boy = "hello".upcase
str = "\#{one_boy}!!!"
String.interpolate{ str } #=> "HELLO!!!"
But I'd first check that Facets doesn't cause any conflicts with Rails.
str.gsub(/%%([^%]+)%%/) { |match| $1.upcase }

English Sentence to Camel-cased method name

I had to convert a series of sentences into camel-cased method names. I ended writing something for it. I am still curious if there's something simpler for it.
Given the string a = "This is a test." output thisIsATest
I used for following:
a.downcase.gsub(/\s\w/){|b| b[-1,1].upcase }
Not sure it's better as your solution but it should do the trick:
>> "This is a test.".titleize.split(" ").join.camelize(:lower)
=> "thisIsATest."
titleize: uppercase every first letter of each word
split(" ").join: create an array with each word and join to squeeze the spaces out
camelize(:lower): make the first letter lowercase
You can find some more fun functions in the Rails docs: http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html
"active_record".camelize(:lower)
output : "activeRecord"
use these
"Some string for you".gsub(/\s+/,'_').camelize(:lower) #=> "someStringForYou"
gsub: Replace spaces by underscores
camelize: java-like method camelcase
You might try using the 'English' gem, available at http://english.rubyforge.org/
require 'english/case'
a = "This is a test."
a.camelcase().uncapitalize() # => 'thisIsATest

Sinatra does not support multiple lines?

For the following code, why does only "World" gets printed
get '/' do
"Hello"
"World"
end
This has nothing to do with sinatra itself. It just uses the return value of the block and in ruby the return value is the last evaluated expression, which in your case is "World". This might work for you:
get '/' do
r = "Hello"
r += "World"
end
In this case you add as many string values to r as you want and the last expression would return the complete string "HelloWorld".
Correct me if I'm wrong, but I do believe in plain ruby, the last line evaluated is what gets returned.
Tomas correctly answered your question, but one way to do what I think you're meaning to do (output multiple lines), you could use:
get '/' do
output =<<EOS
Hello
World
EOS
output
end
You could use a line break char to separate lines..
get '/' do
"Hello\nWorld"
end
Don't confuse your controller with your view.
What you're probably looking for is this:
get '/' do
haml :hello_world
end
And then in views/hello_world.haml:
Hello
World
I agree with Matt.
If you want you can use that method with one file too.
get '/' do
erb :hello_world
end
__END__
##hello_world
hello
world
I just use puts inside my controller to get some debug printed to STDOUT.

Resources