# PageTitleHelper provides an +ActionView+ helper method to simplify adding
# custom titles to pages.
#
# Author:: Lukas Westermann
# Copyright:: Copyright (c) 2009 Lukas Westermann (Zurich, Switzerland)
# Licence:: MIT-Licence (http://www.opensource.org/licenses/mit-license.php)
#
# See documentation for +page_title+ for usage examples and more informations.
# PageTitleHelper
module PageTitleHelper
module Interpolations
# Represents the environment which is passed into each interpolation call.
class Env < Struct.new(:options, :view, :controller, :title); end
extend self
def self.interpolate(pattern, *args)
instance_methods(false).sort.reverse.inject(pattern.to_s.dup) do |result, tag|
result.gsub(/:#{tag}/) do |match|
send(tag, *args)
end
end
end
def app(env)
env.options[:app] || I18n.translate(:'app.name', :default => File.basename(RAILS_ROOT).humanize)
end
def title(env)
env.title
end
end
# Add new, custom, interpolation.
def self.interpolates(key, &block)
Interpolations.send(:define_method, key, &block)
end
# Default options, which are globally referenced and can
# be changed globally, which might be useful in some cases.
def self.options
@options ||= {
:format => :default,
:default => :'app.tagline',
:suffix => :title
}
end
# Defined alias formats, pretty useful.
def self.formats
@formats ||= {
:app => ":app",
:default => ':title - :app',
:title => ":title"
}
end
def page_title(options = nil, &block)
if block_given? # define title
content_for(:page_title) { yield }
return (title = read_page_title_content_block).is_a?(Array) ? title.first : title
end
options = PageTitleHelper.options.merge(options || {}).symbolize_keys!
options[:format] ||= :title # handles :format => false
options.assert_valid_keys(:app, :suffix, :default, :format)
# construct basic env to pass around
env = Interpolations::Env.new(options, self, self.controller)
# read page title and split into 'real' title and customized format
env.title = read_page_title_content_block || I18n.translate(i18n_template_key(options[:suffix]), :default => options[:default])
env.title, options[:format] = *(env.title << options[:format]) if env.title.is_a?(Array)
# handle format aliases
format = options[:format]
format = PageTitleHelper.formats[format] if PageTitleHelper.formats.include?(format)
# interpolate format
Interpolations.interpolate format, env
end
protected
# Access @content_for_page_title variable, though this is a tad
# hacky, because... what if they (the rails guys) change the behaviour of
# content_for? Well, in Rails 2.3.x it works so far.
#
# But to simplify compatibility with later versions, this method kinda abstracts
# away access to the content within a content_for block.
def read_page_title_content_block
instance_variable_get(:'@content_for_page_title')
end
# Access +ActionView+s internal @_first_render variable, to access
# template first rendered, this is to help create the DRY-I18n-titles magic,
# and also kind of a hack, because this really seems to be some sort if
# internal variable, that's why it's "abstracted" away as well.
#
# Also ensure that the extensions (like .html.erb or
# .html.haml) have been stripped of and translated in the sense
# of converting / to ..
def first_render_path_translated
@_first_render.template_path.gsub(/\.[^\/]*\Z/, '').tr('/', '.')
end
def i18n_template_key(suffix = nil)
first_render_path_translated + (suffix.present? ? ".#{suffix}" : "")
end
end
# tie stuff together
if Object.const_defined?('ActionView')
ActionView::Base.send(:include, PageTitleHelper)
end