diff --git a/lib/titleize.rb b/lib/titleize.rb index e88124f..870fb8c 100644 --- a/lib/titleize.rb +++ b/lib/titleize.rb @@ -17,10 +17,25 @@ module Titleize # # "notes on a scandal" # => "Notes on a Scandal" # "the good german" # => "The Good German" - def titleize(title) + # + # Pass additional small words in opts[:small_words] + # + # titleize("coffee w cream", small_words: ['w']) # => "Coffee w Cream" + # + # For strings in ALL CAPS, specify acronyms to be preserved in opts[:acronyms] + # + # titleize("SMITH TO HEAD SEC", acronyms: ['SEC']) # => "Smith to Head SEC" + # + def titleize(title, opts={}) title = title.dup title.downcase! unless title[/[[:lower:]]/] # assume all-caps need fixing + small_words = SMALL_WORDS + (opts[:small_words] || []) + small_words = small_words + small_words.map { |small| small.capitalize } + + acronyms = opts[:acronyms] || [] + acronyms = acronyms + acronyms.map { |acronym| acronym.downcase } + phrases(title).map do |phrase| words = phrase.split words.map.with_index do |word, index| @@ -40,12 +55,14 @@ def word.capitalize word when /^[[:digit:]]/ # first character is a number word - when *(SMALL_WORDS + SMALL_WORDS.map {|small| small.capitalize }) + when *small_words if index == 0 || index == words.size - 1 word.capitalize else word.downcase end + when *acronyms + word.upcase else word.capitalize end @@ -87,17 +104,26 @@ class String # # "notes on a scandal" # => "Notes on a Scandal" # "the good german" # => "The Good German" + # + # Pass additional small words in opts[:small_words] + # + # titleize("coffee w cream", small_words: ['w']) # => "Coffee w Cream" + # + # For strings in ALL CAPS, specify acronyms to be preserved in opts[:acronyms] + # + # titleize("SMITH TO HEAD SEC", acronyms: ['SEC']) # => "Smith to Head SEC" + # def titleize(opts={}) if defined? ActiveSupport::Inflector ActiveSupport::Inflector.titleize(self, opts) else - Titleize.titleize(self) + Titleize.titleize(self, opts) end end alias_method :titlecase, :titleize - def titleize! - replace(titleize) + def titleize!(opts={}) + replace(titleize(opts)) end alias_method :titlecase!, :titleize! end @@ -114,19 +140,37 @@ module ActiveSupport::Inflector # This replaces the default Rails titleize. Like the default, it uses # Inflector.underscore and Inflector.humanize to convert # underscored_names and CamelCaseNames to a more human form. However, you can change - # this behavior by passing :humanize => false or :underscore => false as options. + # this behavior by passing :humanize => false or :underscore => false as options. # This can be useful when dealing with words like "iPod" and "GPS". # # titleize is also aliased as titlecase. # # "notes on an active_record" # => "Notes on an Active Record" # "the GoodGerman" # => "The Good German" + # + # Pass additional small words in opts[:small_words] + # + # titleize("coffee w cream", small_words: ['w']) # => "Coffee w Cream" + # + # For strings in ALL CAPS, acronyms specified in + # ActiveSupport::Inflector.inflections.acronyms will be properly + # capitalized. To override the set of acronyms used, pass in + # opts[:acronyms] + # + # titleize("SMITH TO HEAD SEC", acronyms: ['SEC']) # => "Smith to Head SEC" + # def titleize(title, opts={}) opts = {:humanize => true, :underscore => true}.merge(opts) title = ActiveSupport::Inflector.underscore(title) if opts[:underscore] title = ActiveSupport::Inflector.humanize(title) if opts[:humanize] - Titleize.titleize(title) + # prioritize passed-in acronyms, fall back to those configured + # for ActiveSupport::Inflector + opts[:acronyms] ||= ActiveSupport::Inflector.inflections.acronyms + + passthru_opts = opts.select { |k, _| [:acronyms, :small_words].include?(k) } + + Titleize.titleize(title, passthru_opts) end alias_method :titlecase, :titleize end diff --git a/spec/titleize_spec.rb b/spec/titleize_spec.rb index 5d5edc6..60adbbb 100644 --- a/spec/titleize_spec.rb +++ b/spec/titleize_spec.rb @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- - module ActiveSupport module Inflector + require 'ostruct' + #stub def underscore(string) string; end def humanize(string) string; end + def inflections + OpenStruct.new(acronyms: []) + end end end @@ -102,11 +106,29 @@ def humanize(string) string; end end end + it "should not capitalize custom small words specified in opts[:small_words]" do + titleize("first w last", small_words: ['w']).should == "First w Last" + end + it "should not screw up acronyms" do titleize("the SEC's decision").should == "The SEC's Decision" end - it "should not capitalize words with dots" do + context "handling acronyms" do + context "in a mixed-case string" do + it "should not screw up acronyms" do + titleize("the SEC's decision").should == "The SEC's Decision" + end + end + + context "in an uppercase string with the acronyms option specified" do + it 'keeps the acronyms in upper case' do + titleize("SMITH TO HEAD SEC", acronyms: ['SEC']).should == "Smith to Head SEC" + end + end + end + + it "should not capitalize words with dots" do titleize("del.icio.us web site").should == "del.icio.us Web Site" end @@ -115,7 +137,7 @@ def humanize(string) string; end titleize("ends with 'quotation.'").should == "Ends With 'Quotation.'" end - it "should not capitalize words that have a lowercase first letter" do + it "should not capitalize words that have a lowercase first letter" do titleize("iTunes").should == "iTunes" end @@ -263,7 +285,7 @@ def humanize(string) string; end end it "should replace Inflector.titleize" do - Titleize.should_receive(:titleize).with(@title) + Titleize.should_receive(:titleize).with(@title, acronyms: []) ActiveSupport::Inflector.stub!(:underscore).and_return(@title) ActiveSupport::Inflector.stub!(:humanize).and_return(@title) ActiveSupport::Inflector.titleize(@title) @@ -326,6 +348,12 @@ def humanize(string) string; end ActiveSupport::Inflector.should_not_receive(:humanize) "title".titleize(:humanize => false) end + + it "should properly capitalize acronyms configured in Inflector.inflections.acronyms" do + inflections = double(acronyms: ['SEC']) + ActiveSupport::Inflector.stub!(:inflections).and_return(inflections) + "SMITH TO HEAD SEC".titleize.should == 'Smith to Head SEC' + end end context "when ActiveSupport is not loaded" do @@ -338,7 +366,7 @@ def humanize(string) string; end end it "should call Titleize#titleize" do - Titleize.should_receive(:titleize).with("title") + Titleize.should_receive(:titleize).with("title", {}) "title".titleize end end