Rails Inflector

33 views
Skip to first unread message

garru

unread,
Apr 1, 2008, 9:16:10 PM4/1/08
to Iowa City Ruby Group
Hi Everyone,

So I've run into a problem with the way Rails handles camel casing and
I'd like your opinion on a solution I came up with.

The problem is that I can't name any model, controller, class with an
acronym.

For example Rails expects

app/controllers/vpr/test.rb expects to define
Vpr::TestController

However I want it to be defined VPR::TestController.

This is because rails uses the Inflector class to camelcase the path
'vpr/test' to create the expected name.

In rails the Inflector singleton class handles pluralizing (person =>
people) along with several other things. In core rails they give you
the ability to add your own rules for pluralizing, but not for
camelcase.

This afternoon I added my own way of handling exceptions for
camelcasing to allow for acronyms. Can anyone think of a reason why
this was left out?


Benjamin Oakes

unread,
Apr 1, 2008, 10:10:53 PM4/1/08
to ic-...@googlegroups.com
Hi Gary,

I wrote a Ruby script a few months ago which was used to batch-rename
a bunch of files from camel case to underscored, since I couldn't find
a ready made program to do so. I never had to change the way Rails
behaves, but I did base the string conversion stuff on how Rails does
it. Here's my modified "decamelize" which handles acronyms
correctly: (Hopefully it's not too horrible -- it did what I needed
at the time.)

{{{
module Inflector
# Converts converts a camel case string (i.e. CamelCase) to its
decamelized
# version. It makes several special considerations when splitting
into "words".
# For example, "&" is considered a word by itself, as are digits.
(Please refer
# to the unit tests for a completely accurate description.)
#
# Examples:
# "CamelCase" => "Camel_Case"
# "Space Separated" => "Space_Separated"
# "XMLThingy" => "XML_Thingy"
def decamelize
self.to_s.
gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2').
gsub(/([a-z]+)([A-Z\d])/, '\1_\2').
gsub(/([A-Z]{2,})(\d+)/i, '\1_\2').
gsub(/(\d+)([a-z])/i, '\1_\2').
gsub(/(.+?)\&(.+?)/, '\1_&_\2').
gsub(/\s/, '_')
end
end

# And the corresponding tests. You'll have to include Inflector in
String to get them to work:

class StringTest < Test::Unit::TestCase
# Baseline tests generated before using real file names.
def test_decamelize
assert_equal('Hello_World', 'HelloWorld'.decamelize)
assert_equal('This_Is_A_Longer_Test',
'ThisIsALongerTest'.decamelize)
assert_equal('A_Two-fold_Test', 'ATwo-foldTest'.decamelize)
assert_equal('BDSCHED', 'BDSCHED'.decamelize)
assert_equal('XML_Thingy', 'XMLThingy'.decamelize)
assert_equal('YAML_Thingy', 'YAMLThingy'.decamelize)
assert_equal('XML_And_YAML_Thingy', 'XMLAndYAMLThingy'.decamelize)
assert_equal('PHP_And_Perl', 'PHPAndPerl'.decamelize)
assert_equal('UNIX_And_Linux_Are_Different',
'UNIXAndLinuxAreDifferent'.decamelize)
assert_equal('Mac_OS', 'MacOS'.decamelize)
assert_equal("F8796403.tmp", "F8796403.tmp".decamelize)
assert_equal('This_Is_1_Test_With_Numbers',
'ThisIs1TestWithNumbers'.decamelize)
assert_equal('This_has_spaces', 'This has spaces'.decamelize)
end

#
=
=
=
=
=
=
=
=
=
=
=
=
========================================================================
# The following tests (and bugs) were extrapolated from the
initial output of this
# program and its subsequent revisions.
#
=
=
=
=
=
=
=
=
=
=
=
=
========================================================================

def test_decamelize_gives_lowercase_after_numbers
# Bugs:
# oct05coords.doc -> oct_05coords.doc
# 2001payroll.pdf -> 2001payroll.pdf
# w4federal2001.pdf -> w_4federal_2001.pdf
# w4state2000.pdf -> w_4state_2000.pdf
# 457amountchange.pdf -> 457amountchange.pdf
# fy08stratplanweb.doc -> fy_08stratplanweb.doc
# fy08withtasksafter61807coords.doc ->
fy_08withtasksafter_61807coords.doc
# hp4050tnug.pdf -> hp_4050tnug.pdf
# hp4050tngs.pdf -> hp_4050tngs.pdf
# hp4050tnqr.pdf -> hp_4050tnqr.pdf

assert_equal("oct_05_coords.doc", "oct05coords.doc".decamelize)
assert_equal("2001_payroll.pdf", "2001payroll.pdf".decamelize)
assert_equal("w_4_federal_2001.pdf",
"w4federal2001.pdf".decamelize)
assert_equal("w_4_state_2000.pdf", "w4state2000.pdf".decamelize)

assert_equal("457_amountchange.pdf",
"457amountchange.pdf".decamelize)
assert_equal("fy_08_stratplanweb.doc",
"fy08stratplanweb.doc".decamelize)
assert_equal("fy_08_withtasksafter_61807_coords.doc",
"fy08withtasksafter61807coords.doc".decamelize)
assert_equal("hp_4050_tnug.pdf", "hp4050tnug.pdf".decamelize)
assert_equal("hp_4050_tngs.pdf", "hp4050tngs.pdf".decamelize)
assert_equal("hp_4050_tnqr.pdf", "hp4050tnqr.pdf".decamelize)
end


def test_decamelize_surrounds_ampersand_with_underscores
# Bugs:
# CustRelations&AccessStrategies.doc ->
Cust_Relations&Access_Strategies.doc
# Circ&TSFAQ.doc -> Circ&TSFAQ.doc

assert_equal("Cust_Relations_&_Access_Strategies.doc",
"CustRelations&AccessStrategies.doc".decamelize)
assert_equal("Circ_&_TSFAQ.doc", "Circ&TSFAQ.doc".decamelize)
end

def test_decamelize_inserts_underscore_between_uppercase_and_numbers
# Bugs:
# MoviesETV2007.doc -> Movies_ETV2007.doc
# MusicCD2003.doc -> Music_CD2003.doc
# NonfictionDVD2002.doc -> Nonfiction_DVD2002.doc
# PaperbacksAdult&YA2005.doc -> Paperbacks_Adult&YA2005.doc
# AFSCMEContractOLD2003.pdf -> AFSCME_Contract_OLD2003.pdf
# AFSCMEContractOLD2007.pdf -> AFSCME_Contract_OLD2007.pdf

assert_equal("Movies_ETV_2007.doc", "MoviesETV2007.doc".decamelize)
assert_equal("Music_CD_2003.doc", "MusicCD2003.doc".decamelize)
assert_equal("Nonfiction_DVD_2002.doc",
"NonfictionDVD2002.doc".decamelize)
assert_equal("Paperbacks_Adult_&_YA_2005.doc",
"PaperbacksAdult&YA2005.doc".decamelize)
assert_equal("AFSCME_Contract_OLD_2003.pdf",
"AFSCMEContractOLD2003.pdf".decamelize)
assert_equal("AFSCME_Contract_OLD_2007.pdf",
"AFSCMEContractOLD2007.pdf".decamelize)
end
end
}}}

I hope this helps a little.

-- Ben

> --~--~---------~--~----~------------~-------~--~----~
> You received this message because you are subscribed to the Google
> Groups "Iowa City Ruby Group" group.
> To post to this group, send email to ic-...@googlegroups.com
> To unsubscribe from this group, send email to ic-ruby-u...@googlegroups.com
> For more options, visit this group at http://groups.google.com/group/ic-ruby?hl=en
> -~----------~----~----~----~------~----~------~--~---
>

Benjamin Oakes

unread,
Apr 1, 2008, 10:16:15 PM4/1/08
to ic-...@googlegroups.com
Argh -- the formatting got messed up. The code is now attached.

-- Ben

decamelize.rb

garru

unread,
Apr 2, 2008, 1:21:22 PM4/2/08
to Iowa City Ruby Group
So Ben came up with this great idea of adding acronym as a helper
method for some changes I was working on. And we created a patch. i'm
trolling for +1s

http://dev.rubyonrails.org/ticket/11511


On Apr 1, 9:16 pm, Benjamin Oakes <benjamin.d.oa...@gmail.com> wrote:
> Argh -- the formatting got messed up.  The code is now attached.
>
> -- Ben
>
>  decamelize.rb
> 4KDownload
>  smime.p7s
> 3KDownload

Denny

unread,
Apr 28, 2008, 10:58:22 AM4/28/08
to Iowa City Ruby Group
That's really cool. Good work, guys.
Reply all
Reply to author
Forward
0 new messages