Skip to content

Commit 3786fbf

Browse files
committed
Ruby: Rewrite ActionDispatch::underscore
This version is much shorter and hopefully performs a bit better.
1 parent eff2136 commit 3786fbf

File tree

2 files changed

+27
-57
lines changed

2 files changed

+27
-57
lines changed

ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -815,64 +815,34 @@ module ActionDispatch {
815815
* Convert a camel-case string to underscore case. Converts `::` to `/`.
816816
* This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle.
817817
* Note: All-uppercase words like `CONSTANT` are not handled correctly.
818-
* TODO: is there a more concise way to write this?
819818
*/
820-
bindingset[input]
821-
string underscore(string input) {
819+
bindingset[base]
820+
string underscore(string base) {
821+
base = "" and result = ""
822+
or
822823
result =
823-
decapitalize(input
824-
.regexpReplaceAll("([^:])A", "$1_a")
825-
.regexpReplaceAll("([^:])B", "$1_b")
826-
.regexpReplaceAll("([^:])C", "$1_c")
827-
.regexpReplaceAll("([^:])D", "$1_d")
828-
.regexpReplaceAll("([^:])E", "$1_e")
829-
.regexpReplaceAll("([^:])F", "$1_f")
830-
.regexpReplaceAll("([^:])G", "$1_g")
831-
.regexpReplaceAll("([^:])H", "$1_h")
832-
.regexpReplaceAll("([^:])I", "$1_i")
833-
.regexpReplaceAll("([^:])J", "$1_j")
834-
.regexpReplaceAll("([^:])K", "$1_k")
835-
.regexpReplaceAll("([^:])L", "$1_l")
836-
.regexpReplaceAll("([^:])M", "$1_m")
837-
.regexpReplaceAll("([^:])N", "$1_n")
838-
.regexpReplaceAll("([^:])O", "$1_o")
839-
.regexpReplaceAll("([^:])P", "$1_p")
840-
.regexpReplaceAll("([^:])Q", "$1_q")
841-
.regexpReplaceAll("([^:])R", "$1_r")
842-
.regexpReplaceAll("([^:])S", "$1_s")
843-
.regexpReplaceAll("([^:])T", "$1_t")
844-
.regexpReplaceAll("([^:])U", "$1_u")
845-
.regexpReplaceAll("([^:])V", "$1_v")
846-
.regexpReplaceAll("([^:])W", "$1_w")
847-
.regexpReplaceAll("([^:])X", "$1_x")
848-
.regexpReplaceAll("([^:])Y", "$1_y")
849-
.regexpReplaceAll("([^:])Z", "$1_z")
850-
.regexpReplaceAll("::A", "/a")
851-
.regexpReplaceAll("::B", "/b")
852-
.regexpReplaceAll("::C", "/c")
853-
.regexpReplaceAll("::D", "/d")
854-
.regexpReplaceAll("::E", "/e")
855-
.regexpReplaceAll("::F", "/f")
856-
.regexpReplaceAll("::G", "/g")
857-
.regexpReplaceAll("::H", "/h")
858-
.regexpReplaceAll("::I", "/i")
859-
.regexpReplaceAll("::J", "/j")
860-
.regexpReplaceAll("::K", "/k")
861-
.regexpReplaceAll("::L", "/l")
862-
.regexpReplaceAll("::M", "/m")
863-
.regexpReplaceAll("::N", "/n")
864-
.regexpReplaceAll("::O", "/o")
865-
.regexpReplaceAll("::P", "/p")
866-
.regexpReplaceAll("::Q", "/q")
867-
.regexpReplaceAll("::R", "/r")
868-
.regexpReplaceAll("::S", "/s")
869-
.regexpReplaceAll("::T", "/t")
870-
.regexpReplaceAll("::U", "/u")
871-
.regexpReplaceAll("::V", "/v")
872-
.regexpReplaceAll("::W", "/w")
873-
.regexpReplaceAll("::X", "/x")
874-
.regexpReplaceAll("::Y", "/y")
875-
.regexpReplaceAll("::Z", "/z"))
824+
base.charAt(0).toLowerCase() +
825+
// Walk along the string, keeping track of the previous character
826+
// in order to determine if we've crossed a boundary.
827+
// Boundaries are:
828+
// - lower case to upper case: B in FooBar
829+
// - entering a namespace: B in Foo::Bar
830+
concat(int i, string prev, string char |
831+
prev = base.charAt(i) and
832+
char = base.charAt(i + 1)
833+
|
834+
any(string s |
835+
char.regexpMatch("[A-Za-z0-9]") and
836+
if prev = ":"
837+
then s = "/" + char.toLowerCase()
838+
else
839+
if prev.isLowercase() and char.isUppercase()
840+
then s = "_" + char.toLowerCase()
841+
else s = char.toLowerCase()
842+
)
843+
order by
844+
i
845+
)
876846
}
877847

878848
/**

ruby/ql/test/library-tests/frameworks/ActionDispatch.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ underscore
4545
| Foo::Bar::BazQuux | foo/bar/baz_quux |
4646
| FooBar | foo_bar |
4747
| FooBar::Baz | foo_bar/baz |
48-
| HTTPServerRequest | h_tT_p_server_request |
48+
| HTTPServerRequest | httpserver_request |
4949
| LotsOfCapitalLetters | lots_of_capital_letters |
5050
| invalid | invalid |

0 commit comments

Comments
 (0)