Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

[PATCH] Uncountable singularize uses regex match while pluralize uses exact match leading leading to unexpected collisions

#6093

I have an model named "ors" which is both singular and plural (e.g. it's uncountable)

ActiveSupport::Inflector.inflections do |inflect|
  inflect.uncountable %w( ors )
end

The inflector works beautifully

"ors".pluralize => "ors"
"ors".singularize => "ors"
"ors".pluralize.singularize => "ors"

Then I add some scaffolding for my Sponsor model and everything goes crazy with the tests -- long story short the pluralization/singularization is not right. I try the same inflecting:

"sponsor".pluralize => "sponsors"
"sponsors".singularize => "sponsors"
"sponsor".pluralize.singularize => "sponsors"

After some WTF time, I try a clean Rails3 project and "sponsors".singularize => "sponsor" as expected, so I realize it might be the "ors" inflection. I add the uncountable "ors" inflection to the clean Rails3 project and get the problematic "sponsors" singular inflection. I remove it, problem stops. I verify it with "razors" to make sure I'm not crazy:

"razor".pluralize => "razors"
"razors".singularize => "razors"
"razor".pluralize.singularize => "razors"

I'm going to assume this was not the intended behavior but I know there's lots of back and forth discussion on inflections and lack of interest in changing them. The core problem for me here was that setting an uncountable inflection had the side effect of changing a number of other inflections (sponsor, donor, razor). The reason I think it's a bug is that it's not reflexive -- "string".pluralize.singularize == "string" IMHO.

The problem is in #singularize on line 151 of https://github.com/rails/rails/blob/4eab983b955fe17cf02c6fe96cab1c8...

151 if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i }

The matching #pluralize is specific

132 if word.empty? || inflections.uncountables.include?(result.downcase)

For what it's worth, I couldn't get "ors" to work as an irregular either since that generates ending/suffix matches instead of word matches.

I looked at the tests in ActiveSupport::Inflections and it's also silent on the details of irregular words:

jeans => jeans
funky jeans => funky jeans
bluejeans => ... (it's not there to settle the issue)

If the intention is matching the ending fragment, as #singularize is currently written, then

bluejeans => bluejeans
sponsors => sponsors

given inflect.uncountables = %w( jeans ors)

This is definitely broken for the word "sponsors" since there is a singular that's different.

If instead, the intention is the word, as "funky jeans" suggests, then adding a word boundary to the regex fixes "sponsors" and retains "funky jeans". But it breaks "bluejeans"

It seems like I'm making a big deal, bit the key thing for me is that I can't use "ors" as an @uncountable without breaking things and I can't use it as an irregular because the singular and plurals generated from that match and word containing /or(s)/. Right now, my only solution other than refactoring the name of the object is to manually set the singular and plural

inflect.plural /^(ors)$/i, '\1'
inflect.singular /^(ors)$/i, '\1'

I would have thought that was what uncountable was supposed to do.

Reported by John Paul Ashenfelter · November 30th, 2010 @ 06:37 PM

State: resolved
Milestone: 3.0.5
Assigned to: Xavier Noria Xavier Noria
Importance: Low

Activity

  1. John Paul Ashenfelter
  2. John Paul Ashenfelter
  3. John Paul Ashenfelter
    John Paul Ashenfelter
    • Title changed from Uncountable singularize uses regex match while pluralize uses exact match leading leading to unexpected collisions to [PATCH] Uncountable singularize uses regex match while pluralize uses exact match leading leading to unexpected collisions

    November 30th, 2010 @ 06:56 PM

  4. John Paul Ashenfelter
    John Paul Ashenfelter
    • Tag changed from bug, patch to activesupport, bug, patch

    November 30th, 2010 @ 07:11 PM

  5. Aditya Sanghi
    Aditya Sanghi
    • State changed from new to open
    • Milestone cleared.
    • Assigned user set to Xavier Noria
    • Importance changed from to Low

    December 1st, 2010 @ 08:55 AM

  6. Lake
    Lake

    +1 Tests are passing on master.

    Nicely done, JP.

    December 6th, 2010 @ 03:01 AM

  7. Vijay Dev
  8. Piotr Sarnacki
    Piotr Sarnacki

    John: only one thing, you should move the last line of second test to ensure:

    ensure
      ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_uncountables
    end
    

    if that test breaks, other tests will not break because of that.

    December 18th, 2010 @ 08:53 AM

  9. Repository
    Repository
    • State changed from open to resolved

    (from [9b4622a483319f3d1e7f4489442f0d86afb6da36]) Added a word boundary to uncountable inflection regex for #singularize so short inflections like ors do not affect larger words like sponsors [#6093 [PATCH] Uncountable singularize uses regex match while pluralize uses exact m... state:resolved] https://github.com/rails/rails/commit/9b4622a483319f3d1e7f4489442f0...

    December 22nd, 2010 @ 08:45 AM

  10. Repository
    Repository

    (from [cad4f0030969476941ec62096b9c84070355b823]) Added a word boundary to uncountable inflection regex for #singularize so short inflections like ors do not affect larger words like sponsors [#6093 [PATCH] Uncountable singularize uses regex match while pluralize uses exact m... state:resolved] https://github.com/rails/rails/commit/cad4f0030969476941ec62096b9c8...

    December 22nd, 2010 @ 08:48 AM

  11. Santiago Pastorino