Why does Rails titlecase add a space to a name?

2020-05-25 07:04发布

Why does the titlecase mess up the name? I have:

John Mark McMillan

and it turns it into:

>> "john mark McMillan".titlecase
=> "John Mark Mc Millan"

Why is there a space added to the last name?

Basically I have this in my model:

before_save :capitalize_name

def capitalize_name
  self.artist = self.artist.titlecase
end

I am trying to make sure that all the names are titlecase in the DB, but in situtations with a camelcase name it fails. Any ideas how to fix this?

9条回答
劳资没心,怎么记你
2楼-- · 2020-05-25 07:35

Hmm, that's odd.. but you could write a quick custom regex to avoid using that method.

class String
    def custom_titlecase
        self.gsub(/\b\w/) { |w| w.upcase }
    end
end

"John Mark McMillan".custom_titlecase    # => "John Mark McMillan"

Source

查看更多
Summer. ? 凉城
3楼-- · 2020-05-25 07:39

If you want to handle the case where someone has entered JOHN CAPSLOCK JOE as well as the others, I combined this one:

class String
  def proper_titlecase
    if self.titleize.split.length == self.split.length
      self.titleize
    else
      self.split(" ").collect{|word| word[0] = word[0].upcase; word}.join(" ")
    end
  end
end

Depends if you want that kinda logic on a String method ;)

查看更多
手持菜刀,她持情操
4楼-- · 2020-05-25 07:44

Edited (inspired by The Tin Man's suggestion)

A hack will be:

class String
  def titlecase
    gsub(/(?:_|\b)(.)/){$1.upcase}
  end
end

p "john mark McMillan".titlecase
# => "John Mark McMillan"

Note that the string 'john mark McMillan' is inconsistent in capitalization, and is somewhat unexpected as a human input, or if it is not from a human input, you probably should not have the strings stored in that way. A string like 'john mark mc_millan' is more consistent, and would more likely appear as a human input if you define such convention. My answer will handle these cases as well:

p "john mark mc_millan".titlecase
# => "John Mark McMillan"
查看更多
别忘想泡老子
5楼-- · 2020-05-25 07:44

The "Why" question has already been answered...but as evidenced by the selected answer and upvotes, I think what most of us are ACTUALLY wanting is a silver bullet to deal with the hell that is name-formatting...While multiple capitals trigger that behavior, I've found that hyphenated names do the same.

These cases and many more have already been handled in the gem, NameCase.

In version 2.0 it only converts a string if the string is all uppercase or all lowercase, based on a defined ruleset as a best guess. I like this, because I'm sure the ruleset can never be 100% correct. Example, Ian McDonald (from Scotland) has a different capitalization from Ian Mcdonald (from Ireland)...however those names will be handled correctly at the time of input if the user is particular and if not, the name can be corrected if needed and retain its formatting.

My Solution:

# If desired, add string method once NameCase gem is added
class String

  def namecase
    NameCase(self)
  end

end

Tests: (name.namecase)

test_names = ["john mark McMillan", "JOHN CAPSLOCK JOE", "test name", "test name-name", "test McName-name", "John w McHENRY", "ian mcdonald", "Ian McDonald", "Ian Mcdonald"]

test_names.each { |name| puts '# "' + name + '" => "' + name.namecase + '"' }
  # "john mark McMillan" => "John Mark McMillan"
  # "JOHN CAPSLOCK JOE" => "John Capslock Joe"
  # "test name" => "Test Name"
  # "test name-name" => "Test Name-Name"
  # "test McName-name" => "Test McName-Name"
  # "John w McHENRY" => "John w McHENRY" -FAIL
  # "ian mcdonald" => "Ian McDonald"
  # "Ian McDonald" => "Ian McDonald"
  # "Ian Mcdonald" => "Ian Mcdonald"

If you feel you need to handle all of the corner cases on this page and don't care about losing names that may have been formatted at the start, eg. Ian Mcdonald (from Ireland)...you could use upcase first:

Tests: (name.upcase.namecase)

test_names.each { |name| puts '# "' + name + '" => "' + name.upcase.namecase + '"' }
  # "john mark McMillan" => "John Mark McMillan"
  # "JOHN CAPSLOCK JOE" => "John Capslock Joe"
  # "test name" => "Test Name"
  # "test name-name" => "Test Name-Name"
  # "test McName-name" => "Test McName-Name"
  # "John w McHENRY" => "John W McHenry"
  # "ian mcdonald" => "Ian McDonald"
  # "Ian McDonald" => "Ian McDonald"
  # "Ian Mcdonald" => "Ian McDonald"

The only silver bullet is to go old school...ALL CAPS. But who wants that eyesore in their modern web app?

查看更多
forever°为你锁心
6楼-- · 2020-05-25 07:45

If all you want is to ensure that each word starts with a capital:

class String
  def titlecase2
    self.split(' ').map { |w| w[0] = w[0].upcase; w }.join(' ')
  end
end

irb(main):016:0> "john mark McMillan".titlecase2
=> "John Mark McMillan"
查看更多
太酷不给撩
7楼-- · 2020-05-25 07:45

The documentation for titlecase says ([emphasis added]):

Capitalizes all the words and replaces some characters in the string to create a nicer looking title. titleize is meant for creating pretty output. It is not used in the Rails internals.

I'm only guessing here, but perhaps it regards PascalCase as a problem - maybe it thinks it's the name of a ActiveRecordModelClass.

查看更多
登录 后发表回答