@@ -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 /**
0 commit comments