Changed _plugins folder to plugins and updated rake tasks accordingly

This commit is contained in:
Brandon Mathis
2011-07-11 11:17:15 -04:00
parent d4139e394e
commit ee7b9dd958
13 changed files with 3 additions and 2 deletions

View File

@ -0,0 +1,73 @@
#
# Author: Brandon Mathis
# Based on the work of: Josediaz Gonzalez - https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/blockquote.rb
#
# Outputs a string with a given attribution as a quote
#
# {% blockquote Bobby Willis http://google.com/blah the search for bobby's mom %}
# Wheeee!
# {% endblockquote %}
# ...
# <blockquote>
# <p>Wheeee!</p>
# <footer>
# <strong>Bobby Willis</strong><cite><a href="http://google.com/blah">The Search For Bobby's Mom</a>
# </blockquote>
#
require './_plugins/titlecase.rb'
module Jekyll
class Blockquote < Liquid::Block
FullCiteWithTitle = /([\w\s]+)(https?:\/\/)(\S+\s)([\w\s]+)/i
FullCite = /([\w\s]+)(https?:\/\/)(\S+)/i
Author = /([\w\s]+)/
def initialize(tag_name, markup, tokens)
@by = nil
@source = nil
@title = nil
if markup =~ FullCiteWithTitle
@by = $1
@source = $2 + $3
@title = $4.titlecase
elsif markup =~ FullCite
@by = $1
@source = $2 + $3
elsif markup =~ Author
@by = $1
end
super
end
def render(context)
output = paragraphize(super.map(&:strip).join)
author = "<strong>#{@by.strip}</strong>"
if @source
url = @source.match(/https?:\/\/(.+)/)[1].split('/')
parts = []
url.each do |part|
if (parts + [part]).join('/').length < 32
parts << part
end
end
source = parts.join('/')
source << '/&hellip;' unless source == @source
end
cite = "<cite><a href='#{@source}'>#{(@title || source)}</a></cite>"
reply = if @by.nil?
output
elsif !@source.nil?
"#{output}<footer>#{author + cite}</footer>"
else
"#{output}<footer>#{author}</footer>"
end
"<blockquote>#{reply}</blockquote>"
end
def paragraphize(input)
"<p>#{input.gsub(/\n\n/, '</p><p>').gsub(/\n/, '<br/>')}</p>"
end
end
end
Liquid::Template.register_tag('blockquote', Jekyll::Blockquote)

View File

@ -0,0 +1,161 @@
# Jekyll category page generator.
# http://recursive-design.com/projects/jekyll-plugins/
#
# Version: 0.1.4 (201101061053)
#
# Copyright (c) 2010 Dave Perrett, http://recursive-design.com/
# Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
#
# A generator that creates category pages for jekyll sites.
#
# To use it, simply drop this script into the _plugins directory of your Jekyll site. You should
# also create a file called 'category_index.html' in the _layouts directory of your jekyll site
# with the following contents (note: you should remove the leading '# ' characters):
#
# ================================== COPY BELOW THIS LINE ==================================
# ---
# layout: default
# ---
#
# <h1 class="category">{{ page.title }}</h1>
# <ul class="posts">
# {% for post in site.categories[page.category] %}
# <div>{{ post.date | date_to_html_string }}</div>
# <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
# <div class="categories">Filed under {{ post.categories | category_links }}</div>
# {% endfor %}
# </ul>
# ================================== COPY ABOVE THIS LINE ==================================
#
# You can alter the _layout_ setting if you wish to use an alternate layout, and obviously you
# can change the HTML above as you see fit.
#
# When you compile your jekyll site, this plugin will loop through the list of categories in your
# site, and use the layout above to generate a page for each one with a list of links to the
# individual posts.
#
# Included filters :
# - category_links: Outputs the list of categories as comma-separated <a> links.
# - date_to_html_string: Outputs the post.date as formatted html, with hooks for CSS styling.
#
# Available _config.yml settings :
# - category_dir: The subfolder to build category pages in (default is 'categories').
# - category_title_prefix: The string used before the category name in the page title (default is
# 'Category: ').
module Jekyll
# The CategoryIndex class creates a single category page for the specified category.
class CategoryIndex < Page
# Initializes a new CategoryIndex.
#
# +base+ is the String path to the <source>.
# +category_dir+ is the String path between <source> and the category folder.
# +category+ is the category currently being processed.
def initialize(site, base, category_dir, category)
@site = site
@base = base
@dir = category_dir
@name = 'index.html'
self.process(@name)
# Read the YAML data from the layout page.
self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
self.data['category'] = category
# Set the title for this page.
title_prefix = site.config['category_title_prefix'] || 'Category: '
self.data['title'] = "#{title_prefix}#{category}"
# Set the meta-description for this page.
meta_description_prefix = site.config['category_meta_description_prefix'] || 'Category: '
self.data['description'] = "#{meta_description_prefix}#{category}"
end
end
# The Site class is a built-in Jekyll class with access to global site config information.
class Site
# Creates an instance of CategoryIndex for each category page, renders it, and
# writes the output to a file.
#
# +category_dir+ is the String path to the category folder.
# +category+ is the category currently being processed.
def write_category_index(category_dir, category)
index = CategoryIndex.new(self, self.source, category_dir, category)
index.render(self.layouts, site_payload)
index.write(self.dest)
# Record the fact that this page has been added, otherwise Site::cleanup will remove it.
self.pages << index
end
# Loops through the list of category pages and processes each one.
def write_category_indexes
if self.layouts.key? 'category_index'
dir = self.config['category_dir'] || 'categories'
self.categories.keys.each do |category|
self.write_category_index(File.join(dir, category.gsub(/_|\W/, '-')), category)
end
# Throw an exception if the layout couldn't be found.
else
throw "No 'category_index' layout found."
end
end
end
# Jekyll hook - the generate method is called by jekyll, and generates all of the category pages.
class GenerateCategories < Generator
safe true
priority :low
def generate(site)
site.write_category_indexes
end
end
# Adds some extra filters used during the category creation process.
module Filters
# Outputs a list of categories as comma-separated <a> links. This is used
# to output the category list for each post on a category page.
#
# +categories+ is the list of categories to format.
#
# Returns string
#
def category_links(categories)
categories = categories.sort!.map do |item|
"<a class='category' href='/#{@context.registers[:site].config['category_dir']}/#{item.gsub(/_|\W/, '-')}'/>#{item}</a>"
end
case categories.length
when 0
""
when 1
categories[0].to_s
else
"#{categories[0...-1].join(', ')}, #{categories[-1]}"
end
end
# Outputs the post.date as formatted html, with hooks for CSS styling.
#
# +date+ is the date object to format as HTML.
#
# Returns string
def date_to_html_string(date)
result = '<span class="month">' + date.strftime('%b').upcase + '</span> '
result += date.strftime('<span class="day">%d</span> ')
result += date.strftime('<span class="year">%Y</span> ')
result
end
end
end

View File

@ -0,0 +1 @@
system "compass compile --css-dir source/stylesheets"

View File

@ -0,0 +1,75 @@
#custom filters for Octopress
module OctopressFilters
# Used on the blog index to split posts on the <!--more--> marker
def exerpt(input)
if input.index(/<!--\s*more\s*-->/i)
input.split(/<!--\s*more\s*-->/i)[0]
else
input
end
end
# Summary is used on the Archive pages to return the first block of content from a post.
def summary(input)
if input.index(/\n\n/)
input.split(/\n\n/)[0]
else
input
end
end
# Replaces relative urls with full urls
def full_urls(input, url='')
input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]+)/ do
$1+url+$3
end
end
# Returns a url without the http:// for use in as a search modifier eg. 'search terms site:website.com'
def search_url(input)
input.gsub /(https?:\/\/)(\S+)/ do
$2
end
end
# replaces primes with smartquotes using RubyPants
def smart_quotes(input)
require 'rubypants'
RubyPants.new(input).to_html
end
# Returns a title cased string based on John Gruber's title case http://daringfireball.net/2008/08/title_case_update
def titlecase(input)
input.titlecase
end
# Returns a datetime if the input is a string
def datetime(date)
if date.class == String
date = Time.parse(date)
end
date
end
# Returns an ordidinal date eg July 22 2007 -> July 22nd 2007
def ordinalize(date)
date = datetime(date)
"#{date.strftime('%b')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}"
end
# Returns an ordinal number. 13 -> 13th, 21 -> 21st etc.
def ordinal(number)
if (11..13).include?(number.to_i % 100)
"#{number}<span>th</span>"
else
case number.to_i % 10
when 1; "#{number}<span>st</span>"
when 2; "#{number}<span>nd</span>"
when 3; "#{number}<span>rd</span>"
else "#{number}<span>th</span>"
end
end
end
end
Liquid::Template.register_filter OctopressFilters

View File

@ -0,0 +1,94 @@
# A Liquid tag for Jekyll sites that allows embedding Gists and showing code for non-JavaScript enabled browsers and readers.
# by: Brandon Tilly
# Source URL: https://gist.github.com/1027674
# Post http://brandontilley.com/2011/01/31/gist-tag-for-jekyll.html
#
# Example usage: {% gist 1027674 gist_tag.rb %} //embeds a gist for this plugin
require 'cgi'
require 'digest/md5'
require 'net/https'
require 'uri'
module Jekyll
class GistTag < Liquid::Tag
def initialize(tag_name, text, token)
super
@text = text
@cache_disabled = false
@cache_folder = File.expand_path "../_gist_cache", File.dirname(__FILE__)
FileUtils.mkdir_p @cache_folder
end
def render(context)
if parts = @text.match(/([\d]*) (.*)/)
gist, file = parts[1].strip, parts[2].strip
script_url = script_url_for gist, file
code = get_cached_gist(gist, file) || get_gist_from_web(gist, file)
html_output_for script_url, code
else
""
end
end
def html_output_for(script_url, code)
code = CGI.escapeHTML code
<<-HTML
<script src='#{script_url}'></script>
<noscript><pre><code>#{code}</code></pre></noscript>
HTML
end
def script_url_for(gist_id, filename)
"https://gist.github.com/#{gist_id}.js?file=#{filename}"
end
def get_gist_url_for(gist, file)
"https://raw.github.com/gist/#{gist}/#{file}"
end
def cache(gist, file, data)
cache_file = get_cache_file_for gist, file
File.open(cache_file, "w") do |io|
io.write data
end
end
def get_cached_gist(gist, file)
return nil if @cache_disabled
cache_file = get_cache_file_for gist, file
File.read cache_file if File.exist? cache_file
end
def get_cache_file_for(gist, file)
bad_chars = /[^a-zA-Z0-9\-_.]/
gist = gist.gsub bad_chars, ''
file = file.gsub bad_chars, ''
md5 = Digest::MD5.hexdigest "#{gist}-#{file}"
File.join @cache_folder, "#{gist}-#{file}-#{md5}.cache"
end
def get_gist_from_web(gist, file)
gist_url = get_gist_url_for gist, file
raw_uri = URI.parse gist_url
https = Net::HTTP.new raw_uri.host, raw_uri.port
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new raw_uri.request_uri
data = https.request request
data = data.body
cache gist, file, data unless @cache_disabled
data
end
end
class GistTagNoCache < GistTag
def initialize(tag_name, text, token)
super
@cache_disabled = true
end
end
end
Liquid::Template.register_tag('gist', Jekyll::GistTag)
Liquid::Template.register_tag('gistnocache', Jekyll::GistTagNoCache)

View File

@ -0,0 +1,24 @@
module Jekyll
require 'haml'
class HamlConverter < Converter
safe true
priority :low
def matches(ext)
ext =~ /haml/i
end
def output_ext(ext)
".html"
end
def convert(content)
begin
engine = Haml::Engine.new(content)
engine.render
rescue StandardError => e
puts "!!! HAML Error: " + e.message
end
end
end
end

View File

@ -0,0 +1,40 @@
require 'pathname'
module Jekyll
class IncludeCodeTag < Liquid::Tag
def initialize(tag_name, file, tokens)
super
@file = file.strip
end
def render(context)
code_dir = (context.registers[:site].config['code_dir'] || 'downloads/code')
code_path = (Pathname.new(context.registers[:site].source) + code_dir).expand_path
file = code_path + @file
if File.symlink?(code_path)
return "Code directory '#{code_path}' cannot be a symlink"
end
unless file.file?
return "File #{file} could not be found"
end
Dir.chdir(code_path) do
code = file.read
file_type = file.extname
url = "#{context.registers[:site].config['url']}/#{code_dir}/#{@file}"
source = "<figure><figcaption><span>#{file.basename}</span><a href='#{url}'>download</a></figcaption>\n"
source += "{% highlight #{file_type} %}\n" + code + "\n{% endhighlight %}</figure>"
partial = Liquid::Template.parse(source)
context.stack do
partial.render(context)
end
end
end
end
end
Liquid::Template.register_tag('include_code', Jekyll::IncludeCodeTag)

View File

@ -0,0 +1,31 @@
require 'pathname'
module Jekyll
class IncludePartialTag < Liquid::Tag
def initialize(tag_name, file, tokens)
super
@file = file.strip
end
def render(context)
file_dir = (context.registers[:site].source || 'source')
file_path = Pathname.new(file_dir).expand_path
file = file_path + @file
unless file.file?
return "File #{file} could not be found"
end
Dir.chdir(file_path) do
partial = Liquid::Template.parse(file.read)
context.stack do
partial.render(context)
end
end
end
end
end
Liquid::Template.register_tag('include_partial', Jekyll::IncludePartialTag)

View File

@ -0,0 +1,42 @@
#
# Author: Brandon Mathis
# Based on the sematic pullquote technique by Maykel Loomans at http://miekd.com/articles/pull-quotes-with-html5-and-css/
#
# Outputs a span with a data-pullquote attribute set from the marked pullquote. Example:
#
# {% pullquote %}
# When writing longform posts, I find it helpful to include pullquotes, which help those scanning a post discern whether or not a post is helpful.
# It is important to note, {" pullquotes are merely visual in presentation and should not appear twice in the text. "} That is why it is prefered
# to use a CSS only technique for styling pullquotes.
# {% endpullquote %}
# ...will output...
# <p>
# <span data-pullquote="pullquotes are merely visual in presentation and should not appear twice in the text.">
# When writing longform posts, I find it helpful to include pullquotes, which help those scanning a post discern whether or not a post is helpful.
# It is important to note, pullquotes are merely visual in presentation and should not appear twice in the text. This is why a CSS only approach # for styling pullquotes is prefered.
# </span>
# </p>
#
module Jekyll
class PullquoteTag < Liquid::Block
PullQuoteMarkup = /\{(.+)\}/i
def initialize(tag_name, markup, tokens)
super
end
def render(context)
output = super
if output.join =~ /\{"\s*(.+)\s*"\}/
@quote = $1
"<span class='has-pullquote' data-pullquote='#{@quote}'>#{output.join.gsub(/\{"\s*|\s*"\}/, '')}</span>"
else
return "Surround your pullquote like this {! text to be quoted !}"
end
end
end
end
Liquid::Template.register_tag('pullquote', Jekyll::PullquoteTag)

View File

@ -0,0 +1,30 @@
#
# Author: Raimonds Simanovskis, http://blog.rayapps.com/
# Source URL: https://github.com/rsim/blog.rayapps.com/blob/master/_plugins/pygments_cache_patch.rb
#
require 'fileutils'
require 'digest/md5'
PYGMENTS_CACHE_DIR = File.expand_path('../../_code_cache', __FILE__)
FileUtils.mkdir_p(PYGMENTS_CACHE_DIR)
Jekyll::HighlightBlock.class_eval do
def render_pygments(context, code)
if defined?(PYGMENTS_CACHE_DIR)
path = File.join(PYGMENTS_CACHE_DIR, "#{@lang}-#{Digest::MD5.hexdigest(code)}.html")
if File.exist?(path)
highlighted_code = File.read(path)
else
highlighted_code = Albino.new(code, @lang).to_s(@options)
File.open(path, 'w') {|f| f.print(highlighted_code) }
end
else
highlighted_code = Albino.new(code, @lang).to_s(@options)
end
output = add_code_tags(highlighted_code, @lang)
output = context["pygments_prefix"] + output if context["pygments_prefix"]
output = output + context["pygments_suffix"] if context["pygments_suffix"]
output
end
end

View File

@ -0,0 +1,308 @@
# Sitemap.xml Generator is a Jekyll plugin that generates a sitemap.xml file by
# traversing all of the available posts and pages.
#
# How To Use:
# 1) Copy source file into your _plugins folder within your Jekyll project.
# 2) Change modify the url variable in _config.yml to reflect your domain name.
# 3) Run Jekyll: jekyll --server to re-generate your site.
#
# Variables:
# * Change SITEMAP_FILE_NAME if you want your sitemap to be called something
# other than sitemap.xml.
# * Change the PAGES_INCLUDE_POSTS list to include any pages that are looping
# through your posts (e.g. "index.html", "archive.html", etc.). This will
# ensure that right after you make a new post, the last modified date will
# be updated to reflect the new post.
# * A sitemap.xml should be included in your _site folder.
# * If there are any files you don't want included in the sitemap, add them
# to the EXCLUDED_FILES list. The name should match the name of the source
# file.
# * If you want to include the optional changefreq and priority attributes,
# simply include custom variables in the YAML Front Matter of that file.
# The names of these custom variables are defined below in the
# CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME and PRIORITY_CUSTOM_VARIABLE_NAME
# constants.
#
# Notes:
# * The last modified date is determined by the latest from the following:
# system modified date of the page or post, system modified date of
# included layout, system modified date of included layout within that
# layout, ...
#
# Author: Michael Levin
# Site: http://www.kinnetica.com
# Distributed Under A Creative Commons License
# - http://creativecommons.org/licenses/by/3.0/
#
# Modified for Octopress by John W. Long
#
require 'rexml/document'
module Jekyll
# Change SITEMAP_FILE_NAME if you would like your sitemap file
# to be called something else
SITEMAP_FILE_NAME = "sitemap.xml"
# Any files to exclude from being included in the sitemap.xml
EXCLUDED_FILES = ["atom.xml"]
# Any files that include posts, so that when a new post is added, the last
# modified date of these pages should take that into account
PAGES_INCLUDE_POSTS = ["index.html"]
# Custom variable names for changefreq and priority elements
# These names are used within the YAML Front Matter of pages or posts
# for which you want to include these properties
CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME = "change_frequency"
PRIORITY_CUSTOM_VARIABLE_NAME = "priority"
class Post
attr_accessor :name
def full_path_to_source
File.join(@base, @name)
end
def location_on_server
"#{site.config['url']}#{url}"
end
end
class Page
attr_accessor :name
def full_path_to_source
File.join(@base, @dir, @name)
end
def location_on_server
location = "#{site.config['url']}#{@dir}#{url}"
location.gsub(/index.html$/, "")
end
end
class Layout
def full_path_to_source
File.join(@base, @name)
end
end
# Recover from strange exception when starting server without --auto
class SitemapFile < StaticFile
def write(dest)
begin
super(dest)
rescue
end
true
end
end
class SitemapGenerator < Generator
# Valid values allowed by sitemap.xml spec for change frequencies
VALID_CHANGE_FREQUENCY_VALUES = ["always", "hourly", "daily", "weekly",
"monthly", "yearly", "never"]
# Goes through pages and posts and generates sitemap.xml file
#
# Returns nothing
def generate(site)
sitemap = REXML::Document.new << REXML::XMLDecl.new("1.0", "UTF-8")
urlset = REXML::Element.new "urlset"
urlset.add_attribute("xmlns",
"http://www.sitemaps.org/schemas/sitemap/0.9")
@last_modified_post_date = fill_posts(site, urlset)
fill_pages(site, urlset)
sitemap.add_element(urlset)
# File I/O: create sitemap.xml file and write out pretty-printed XML
file = File.new(File.join(site.dest, SITEMAP_FILE_NAME), "w")
formatter = REXML::Formatters::Pretty.new(4)
formatter.compact = true
formatter.write(sitemap, file)
file.close
# Keep the sitemap.xml file from being cleaned by Jekyll
site.static_files << Jekyll::SitemapFile.new(site, site.dest, "/", SITEMAP_FILE_NAME)
end
# Create url elements for all the posts and find the date of the latest one
#
# Returns last_modified_date of latest post
def fill_posts(site, urlset)
last_modified_date = nil
site.posts.each do |post|
if !excluded?(post.name)
url = fill_url(site, post)
urlset.add_element(url)
end
path = post.full_path_to_source
date = File.mtime(path)
last_modified_date = date if last_modified_date == nil or date > last_modified_date
end
last_modified_date
end
# Create url elements for all the normal pages and find the date of the
# index to use with the pagination pages
#
# Returns last_modified_date of index page
def fill_pages(site, urlset)
site.pages.each do |page|
if !excluded?(page.name)
path = page.full_path_to_source
if File.exists?(path)
url = fill_url(site, page)
urlset.add_element(url)
end
end
end
end
# Fill data of each URL element: location, last modified,
# change frequency (optional), and priority.
#
# Returns url REXML::Element
def fill_url(site, page_or_post)
url = REXML::Element.new "url"
loc = fill_location(page_or_post)
url.add_element(loc)
lastmod = fill_last_modified(site, page_or_post)
url.add_element(lastmod) if lastmod
if (page_or_post.data[CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME])
change_frequency =
page_or_post.data[CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME].downcase
if (valid_change_frequency?(change_frequency))
changefreq = REXML::Element.new "changefreq"
changefreq.text = change_frequency
url.add_element(changefreq)
else
puts "ERROR: Invalid Change Frequency In #{page_or_post.name}"
end
end
if (page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME])
priority_value = page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME]
if valid_priority?(priority_value)
priority = REXML::Element.new "priority"
priority.text = page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME]
url.add_element(priority)
else
puts "ERROR: Invalid Priority In #{page_or_post.name}"
end
end
url
end
# Get URL location of page or post
#
# Returns the location of the page or post
def fill_location(page_or_post)
loc = REXML::Element.new "loc"
loc.text = page_or_post.location_on_server
loc
end
# Fill lastmod XML element with the last modified date for the page or post.
#
# Returns lastmod REXML::Element or nil
def fill_last_modified(site, page_or_post)
path = page_or_post.full_path_to_source
lastmod = REXML::Element.new "lastmod"
date = File.mtime(path)
latest_date = find_latest_date(date, site, page_or_post)
if @last_modified_post_date == nil
# This is a post
lastmod.text = latest_date.iso8601
else
# This is a page
if posts_included?(page_or_post.name)
# We want to take into account the last post date
final_date = greater_date(latest_date, @last_modified_post_date)
lastmod.text = final_date.iso8601
else
lastmod.text = latest_date.iso8601
end
end
lastmod
end
# Go through the page/post and any implemented layouts and get the latest
# modified date
#
# Returns formatted output of latest date of page/post and any used layouts
def find_latest_date(latest_date, site, page_or_post)
layouts = site.layouts
layout = layouts[page_or_post.data["layout"]]
while layout
path = layout.full_path_to_source
date = File.mtime(path)
latest_date = date if (date > latest_date)
layout = layouts[layout.data["layout"]]
end
latest_date
end
# Which of the two dates is later
#
# Returns latest of two dates
def greater_date(date1, date2)
if (date1 >= date2)
date1
else
date2
end
end
# Is the page or post listed as something we want to exclude?
#
# Returns boolean
def excluded?(name)
EXCLUDED_FILES.include? name
end
def posts_included?(name)
PAGES_INCLUDE_POSTS.include? name
end
# Is the change frequency value provided valid according to the spec
#
# Returns boolean
def valid_change_frequency?(change_frequency)
VALID_CHANGE_FREQUENCY_VALUES.include? change_frequency
end
# Is the priority value provided valid according to the spec
#
# Returns boolean
def valid_priority?(priority)
begin
priority_val = Float(priority)
return true if priority_val >= 0.0 and priority_val <= 1.0
rescue ArgumentError
end
false
end
end
end

View File

@ -0,0 +1,36 @@
class String
def titlecase
small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs.)
x = split(" ").map do |word|
# note: word could contain non-word characters!
# downcase all small_words, capitalize the rest
small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize!
word
end
# capitalize first and last words
x.first.to_s.smart_capitalize!
x.last.to_s.smart_capitalize!
# small words after colons are capitalized
x.join(" ").gsub(/:\s?(\W*#{small_words.join("|")}\W*)\s/) { ": #{$1.smart_capitalize} " }
end
def titlecase!
replace(titlecase)
end
def smart_capitalize
# ignore any leading crazy characters and capitalize the first real character
if self =~ /^['"\(\[']*([a-z])/
i = index($1)
x = self[i,self.length]
# word with capitals and periods mid-word are left alone
self[i,1] = self[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/
end
self
end
def smart_capitalize!
replace(smart_capitalize)
end
end