Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Convert string into undescore, avoid the "/" in the resulting string

I have a name spaced class..

"CommonCar::RedTrunk"

I need to convert it to an underscored string "common_car_red_trunk", but when I use

"CommonCar::RedTrunk".underscore, I get "common_car/red_trunk" instead.

Is there another method to accomplish what I need?

like image 998
Lefty Avatar asked Sep 19 '25 02:09

Lefty


2 Answers

Solutions:

"CommonCar::RedTrunk".gsub(':', '').underscore

or:

"CommonCar::RedTrunk".sub('::', '').underscore

or:

"CommonCar::RedTrunk".tr(':', '').underscore

Alternate:

Or turn any of these around and do the underscore() first, followed by whatever method you want to use to replace "/" with "_".

Explanation:

While all of these methods look basically the same, there are subtle differences that can be very impactful.

In short:

  • gsub() – uses a regex to do pattern matching, therefore, it's finding any occurrence of ":" and replacing it with "".

  • sub() – uses a regex to do pattern matching, similarly to gsub(), with the exception that it's only finding the first occurrence (the "g" in gsub() meaning "global"). This is why when using that method, it was necessary to use "::", otherwise a single ":" would have been left. Keep in mind with this method, it will only work with a single-nested namespace. Meaning "CommonCar::RedTrunk::BigWheels" would have been transformed to "CommonCarRedTrunk::BigWheels".

  • tr() – uses the string parameters as arrays of single character replacments. In this case, because we're only replacing a single character, it'll work identically to gsub(). However, if you wanted to replace "on" with "EX", for example, gsub("on", "EX") would produce "CommEXCar::RedTrunk" while tr("on", "EX") would produce "CEmmEXCar::RedTruXk".

Docs:

https://apidock.com/ruby/String/gsub

https://apidock.com/ruby/String/sub

https://apidock.com/ruby/String/tr

like image 88
jeffdill2 Avatar answered Sep 22 '25 15:09

jeffdill2


This is a pure-Ruby solution.

r = /(?<=[a-z])(?=[A-Z])|::/
"CommonCar::RedTrunk".gsub(r, '_').downcase
  #=> "common_car_red_trunk"

See (the first form of) String#gsub and String#downcase.

The regular expression can be made self-documenting by writing it in free-spacing mode:

r = /
    (?<=[a-z]) # assert that the previous character is lower-case
    (?=[A-Z])  # assert that the following character is upper-case
    |          # or
    ::         # match '::'
    /x         # free-spacing regex definition mode

(?<=[a-z]) is a positive lookbehind; (?=[A-Z]) is a positive lookahead.

Note that /(?<=[a-z])(?=[A-Z])/ matches an empty ("zero-width") string. r matches, for example, the empty string between 'Common' and 'Car', because it is preceeded by a lower-case letter and followed by an upper-case letter.

I don't know Rails but I'm guessing you could write

"CommonCar::RedTrunk".delete(':').underscore
like image 39
Cary Swoveland Avatar answered Sep 22 '25 14:09

Cary Swoveland