@@ -815,64 +815,34 @@ module ActionDispatch {
815
815
* Convert a camel-case string to underscore case. Converts `::` to `/`.
816
816
* This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle.
817
817
* Note: All-uppercase words like `CONSTANT` are not handled correctly.
818
- * TODO: is there a more concise way to write this?
819
818
*/
820
- bindingset [ input]
821
- string underscore ( string input ) {
819
+ bindingset [ base]
820
+ string underscore ( string base ) {
821
+ base = "" and result = ""
822
+ or
822
823
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
+ )
876
846
}
877
847
878
848
/**
0 commit comments