0

I cannot understand why word is not capitalized or upcased.

class Book
  attr_accessor :title
  def title=(changed_title)
    changed_title.split(" ").map!{|word|
      word.to_s.capitalize
      }.join(" ")
    @title = changed_title
  end
end

book = Book.new
book.title = "check"
puts book.title
# >> check
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
macsplean
  • 611
  • 3
  • 8
  • 22

4 Answers4

8

split(' ') generates a temporary array. map! mutates that array. And then it's gone (because it wasn't assigned to anything).

changed_title remains in its original state ("check"), it was not mutated.

Here's a fix:

class Book
  attr_reader :title
  def title=(changed_title)
    @title = changed_title.split(" ").map(&:capitalize).join(" ")
  end
end

book = Book.new
book.title = "check"
puts book.title
# >> Check
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • thanks lots, and do you mind explaining the &:capitalize portion (specifically the &: part), I've seen it before, but couldn't find it in the docs. Is it just a shorthand for doing one method on whatever is mapped? – macsplean Oct 22 '13 at 09:09
  • 1
    @macsplean: yeah, exactly, just a shorthand. See [this answer](http://stackoverflow.com/q/1961030/125816), for example. – Sergio Tulentsev Oct 22 '13 at 09:13
3

Alternatively do

def title=(changed_title)
  @title = changed_title.gsub(/\w+/, &:capitalize)
end

The regex matches all words (one or more word characters, to be precise). gsub replaces all occurences with the return value of the block, which takes one match at a time and capitalizes it. Finally, the value is assigned to @title.

Edit — As pointed out by @SergioTulentsev, this will yield different results, e.g. the method originally used in the question will yield:

  • "$hello""$hello"
  • "   foo  bar ""Foo Bar"

The method presented in this answer will however yield:

  • "$hello""$Hello"
  • "   foo  bar ""   Foo  Bar "
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
  • This does something different. `title = '$hello'`. – Sergio Tulentsev Oct 22 '13 at 08:36
  • @SergioTulentsev good observation. However, the question is related to [this one](http://stackoverflow.com/questions/19488307/automatically-changing-variable-values-once-they-are-defined/19488328#19488328), where I gave the initial idea for the code the OP is asking about. Now I think the capitalization method posted here is just as good for the use case, but shorter. – Patrick Oscity Oct 22 '13 at 08:41
  • I prefer not to use regexes when I can (I'm not too smart), but your code is sure shorter. :) – Sergio Tulentsev Oct 22 '13 at 08:43
  • I can understand that, regexes can really get confusing. But when i have the option of using a short regex to write short code, i prefer this over long code. After all, short code is often easier to read. Although again with regexes that's not necessarily the case :) – Patrick Oscity Oct 22 '13 at 08:45
  • Btw as pointed out in the previous question the `split` way will also discard whitespace, e.g. ` foo bar ` --> `Foo Bar` while the method posted here will preserve whitespace. – Patrick Oscity Oct 22 '13 at 08:46
  • I like this one... so *+1*. – Arup Rakshit Oct 22 '13 at 08:47
  • Well, it depends on OP's needs. Maybe he needs to collapse whitespace. – Sergio Tulentsev Oct 22 '13 at 08:47
  • But then again, maybe collapsing whitespace should be a separate step and not happen "magically" in the background. It may not be obvious from the code that whitespace is collapsed, which I think is not optimal. – Patrick Oscity Oct 22 '13 at 08:51
2
def title=(changed_title)
  @title = changed_title.split.map(&:capitalize).join(' ')
end

or if you using rails

# require if not rails app
#require 'rails'
# or to not require all rails as @p11y suggested
#require 'active_support/core_ext/string/inflections'

def title=(changed_title)
  @title = changed_title.titleize
end
Waterlink
  • 2,239
  • 1
  • 17
  • 27
1
a = "a big brown fox"
a = a.split.map(&:capitalize!).join(" ") #=> A Big Brown Fox
Bala
  • 11,068
  • 19
  • 67
  • 120